シンボルとパッケージ
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) NILgetの返り値に対して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 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指定しているパッケージのシンボルを指定する場合は,パッケージを指定しなくてよい.