シンボルとパッケージ

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

This article was written by Fujiko