Next: , Previous: , Up: シンボル   [Contents][Index]


9.3 シンボルの作成とintern

GNU Emacs Lispでシンボルが作成される方法を理解するには、Lispがシンボルを読み取る方法を理解しなければなりません。Lispは同じコンテキストで同じ文字シーケンスを読み取ったら、毎回同じシンボルを見つけることを保証しなければなりません。これに失敗すると、完全な混乱を招くでしょう。

ソースコード内でシンボルを参照する名前に出会うと、Lispリーダーはその名前の文字すべてを読み取ります。それからプログラマーが意図したシンボルを見つけるために、obarray(オブジェクト配列)と呼ばれるテーブルでその名前を照合します。この照合に使用されるテクニックは“ハッシュ化(hashing)”と呼ばれています。これは照合を行う際に文字シーケンスを“ハッシュコード(hash code)”として知られる数値に変換する効率的な手法です。たとえばJan Jonesを見つけるときは、電話帳を表紙から1頁ずつ探すのではなくJの頁から探し始めます。これはハッシュ化の簡単なバージョンです。obarrayの各要素は与えられたハッシュコードとともに、すべてのシンボルを保持するバケット(bucket)です。与えられた名前を探すためには、バケットの中からハッシュコードがその名前であるような、すべてのシンボルを探すのが効果的です(同じアイデアは一般的なEmacsのハッシュテーブルでも使用されていがこれらはデータ型が異なる。ハッシュテーブルを参照されたい)。

Lispリーダーは名前の照合時には“ショートハンド(shorthands 速記、簡略表記)”も考慮します。プログラマーがショートハンドを提供した場合には、たとえソースコード内でシンボル名が完全な形式で与えられなくても、リーダーはシンボルを見つけることができます。もちろんリーダーはそのようなショートハンドに関して事前に既定されたコンテキストを認識する必要があり、同様に“Jan”という名前だけでJan Jonesを一意に参照できるコンテキストが必要です。これはJonesの中にいるときや、Janが最近言及されていれば問題はないでしょうが、他の状況下では非常に曖昧です。ショートハンドを参照してください。

探している名前のシンボルが見つかったら、リーダーはそのシンボルを使用します。obarrayにその名前のシンボルが含まれなければ、リーダーは新しいシンボルを作成してそれをobarrayに追加します。特定の名前のシンボルを探して追加することをインターン(intern)と言い、これが行なわれた後はそのシンボルはインターンされたシンボル(interned symbol)と呼ばれます。

インターンすることによりある特定の名前のシンボルは、各obarrayに1つだけであることが保証されます。同じ名前のシンボルが他に存在するかもしれませんが、同じobarrayには存在しません。したがってリーダーは、(同じobarrayを読みつづける限り)同じ名前にたいして同じシンボルを取得します。

インターンは通常はリーダー内で自動的に発生しますが、他のプログラムがこれを行ないたい場合もあるかもしれません。たとえばM-xコマンドはその後にミニバッファーを使用してコマンド名を文字列として取得して、その文字列をインターンしてからインターンされたその名前のシンボルを得ます。別の例として、照合する人名それぞれをシンボル名としてインターンする架空の電話帳プログラムは、たとえそれがobarrayに含まれていなくても、誰かが最後にそれを照合した際に情報をアタッチできるようにする場合などです。

すべてのシンボルを含むobarrayはありません。実際にどのobarrayにも含まれないシンボルがいくつかあります。これらはインターンされていないシンボル(uninterned symbols)と呼ばれます。インターンされていないシンボルも、他のシンボルと同じく4つのセルをもちます。しかしインターンされていないシンボルへのアクセスを得る唯一の方法は、他の何らかのオブジェクトとして探すか、変数の値として探す方法だけです。インターンされていないシンボルはLispコード生成時に有用な場合があります。以下を参照してください。

Emacs Lispではobarrayはベクターです。ベクター内の各要素がバケットになります。要素の値は、名前がそのバケットにハッシュされるようなインターンされたシンボル、またはバケットが空のときは0です。インターンされたシンボルは、そのバケット内の次のシンボルへの内部リンク(ユーザーからは見えない)をもちます。これらのリンクは不可視なので、mapatoms (以下参照)を使用する方法をのぞき、obarray内のすべてのシンボルを探す方法はありません。バケット内のシンボルの順番に意味はありません。

空のobarrayではすべての要素が0なので、(make-vector length 0)でobarrayを作成することができます。obarrayを作成する有効な方法はこれだけです。長さに素数を指定するとよいハッシュ化がされる傾向があります。2の累乗から1減じた長さもよい結果を生む傾向があります。

自分でobarrayにシンボルを置かないでください。これはうまくいきません — obarrayに正しくシンボルを入力できるのはinternだけです。

Common Lispに関する注意: Common Lispとは異なりEmacs Lispでは複数の異なる“パッケージ”における同一の名前のインターンは提供されていないので、異なるパッケージごとに同じ名前のシンボルが複数作成される。Emacs Lispは“ショートハンド”と呼ばれる別の名前空間システムを提供する(ショートハンドを参照)。

以下の関数のほとんどは、引数に名前とobarrayをとります。名前が文字列以外、またはobarrayがベクター以外ならwrong-type-argumentエラーがシグナルされます。

Function: symbol-name symbol

この関数はsymbolの名前を文字列としてリターンする。たとえば:

(symbol-name 'foo)
     ⇒ "foo"

警告: この関数がリターンした文字列は絶対変更してはならない。これを行うことによってEmacsの機能が損なわれるかもしれず、Emacsのクラッシュすら招きかねない。

インターンされていないシンボルの作成は、Lispコードを生成するとき有用です。なぜなら作成されたコード内で変数として使用されているインターンされていないシンボルは、他のLispプログラムで使用されている任意の変数と競合することはありえないからです。

Function: make-symbol name

この関数は新たに割り当てられた、名前がname(文字列でなければならない)であるような、インターンされていないシンボルをリターンする。このシンボルの値と関数はvoidで、プロパティリストはnil。以下の例ではsymの値はfooeqではない。なぜならこれは名前が‘foo’という、インターンされていないシンボルだからである。

(setq sym (make-symbol "foo"))
     ⇒ foo
(eq sym 'foo)
     ⇒ nil
Function: gensym &optional prefix

この関数はmake-symbolを使用してprefixgensym-counterを付加した名前のシンボルをリターンする。更にこの関数を複数回呼び出しても同一名のシンボルが生成されないことを保証するためにカウンターを増加する。プレフィックスのデフォルトは"g"

意図せず生成したコードのプリント表現をインターンした際の問題を避けるために、make-symbolではなくgensymの使用をお勧めします。(プリント表現と読み取り構文を参照)。

Function: intern name &optional obarray

この関数は名前がnameであるような、インターンされたシンボルをリターンする。オブジェクト配列obarrayの中にそのようなシンボルが存在しなければ、internは新たにシンボルを作成してobarrayに追加してそれをリターンする。obarrayが省略されると、グローバル変数obarrayの値が使用される。

(setq sym (intern "foo"))
     ⇒ foo
(eq sym 'foo)
     ⇒ t

(setq sym1 (intern "foo" other-obarray))
     ⇒ foo
(eq sym1 'foo)
     ⇒ nil

Common Lispに関する注意: Common Lispでは既存のシンボルをobarrayにインターンできる。Emacs Lispではinternの引数はシンボルではなく文字列なのでこれを行なうことはできない。

Function: intern-soft name &optional obarray

この関数はobarray内の名前がnameのシンボル、obarrayにその名前のシンボルが存在しなければnilをリターンする。したがって与えられた名前のシンボルがすでにインターンされているかテストするために、intern-softを使用することができる。obarrayが省略されるとグローバル変数obarrayの値が使用される。

引数nameにはシンボルも使用できる。この場合、指定されたobarrayにnameがインターンされていればname、それ以外ならnilをリターンする。

(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が使用する標準のobarrayである。

Function: mapatoms function &optional obarray

この関数はオブジェクト配列obarrayの中の各シンボルにたいして、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を使用する他の例については、ドキュメント文字列へのアクセスdocumentationを参照のこと。

Function: unintern symbol obarray

この関数はオブジェクト配列obarrayからsymbolを削除する。obarrayの中にsymbolが存在しなければ、uninternは何も行なわない。obarraynilなら現在のobarrayが使用される。

symbolにシンボルではなく文字列を与えると、それはシンボルの名前を意味する。この場合、uninternは(もしあれば)obarrayからその名前のシンボルを削除する。そのようなシンボルが存在するならuninternは何も行なわない。

uninternがシンボルを削除したらt、それ以外はnilをリターンする。


Next: シンボルのプロパティ, Previous: シンボルの定義, Up: シンボル   [Contents][Index]