次: Property Lists, 前: Definitions, 上: Symbols
GNU Emacs Lispにおいて、どのようにシンボルを作成するかを理解するには、 Lispがそれらをどのように読むかを知る必要があります。 Lispは、同じ文字群を読み取るたびに、同じシンボルをみつけることを保証する 必要があります。 これに失敗すると完全に混乱します。
Lispリーダがシンボルに出会うと、名前の文字群をすべて読み取ります。 そして、これらの文字群を『ハッシュ化』して、 オブジェクト配列(obarray)と呼ばれる表の添字を探します。 ハッシュ化は効率的に探索する手法です。 たとえば、Jan Jonesを電話番号簿の表紙から1ページずつ順に探すかわりに、 Jのページから探し始めます。 これは単純なハッシュ化です。 オブジェクト配列の各要素は、 あるハッシュコードを有するすべてのシンボルを格納した バケット(bucket)です。 ある名前を探すには、その名前のハッシュコードに対応するバケット内の すべてのシンボルを調べるだけで十分です。
目的の名前のシンボルがみつかれば、リーダはそのシンボルを使います。 オブジェクト配列に目的の名前のシンボルがなければ、 リーダは新たなシンボルを作成し、それをオブジェクト配列に追加します。 ある名前のシンボルを探したり追加することをシンボルを インターン(interning)するといい、 そのシンボルをインターンしたシンボル(interned symbol)と呼びます。
インターンすることで、各オブジェクト配列には 特定の名前のシンボルが1個だけあることを保証します。 他の似たような名前のシンボルが存在しても、 同じオブジェクト配列には入っていません。 したがって、同じオブジェクト配列を使って読む限り、 リーダは同じ名前に対して同じシンボルを得ることができます。
すべてのシンボルがオブジェクト配列に入っているとは限りません。 実際、どのオブジェクト配列にも属さないシンボルがいくつかあります。 これらをインターンしてないシンボル(uninterned symbols)と呼びます。 インターンしてないシンボルにも、他のシンボルと同様に4つのセルがあります。 しかし、それを参照する手段は、他のオブジェクトを介して探すか、 変数の値として探すしかありません。
Emacs Lispでは、オブジェクト配列は実際にはベクトルです。
ベクトルの各要素はバケットです。
その値は、そのバケットにハッシュ化される名前のインターンしたシンボルであるか、
そのバケットが空ならば0です。
インターンした各シンボルには、バケットのつぎのシンボルを指す
(ユーザーには見えない)内部的なリンクがあります。
このリンクは見えないので、mapatoms
(下記)を使う以外には、
オブジェクト配列内のすべてのシンボルを探す方法はありません。
バケット内でのシンボルの順序は関係ありません。
空のオブジェクト配列では、各要素は0です。
(make-vector
length 0)
でオブジェクト配列を作成できます。
これは、オブジェクト配列を作成する唯一の正当な方法です。
長さとして素数を用いると、ハッシュ化の結果がよい傾向があります。
2の巾より1小さい長さもよい結果になります。
読者自身でオブジェクト配列にシンボルを入れないでください。
うまくいきません。
オブジェクト配列にシンボルを正しく入れられるのはintern
だけです。
Common Lispに関した注意: Common Lispでは、1つのシンボルを複数のオブジェクト配列に入れることができる。
下記の関数のほとんどは、引数に名前を取り、
場合によってはオブジェクト配列を引数に取ります。
名前が文字列でなかったり、オブジェクト配列がベクトルでないと、
エラーwrong-type-argument
を通知します。
この関数は、symbolの名前を表す文字列を返す。 たとえば、つぎのとおり。
(symbol-name 'foo) => "foo"警告: 文字列の文字を置き換えるとシンボルの名前を変更するが、 オブジェクト配列は更新できないので変更しないこと!
この関数は、name(文字列であること)を名前とする 新たに割り付けたインターンしていないシンボルを返す。 その値と関数定義は空であり、属性リストは
nil
である。 以下の例では、sym
の値はfoo
とeq
ではない。 なぜなら、名前は`foo'ではあるが、 インターンしていない別のシンボルであるため。(setq sym (make-symbol "foo")) => foo (eq sym 'foo) => nil
この関数は、nameを名前とするインターンしたシンボルを返す。 そのようなシンボルがオブジェクト配列obarrayに存在しなければ、
intern
は新たなものを作成し、それをオブジェクト配列に追加してから、 それを返す。 obarrayを省略すると、グローバル変数obarray
の値を使う。(setq sym (intern "foo")) => foo (eq sym 'foo) => t (setq sym1 (intern "foo" other-obarray)) => foo (eq sym 'foo) => nil
Common Lispに関した注意:
Common Lispでは、既存のシンボルをオブジェクト配列にインターンできる。
Emacs Lispでは、これはできない。
なぜなら、intern
の引数は文字列である必要があり、
シンボルではない。
この関数は、obarray内のnameを名前とするシンボルを返す。 ただし、その名前のシンボルがobarrayになければ
nil
を返す。 したがって、intern-soft
を用いて、指定した名前のシンボルが インターンされているかどうか調べられる。 obarrayを省略すると、グローバル変数obarray
の値を使う。(intern-soft "frazzle") ; そのようなシンボルは存在しない => nil (make-symbol "frazzle") ; インターンしないものを作る => frazzle (intern-soft "frazzle") ; そのようなものはみつからない => nil (setq sym (intern "frazzle")) ; インターンしたものを作る => frazzle (intern-soft "frazzle") ; そのようなものがみつかった! => frazzle (eq sym 'frazzle) ; しかも、それらは同一 => t
この関数は、オブジェクト配列obarrayの各シンボルについて、 1回ずつfunctionを呼び出す。 そして、
nil
を返す。 obarrayを省略すると、通常のシンボル向けの標準のオブジェクト配列であるobarray
の値をデフォルトにする。(setq count 0) => 0 (defun count-syms (s) (setq count (1+ count))) => count-syms (mapatoms 'count-syms) => nil count => 1871
mapatoms
を使った別の例については、 Accessing Documentationのdocumentation
を参照。
この関数は、オブジェクト配列obarrayからsymbolを削除する。
symbol
が実際にはオブジェクト配列内になければ、unintern
はなにもしない。 obarrayがnil
であると、現在のオブジェクト配列を使う。symbolのシンボルのかわりに文字列を指定すると、 それはシンボルの名前を表す。 そして、
unintern
はその名前のシンボルを(あれば)オブジェクト配列から 削除する。 そのようなシンボルがなければ、unintern
はなにもしない。
unintern
は、シンボルを削除したときにはt
を返す。 さもなければnil
を返す。