Next: , Previous: Definitions, Up: Symbols


7.3 シンボルの作成とインターン

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を通知します。

— Function: symbol-name symbol

この関数は、symbolの名前を表す文字列を返す。 たとえば、つぎのとおり。

          (symbol-name 'foo)
               ⇒ "foo"

警告: 文字列の文字を置き換えるとシンボルの名前を変更するが、 オブジェクト配列は更新できないので変更しないこと!

— Function: make-symbol name

この関数は、name(文字列であること)を名前とする 新たに割り付けたインターンしていないシンボルを返す。 その値と関数定義は空であり、属性リストはnilである。 以下の例では、symの値はfooeqではない。 なぜなら、名前は‘foo’ではあるが、 インターンしていない別のシンボルであるため。

          (setq sym (make-symbol "foo"))
               ⇒ foo
          (eq sym 'foo)
               ⇒ nil
— Function: intern name &optional obarray

この関数は、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の引数は文字列である必要があり、 シンボルではない。
— Function: intern-soft name &optional obarray

この関数は、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
— Variable: obarray

この変数は、internreadが使う標準のオブジェクト配列。

— Function: mapatoms function &optional obarray

この関数は、オブジェクト配列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 Documentationdocumentationを参照。

— Function: unintern symbol &optional obarray

この関数は、オブジェクト配列obarrayからsymbolを削除する。 symbolが実際にはオブジェクト配列内になければ、 uninternはなにもしない。 obarraynilであると、現在のオブジェクト配列を使う。

symbolのシンボルのかわりに文字列を指定すると、 それはシンボルの名前を表す。 そして、uninternはその名前のシンボルを(あれば)オブジェクト配列から 削除する。 そのようなシンボルがなければ、uninternはなにもしない。

uninternは、シンボルを削除したときにはtを返す。 さもなければnilを返す。