ファイヤープロジェクト
シンボルとパッケージ
2004-04-27T01:20+09:00   matsu
Common Lispのシンボルには,いろいろな情報が格納されている.また,Common Lispにはパッケージというものがあって,これによってシンボルの名前空間を拡張できる.
シンボルは以下から構成される.
  • シンボル名
  • パッケージ
  • 変数値
  • 関数
  • 属性リスト
シンボルは簡単に作成できて,見ためも単なる文字列のように見えるが,内部では構造を持っていて,いろいろな情報を持っている.
前節のとおり,シンボルは単なる名前ではなく実体を伴うオブジェクトである.人間はシンボル名を通して,シンボルを単なる名前として認識できる.シンボルのシンボル名はsymbol-nameによって参照できる.
> (symbol-name 'SYMBOL)
"SYMBOL"
> (symbol-name 'Symbol)
"SYMBOL"
> (symbol-name 'symbol)
"SYMBOL"
特に指定しない限り,シンボルに大文字小文字の区別はない.空白や(や)などをシンボル名に持つシンボルは|で囲んで表現する.シンボル名に|(パイプ)そのものを含める場合には,|の前に\を置いてエスケープする.
> (symbol-name '|hoge fuga foo|)
"hoge fuga foo"
> (setf |()| 10)
10
> |()|
10
> (symbol-name '|\||)
"|"
シンボルの要素には属性リストがある.getという関数をキーとともに呼び出すことによって,キーに対応する値を取り出すことができる.
> (get 'symbolhoge 'fugakey)
NIL
getの返り値に対してsetfすれば属性を設定できる.
> (setf (get 'symbolhoge 'fugakey) 'fugavalue)
FUGAVALUE
> (get 'symbolhoge 'fugakey)
FUGAVALUE
属性リスト全体を取得するには,関数symbol-plistを使用する.
> (setf (get 'symbolhoge 'fookey) 'foovalue)
FOOVALUE
> (symbol-plist 'symbolhoge)
(FOOKEY FOOVALUE FUGAKEY FUGAVALUE)
シンボルはパッケージに属している.デフォルトのパッケージはcommon-lisp-userである.パッケージとシンボルには以下のような関係がある.
  • パッケージとはシンボルのテーブルである.
  • シンボルはいずれかのパッケージに属する.これを「シンボルはそのパッケージにインターンされている」という.
  • プログラムはいずれかのパッケージに属し,「その時のパッケージ」をカレントパッケージという.
  • パッケージ宣言時にexport指定したシンボルは,パッケージ外から参照できる.
  • シンボルはシンボル名とパッケージ名を指定することで一意となる.
  • パッケージ名を指定しない場合,カレントパッケージを指定したことになる.
関数internは,指定した文字列をシンボル名にもつシンボルを,指定した文字列をパッケージ名にもつパッケージにインターンする.
intern string &optional package
関数internはインターンしたシンボルがあればそのシンボルを,なければシンボルを作成して返す.さらにシンボルが既存か否かという値も返す(多値関数).パッケージを省略した場合は,カレントパッケージを指定したものとみなされる.
> (intern "FOO")
FOO ;
NIL
> 'BAR
BAR
> (intern "BAR")
BAR ;
:INTERNAL
なお,関数internは第一引数に文字列を要求し,大文字小文字を区別することに注意する.
> (intern "HOGE")
HOGE ;
NIL
> (intern "hoge")
|hoge| ;
NIL
ここまでの話から想像できるとおり,パッケージは定義でき,カレントパッケージは変更できる.パッケージの定義は関数defpackageで行う.
defpackage defined-package-name [[option]]
option::= (:nicknames nickname*)* |  
          (:documentation string) |  
          (:use package-name*)* |  
          (:shadow {symbol-name}*)* |  
          (:shadowing-import-from package-name {symbol-name}*)* |  
          (:import-from package-name {symbol-name}*)* |  
          (:export {symbol-name}*)* |  
          (:intern {symbol-name}*)* |  
          (:size integer) 
オプションが若干複雑になっている.主なオプションの説明を以下に示す.
use
useで指定したパッケージのシンボルは,定義しているパッケージ内でパッケージ指定なしで指定できる.
nicknames
定義するパッケージのニックネーム.パッケージ名の変わりにニックネームを使用してパッケージを指定できる.
export
定義するパッケージにインターンされていて,パッケージ外から参照できるシンボルを指定する(パッケージ外に公開するシンボルを指定する).
カレントパッケージは関数in-packageで指定する.
in-package name
パッケージ定義とカレントパッケージ指定のサンプルを作成してみた.パッケージの定義とカレントパッケージの指定はそれぞれ明示的に指定する必要がある点に注意.
> (defpackage "HOGE-PACKAGE"
       (:use "COMMON-LISP")
       (:nicknames "hoge")
       (:export "HOGE-A"))

#<PACKAGE HOGE-PACKAGE>
> (in-package hoge-package)
#<PACKAGE HOGE-PACKAGE>
> (setf hoge-a 1)
1
> (setf hoge-b 2)
2

> (defpackage "FUGA-PACKAGE"
       (:use "COMMON-LISP")
       (:nicknames "fuga")
       (:export "FUGA-A" "HOGE-A"))
#<PACKAGE FUGA-PACKAGE>
> (in-package fuga-package)
#<PACKAGE FUGA-PACKAGE>
> (setf fuga-a 3)
3
> (setf fuga-b 4)
4
> (setf hoge-a 5)
5

> (in-package common-lisp-user)
#<PACKAGE COMMON-LISP-USER>
> (format t "hoge-package:hoge-a = ~A~%" hoge-package:hoge-a)
hoge-package:hoge-a = 1
NIL
> (format t "hoge-package:hoge-b = ~A~%" hoge-package:hoge-b)
*** - READ from #<INPUT CONCATENATED-STREAM 
#<INPUT STRING-INPUT-STREAM>
#<IO TERMINAL-STREAM>>:
#<PACKAGE HOGE-PACKAGE>
has no external symbol with name "HOGE-B"
> (format t "fuga-package:fuga-a = ~A~%" fuga-package:fuga-a)
fuga-package:fuga-a = 3
NIL
> (format t "fuga-package:fuga-b = ~A~%" fuga-package:fuga-b)
*** - READ from #<INPUT CONCATENATED-STREAM
#<INPUT STRING-INPUT-STREAM>
#<IO TERMINAL-STREAM>>:
#<PACKAGE FUGA-PACKAGE>
has no external symbol with name "FUGA-B"
> (format t "fuga-package:hoge-a = ~A~%" fuga-package:hoge-a)
fuga-package:hoge-a = 5
NIL
上記のようにパッケージ外からそのパッケージのシンボルを参照するには,
パッケージ名:シンボル名
として指定する(※).上のようにexportされていないシンボルをパッケージ外から参照しようとすると,エラーになる.
※ カレントパッケージでuse指定しているパッケージのシンボルを指定する場合は,パッケージを指定しなくてよい.
matsu(C)
Since 2002
Mail to matsu