[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7. シンボル

シンボル(symbol)とは固有の名前をもつオブジェクトのことです。この 章では、シンボルとそれを構成する要素、その属性リストについてお話しし、 さらにそれらがどのようにして生成され、internされるかについて説明します。 シンボルを変数名や関数名として利用することについては、章を改めて記述し ますので、それらについては変数関数を参照してく ださい。シンボルの正確なリード構文については、シンボル型を御覧く ださい。

任意のLispオブジェクトがシンボルであるかどうかは、symbolpで調べ ることができます:

Function: symbolp object

この関数は、 objectがシンボルならばtを返し、そうでなければ nilを返します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.1 シンボルの要素

各シンボルは四つの要素("セル"ともいう)を持っており、それぞれはさら に別のオブジェクトを参照します:

印字名

印字名セル(print name cell)は、読込みや印字の際にシンボルの名前を 表す文字列を格納しています。 シンボルの作成と internsymbol-nameを参照してください。

値セル(value cell)は、変数としてのシンボルの現在の値を格納します。 シンボルが式として使われると、その式の値はシンボルの値セルの内容になり ます。 変数の値へのアクセスsymbol-valueを参照してください。

関数

関数セル(function cell)は、シンボルの関数定義を保持します。あるシ ンボルが関数として使われると、その関数定義がそこで使われます。このセル は、エディタのコマンド実行の際にシンボルにキーマップやキーボード・マク ロを意味させるためにも用いられます。各シンボルは値と関数のセルを別に持 ちますから、変数と関数の名前がぶつかることはありません。関数セルの内容へのアクセスsymbol-functionを参照してください。

属性リスト

属性リスト・セル(property list cell)は、シンボルの属性リストを格納 しています。属性リストsymbol-plistを参照してください。

印字名セルは、常に文字列を格納し、これを変更することはできません。ほか の三つのセルには、与えられた任意のLispオブジェクトを個別にセットするこ とができます。

印字名セルは、そのシンボルの名前である文字列を保持しています。シンボル は、その名前によってテキスト的に表現されるため、同じ名前で二つのシンボ ルが存在しないことは重要です。これはLispリーダによって保証されます。と いうのは、シンボルを読み込むときには新しいシンボルを作成する前に、まず 指定された名前を持つ既存のシンボルがないかどうか検索するからです(GNU Emacs Lispでは、この検索にはハッシュ・アルゴリズムとobarrayを用います。 シンボルの作成と internを参照してください)。

通常の使い方では、関数セルは、Lispインタプリタがそこに期待するとおりの 関数かマクロを保持します(see section 評価)。また、ときどきは、キーボー ド・マクロ(see section キーボード・マクロ)、キーマップ(see section キーマップ)および 自動ロード・オブジェクト(see section 自動ロード)もシンボルの関数セルに格納 されることもあります。しばしば、"関数 foo"という言い方をします が、実際にはシンボルfooの関数セルに格納された関数を意味します。 これらは、必要なときにのみ使い分けられます。

属性リスト・セルは、通常正しい形式の属性リスト(see section 属性リスト) を保持すべきものであり、多くの関数はそこに属性リストがあるものとみなし ています。

関数セルや値セルは、void(無値)であるかも知れません。これは、そ のセルが何のオブジェクトも参照していないことを示しています(これは、 voidというシンボルを保持していることとは違いますし、nilと いうシンボルを保持していることとも違います)。voidであるセルをいじると、 `Symbol's value as variable is void'というようなエラーになります。

symbol-namesymbol-valuesymbol-plistsymbol-functionという四つの関数があり、これらは、シンボルの四つ のセルの内容を返します。ここで、buffer-file-nameというシンボルの 四つのセルの内容を例として示しましょう:

 
(symbol-name 'buffer-file-name)
     ⇒ "buffer-file-name"
(symbol-value 'buffer-file-name)
     ⇒ "/gnu/elisp/symbols.texi"
(symbol-plist 'buffer-file-name)
     ⇒ (variable-documentation 29529)
(symbol-function 'buffer-file-name)
     ⇒ #<subr buffer-file-name>

このシンボルは、カレント・バッファに対応づけられているファイルの名前を 保持する変数ですから、今その値セルの内容は、Emacs Lisp Manualのこの章の ソース・ファイルの名前になっています。 (8) 属性リスト・セルは、buffer-file-nameという変数の説明文字列が `DOC'ファイルのどこで見つかるかを説明関数に伝える (variable-documentation 29529)というリストを格納しています(29529 とは、説明文字列が始まる場所の、`DOC'ファイルの先頭からのオフセッ ト値です)。関数セルは、このファイル名を返す関数を保持しています。 buffer-file-nameが指すのはプリミティブ関数であり、これはリード構 文を持たず、ハッシュ表記(see section プリミティブ関数型)で印字されます。 Lisp言語で記述された関数を指すシンボルの場合であれば、このセルはラムダ 式(あるいはバイトコード・オブジェクト)を持つはずです。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.2 シンボルを定義する

Lispにおいて、定義(definition)とは、あるシンボルを特定の用途で用 いることを告知するための特殊形式です。Emacs Lispでは、シンボルを変数と して定義したり、関数(またはマクロ)として定義したり、さらに両方を独立に 行なったりできます。

定義構文要素はふつう、シンボルのある一つの用法における値や意味と、それ に加えてその用法におけるその意味に関する説明文も与えます。つまり、ある シンボルを変数として定義した場合には、その変数の初期値と、それに加えて その変数の説明文を与えることができます。

defvardefconstは、シンボルをグローバル変数として定義す る特殊形式です。それらはグローバル変数の定義に詳しく記述されていま す。

defunは、シンボルを関数として定義し、ラムダ式を作ってシンボルの 関数セルに格納します。したがってこのラムダ式がそのシンボルの関数定義と なります("関数定義"という用語は、関数セルの内容ということを意味します が、これはdefunがシンボルに対して、関数としての定義を与えるので あるという発想からきたものです)。defsubstdefaliasは、関 数を定義するほかの二とおりのやり方です。See section 関数

defmacroは、シンボルをマクロとして定義します。これはマクロ・オブ ジェクトを作成し、それをシンボルの関数セルに格納します。ここで注意して いただきたいのは、ある一つのシンボルはマクロまたは関数のどちらか一方に はなることができますが、同時に両方にはなれません。というのも、マクロと 関数のいずれもが関数セルに保持され、またどの時点でもセルはただ一つの Lispオブジェクトしか保持できないからです。See section マクロ

Emacs Lispにおいて、変数や関数としてシンボルを利用するにあたって、定義 は必須のものではありません。このため、シンボルをグローバル変数とするこ とはsetqで可能で、これは始めに定義を行なうかどうかには関係しま せん。定義の本当の目的は、プログラマとプログラミング・ツールをガイドす ることです。定義を行なうことにより、コードを読むプログラマに、あるシン ボルが変数とか関数として使われることを意図しているということを伝 えるわけです。さらに、`etags'`make-docfile'のようなユーティ リティは定義を認識し、適当な情報をタグ・テーブルや `emacs/etc/DOC-version'ファイルに加えるわけです。 See section 説明文字列へのアクセス


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.3 シンボルの作成と intern

GNU Emacs Lispにおいてシンボルがどのように作成されるかを理解するには、 Lispがそれらをどう読み込むのかを理解しなければなりません。Lispは、同じ ひと揃いの文字を読み込んだときには、常に同じシンボルを見つけ出さなけれ ばなりません。これに失敗すると、まるっきりの混乱を招くはずです。

Lispリーダがシンボルに出会うと、名前のすべての文字を読み込みます。次に それらの文字に対する"ハッシュ"が行なわれ、obarrayと呼ばれる表に 対するインデクスを見つけます。ハッシュというのは何かを検索するのに効率 的な手法です。たとえば電話帳でJan Jonesさんを探すときには、端から探して ゆく代りにまずJのところを始点にしてそこから探し始めます。これは単純な形 のハッシュ法です。obarrayの各要素はバケツ(bucket)であり、それはあ るハッシュ・コードに対するすべてのシンボルを保持しています。与えられた 名前を探すには、その名前のハッシュ・コードに対応するバケツに入っている すべてのシンボルを調べれば充分なのです。

所望の名前をもつシンボルが見つかると、リーダはそのシンボルを使います。 obarrayにその名前をもつシンボルが含まれていなければ、リーダは新たにシン ボルを作り、obarrayにそれを追加します。ある名前をもつシンボルを検索また は追加することを、internする(interning)といい、またそのシンボルは internされたシンボル(interned symbol)と呼ばれます。

internを行なうことで、それぞれのobarrayが、どんな特定の名前に対してもた だ一つのシンボルを持つことが保証されます。同じ名前のほかのシンボルも存 在しえますが、同じobarrayの中には存在しません。そのため、同じobarrayを 使って読込みを続けるかぎり、リーダは同じ名前に対しては同じシンボルを 得るのです。

すべてのシンボルを保持するobarrayは存在しません。それどころか、どの obarrayにも属さないシンボルもあります。それらは、internされていな いシンボル(uninterned symbols)と呼ばれます。internされていないシンボル でも、ほかのシンボル同様に四つのセルを持っています。しかし、それにアクセ スする唯一の方法は、ほかのオブジェクトの中から見つけるか、変数の値とし て探しだすかの方法しかありません。

Emacs Lispでは、obarrayは実はベクタです。ベクタの各要素はバケツであり、 その値は、名前がそのバケツにハッシュされるようなシンボルであるか、ある いはバケツが空ならば0になります。internされたシンボルはそれぞれバケツ内 での次のシンボルへの(ユーザには見えない)内部リンクを持ちます。これらの リンクが見えないため、あるobarray内のシンボルをすべて探し出すことは、 mapatoms(後述)を使う以外にはできません。バケツ内でのシンボルの 順序には意味はありません。

空のobarrayでは、どの要素も0であり、obarrayを(make-vector length 0)で作成することができます。これがobarrayを作成す る唯一の正当な方法です。 素数をlengthに使うと、よいハッシングになる傾 向があります。また、2のべき乗から1を引いた値もlengthとして適しています。

obarrayにシンボルを勝手に入れようとしてはいけません。これは動 作しません。internだけがobarryにシンボルを正しく入れることができ ます。一つのシンボルを二つのobarryにinternしてはなりません。こ れをやると両方のobarrayをグシャグシャにしてしまいます。というのも、シン ボルはobarryバケツの中での次のシンボルを入れておくスロットを一つしか持 たないためです。このようなことを行なったときの結果は予測できません。

二つの異なるシンボルが、別のobarray内で同じ名前をもつこともあり得ます。 その場合、これらのシンボルはeqでもequalでもありません。し かし、こうしたことはふつう省略形(abbrev)のメカニズム(see section 省略形とその展開)の 一部として起こるだけです。

Common Lisp注意書き:Common Lispの場合、一つのシンボルが複数の obarray相当にinternされ得ます。

以下の関数の大部分は引数として名前を取り、場合によってはそれに加えて obarrayを取ることもあります。名前が文字列でないか、またはobarrayがベク タでないと、wrong-type-argumentエラーが通知されます。

Function: symbol-name symbol

この関数はsymbolの名前である文字列を返します。 たとえば:

 
(symbol-name 'foo)
     ⇒ "foo"

文字を入れ換えてこの文字列を変更するとか、いろいろやればシンボルの名前 を変えることになりますが、 obarrayを更新できませんから、これはやらない でください。

Function: make-symbol name

この関数は、新たに割り付けられた、name(これは文字列であること)と いう名前のinternされていないシンボルを返します。その値と関数定義は存在 せず(voidであり)、その属性リストはnilです。以下の例において、 symの値はfooeqにはなりません。というのも、それは 同じく`foo'という名前をもつ、internされていない別のシンボルになる ためです。

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

この関数は、名前がnameであるinternされたシンボルを返します。 obarray obarrayにそのようなシンボルがない場合、internは新 しくそれを作り、それをそのobarrayに加えたうえで返します。obarray が省略された場合には、グローバル変数obarrayの値が用いられます。

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

(setq sym1 (intern "foo" other-obarray))
     ⇒ foo
(eq sym 'foo)
     ⇒ nil
Function: intern-soft name &optional obarray

この関数は、obarrayの中にある、名前がnameであるシンボルを返 し、またもしobarrayにそのような名前のシンボルがなければnil を返します。したがって、intern-softを、与えられた名前をもつシン ボルがすでにinternされているかどうかを調べるために用いることができます。 obarrayが省略された場合には、グローバル変数obarrayの値が用 いられます。

 
(intern-soft "frazzle")        ; そのようなシンボルは存在しない。
     ⇒ nil
(make-symbol "frazzle")        ; internされていないものを作る。
     ⇒ frazzle
(intern-soft "frazzle")        ; これは見つけ出せない。
     ⇒ nil
(setq sym (intern "frazzle"))  ; internされたものを作る。
     ⇒ frazzle
(intern-soft "frazzle")        ; これは見つけ出せる!
     ⇒ frazzle
(eq sym 'frazzle)              ; で、同じものになる。
     ⇒ t
Variable: obarray

この変数は、internreadで用いられる標準のobarrayです。

Function: mapatoms function &optional obarray

この関数は、obarray obarrayにある各シンボルそれぞれに対して functionを呼び出します。戻り値はnilです。obarrayが省 略されると、デフォルト値として普通のシンボル用の標準の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 &optional obarray

この関数はsymbolをobarry obarryから消去します。もしも symbolが実際にはobarryに入っていなかった場合、uninternは なにもしません。obarraynilの場合には現在のobarrayが使わ れます。

symbolにシンボルの代わりに文字列が与えられた場合、これはシンボル 名であるとみなされます。そのうえでuninternは、その名前を保持して いるobarrayから(もしあれば)そのシンボルを消去します。そのようなシンボル がないならば、uninternはなにもしません。

uninternはシンボルの消去を行なった場合にはtを返します。 そうでなければnilが返されます。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4 属性リスト

属性リスト(property list)(縮めてplist)はシンボルの属性リス ト・セルに格納された、要素を二つ単位でペアにしたリストのことです。 それぞれのペアは、属性の名前(通常はシンボル)と属性、いいかえれば値、を 結び付けます。属性リストは、シンボルに関する情報を記録しておくために広 く用いられ、たとえば変数としての説明やそのシンボルが定義されたファイル 名、さらにはひょっとすると言語解釈システムの中で(単語を表している)シン ボルの品詞を示すためにさえ利用されたりします。

文字列やバッファ内での文字の位置情報もまた属性リストをもつことが可能で す。See section テキスト属性

属性リストにおける属性の名前と値は、どんなLispオブジェクトであっても構 わないのですが、名前はふつうシンボルです。それらは、eqを使って比 較されます。以下に属性リストの例がありますが、これはコンパイラがロード されたときにprognというシンボルにおいて見られるものです:

 
(lisp-indent-function 0 byte-compile byte-compile-progn)

ここでlisp-indent-functionbyte-compileが属性名であ り、そのほかの要素はそれぞれに対応する値です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.1 属性リストと連想リスト

連想リスト(see section 連想リスト)は属性リストととても似通っています。 連想リストとは対照的に、属性の名前は一意でなければならないため、属性リ スト内でのペアの順序は重要ではありません。

さまざまなLispの関数名や変数に情報を付加するためには、属性リストの方が 連想リストよりも優れています。もしも全ての連想情報が一つの連想リストに 記録されていた場合、関数や変数が操作されようとする度に、そのプログラム はそのリスト全体を検索しなければなりません。対照的に、もしその情報がそ の関数名や変数そのものの属性リストに記録されていれば、一回の検索は一つ の属性リストの長さだけスキャンするだけですし、通常その長さは短いのです。 これが、変数に関する説明がvariable-documentationという名前の属性 リストに記録されていることの理由です。同様に、バイトコンパイラは特殊 な取り扱いを必要とするような関数を記録するために属性リストを使います。

とはいうものの、連想リストにもそれなりの利点があります。アプリケーショ ンによっては、連想リストの先頭に連想情報を追加する方が、属性を更新する よりも速い場合があります。一つのシンボルに対する全ての属性は同一の属性 リストに格納されますから、一つの属性名に対して異なる用法がぶつかる可能 性があります(このため、おそらく一意であるような属性名、すなわち属性名に ライブラリの名前を含ませたようなものを選ぶのがよいでしょう)。連想リスト は、連想情報がリストの先頭にプッシュされ、後で捨てられるスタックのよう にして使われ得るものです。これは属性リストでは不可能です。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.2 シンボルの属性リスト関数

Function: symbol-plist symbol

この関数はsymbolの属性リストを返します。

Function: setplist symbol plist

この関数はsymbolの属性リストをplistとします。通常、 plistはきちんとした形式の属性リストであるべきですが、これは強制 はされません。

 
(setplist 'foo '(a 1 b (2 3) c nil))
     ⇒ (a 1 b (2 3) c nil)
(symbol-plist 'foo)
     ⇒ (a 1 b (2 3) c nil)

特殊なobarrayで、それが普通の用途に使われないような場合、その中に置かれ たシンボルに関しては、おかしな格好をした属性リスト・セルを用いることが 意味を持つ場合もあるかも知れません。 実際、省略形(abbrev)の機構ではそう しています(see section 省略形とその展開)。

Function: get symbol property

この関数はsymbolの属性リストの中でpropertyという名前の 属性の値を見つけ出します。もしもそうした属性がない場合には、nil が返されます。したがって、nilという値がある場合と属性が存在しな い場合の区別はつきません。

propertyで与えられる名前は、存在している属性名とeqを使っ て比較されますから、どのようなオブジェクトでも合法的な属性になり得ます。

例としてputを御覧ください。

Function: put symbol property value

この関数はvaluesymbolの属性リストの中にあるproperty という名前の属性に入れ、以前の属性値を置き換えます。put関数は valueを返します。

 
(put 'fly 'verb 'transitive)
     ⇒'transitive
(put 'fly 'noun '(a buzzing little bug))
     ⇒ (a buzzing little bug)
(get 'fly 'verb)
     ⇒ transitive
(symbol-plist 'fly)
     ⇒ (verb transitive noun (a buzzing little bug))

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4.3 シンボルの外での属性リスト

以下の二つの関数はシンボル以外の場所に格納された属性リストを扱うのに 便利です:

Function: plist-get plist property

これは属性リストplistに格納されている属性propertyの値を返し ます。たとえば、

 
(plist-get '(foo 4) 'foo)
     ⇒ 4
Function: plist-put plist property value

これは属性リストplistに、属性propertyの値としてvalue を格納します。これはplistを破壊的に修正するかも知れませんし、ある いは古いものを変更することなく新しいリスト構造を作成するかも知れません。 この関数は変更された属性リストを返すので、plistを得た場所にそれを 書き込み戻すことができます。たとえば、

 
(setq my-plist '(bar t foo 4))
     ⇒ (bar t foo 4)
(setq my-plist (plist-put my-plist 'foo 69))
     ⇒ (bar t foo 69)
(setq my-plist (plist-put my-plist 'quux '(a)))
     ⇒ (quux (a) bar t foo 5)

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Yasutaka SHINDOH on September, 29 2006 using texi2html 1.76.