[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
シンボル(symbol)とは、一意な名前が付いたオブジェクトです。 本章では、シンボル、その構成要素、属性リスト、作成方法とインターン方法に ついて説明します。 シンボルの変数としての使用方法、関数名としての使用方法について説明した 別の章もあります。 10. 変数とSee 節 11. 関数。 シンボルの正確な入力構文については、See 節 2.3.4 シンボル型。
symbolp
で、任意のLispオブジェクトがシンボルかどうか調べられます。
t
を返し、 さもなければnil
を返す。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
各シンボルには4つの構成要素(つまり、『セル』)があり、 それぞれで別のオブジェクトを参照します。
symbol-name
を参照。
symbol-value
を参照。
symbol-function
を参照。
symbol-plist
を参照。表示名セルはつねに文字列を保持していて、変更できません。 他の3つのセルには、任意の指定したLispオブジェクトを個別に設定できます。
表示名セルは、シンボルの名前である文字列を保持しています。 シンボルはテキスト上はその名前で表現されるので、 2つのシンボルが同じ名前を持たないことが重要です。 Lispリーダがこのことを保証します。 シンボルを読み取るたびに、新たにシンボルを作成するまえに、 指定した名前のシンボルが存在するかどうか調べます。 (GNU Emacs Lispでは、これにはハッシュアルゴリズムと オブジェクト配列obarrayを使う。 see 節 7.3 シンボルの作成とインターン。)
普通の使い方では、関数セルには関数(see 節 11. 関数)や マクロ(see 節 12. マクロ)が入っていて、 Lispインタープリタはそのように仮定します(see 節 8. 評価)。 シンボルの関数セルには、 キーボードマクロ(see 節 20.14 キーボードマクロ)、キーマップ(see 節 21. キーマップ)、 自動ロードオブジェクト(see 節 8.2.8 自動ロード)が入っていることもあります。 『関数foo
』といった場合、実際には、シンボルfoo
の関数セルに 入っている関数を意味します。 必要な場合に限って区別します。
属性リストセルは、通常、正しい形式の属性リスト(see 節 7.4 属性リスト)が 入っている必要があり、さまざまな関数がそのように仮定しています。
関数セルや値セルは空(void)でもかまいません。 つまり、セルはどんなオブジェクトも指していません。 (このことは、シンボルvoid
を保持しているとか、 シンボルnil
を保持しているのとは違う。) 空である関数セルや値セルを参照すると、 その結果は`Symbol's value as variable is void' (「変数としてのシンボルの値は空」)のようなエラーになります。
4つの関数、symbol-name
、symbol-value
、 symbol-plist
、symbol-function
は、 シンボルの4つのセルの内容を返します。 以下に、シンボルbuffer-file-name
の 4つのセルの内容を表示する例を示します。
(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) => # |
このシンボルは、カレントバッファで訪問しているファイルの名前を保持するので、 値セルの内容は本書Emacs Lispマニュアルの本章のソースファイルの名前です。 属性リストセルには、リスト(variable-documentation 29529)
が入っていて、 ドキュメント関数に対してファイル`DOC-version'のどこに 変数buffer-file-name
の説明文字列が入っているか伝えます。 (29529は、当該説明文字列の開始位置を表す `DOC-version'の先頭からのオフセット。 23.1 説明文の基本を参照。) 関数セルには、ファイルの名前を返す関数が入っています。 buffer-file-name
は基本関数の名前です。 これには入力構文はなく、 ハッシュ記法(see 節 2.3.14 基本関数型)で表示されています。 Lispで書いた関数を表すシンボルでは、 このセルにラムダ式(あるいはバイトコードオブジェクト)が入っています。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispにおける定義(definition)とは、 特定のシンボルをどのように使うかを意思表示するスペシャルフォームです。 Emacs Lispでは、シンボルを変数と定義したり、 関数(あるいはマクロ)と定義したり、あるいは、それらを独立に定義できます。
定義を行う構文では、典型的には、値を指定したり、 シンボルを特定の使い方をすると指定したりし、 さらに、そのような使い方をしたときの意味を表すための説明文字列を指定します。 したがって、シンボルを変数として定義するときには、 変数に初期値を与え、その変数の説明文字列を指定できます。
defvar
とdefconst
は、シンボルをグローバル変数として定義する スペシャルフォームです。 これらは10.5 グローバル変数を定義するで詳しく説明してあります。 カスタマイズ可能なようにユーザーオプション用の変数を定義するには、 defcustom
(see 節 13. カスタマイズ定義の書き方)を使います。
defun
は、シンボルを関数として定義し、 ラムダ式を作ってシンボルの関数セルに格納します。 したがって、このラムダ式がシンボルの関数定義になります。 (用語『関数定義』は、関数セルの内容を意味し、 defun
がシンボルに関数としての定義を与えることからきている。) defsubst
とdefalias
は、関数を定義する別の2つの方法です。 See 節 11. 関数。
defmacro
は、シンボルをマクロとして定義します。 マクロオブジェクトを作ってシンボルの関数セルに格納します。 シンボルは、マクロか関数のいずれかであって、 同時に両方にはならないことに注意してください。 というのは、マクロ定義も関数定義も関数セルに収められ、 そのセルにはどんなときでもたった1つのLispオブジェクトしか 保持できないからです。 See 節 12. マクロ。
Emacs Lispでは、シンボルを変数や関数として使うための 定義は必須ではありません。 したがって、シンボルをあらかじめ定義しようがしまいが、 setq
を使ってシンボルをグローバル変数にできます。 定義の真の目的は、プログラマに対する指針であり、プログラミングツールなのです。 これらは、コードを読むプログラマに対して、 特定のシンボルを変数として使うのか関数として使うのか、 その意図を伝えます。 さらに、`etags'や`make-docfile'などのユーティリティは、 定義を認識してタグテーブルやファイル`DOC-version'に 適切な情報を追加します。 See 節 23.2 説明文字列の参照。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
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-name 'foo) => "foo" |
警告: 文字列の文字を置き換えるとシンボルの名前を変更するが、 オブジェクト配列は更新できないので変更しないこと!
nil
である。 以下の例では、sym
の値はfoo
とeq
ではない。 なぜなら、名前は`foo'ではあるが、 インターンしていない別のシンボルであるため。
(setq sym (make-symbol "foo")) => foo (eq sym 'foo) => nil |
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
の引数は文字列である必要があり、 シンボルではない。
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 |
intern
やread
が使う標準のオブジェクト配列。nil
を返す。 obarrayを省略すると、通常のシンボル向けの標準のオブジェクト配列である obarray
の値をデフォルトにする。
(setq count 0) => 0 (defun count-syms (s) (setq count (1+ count))) => count-syms (mapatoms 'count-syms) => nil count => 1871 |
mapatoms
を使った別の例については、 23.2 説明文字列の参照のdocumentation
を参照。
symbol
が実際にはオブジェクト配列内になければ、 unintern
はなにもしない。 obarrayがnil
であると、現在のオブジェクト配列を使う。
symbolのシンボルのかわりに文字列を指定すると、 それはシンボルの名前を表す。 そして、unintern
はその名前のシンボルを(あれば)オブジェクト配列から 削除する。 そのようなシンボルがなければ、unintern
はなにもしない。
unintern
は、シンボルを削除したときにはt
を返す。 さもなければnil
を返す。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
属性リスト(property list、略してplist)とは、 シンボルの属性リストセルに格納された対になった要素から成るリストです。 各対は、属性名(通常、シンボル)を属性、すなわち、属性値に対応付けます。 属性リストは、一般に、シンボルに関する情報を記録します。 変数としての説明文字列、定義されているファイルの名前、 言語理解システムにおいては(語を表す)シンボルの文法クラスなどです。
文字列内やバッファ内の文字位置も属性リストを持てます。 See 節 31.19 テキスト属性。
属性リスト内の属性名と属性値は、任意のLispオブジェクトでかまいませんが、 普通、属性名はシンボルです。 属性リスト関数は、eq
を使って属性名を比較します。 コンパイラをロードした際のシンボルprogn
の属性リストをつぎに示します。
(lisp-indent-function 0 byte-compile byte-compile-progn) |
ここで、lisp-indent-function
やbyte-compile
は属性名であり、 他の2つの要素は対応する属性値です。
7.4.1 属性リストと連想リスト Comparison of the advantages of property lists and association lists. 7.4.2 シンボル向け属性リスト関数 Functions to access symbols' property lists. 7.4.3 シンボルの外部の属性リスト Accessing property lists stored elsewhere.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
連想リスト(see 節 5.8 連想リスト)は、 属性リストに非常によく似ています。 連想リストと異なり、属性名は一意である必要があるので、 属性リスト内での対の出現順序は関係ありません。
さまざまなLisp関数やLisp変数に情報を付加するには、 属性リストは連想リストより優れています。 読者のプログラムで1つの連想リストにすべての連想を入れておいたとすると、 1つの連想を探すたびに、リスト全体を探索する必要があります。 これには時間がかかります。 一方、同じ情報を関数名や変数自身の属性リストに保持しておけば、 各探索では1つの属性リストを走査するだけでよく、 属性リストは、普通、短いものです。 このため、変数の説明文字列をvariable-documentation
という名前の 属性に記録しているのです。 同様に、バイトコンパイラも、 特別な処理が必要な関数を属性を使って記録しています。
しかしながら、連想リストにもそれ独自の利点があります。 読者のアプリケーションに依存しますが、 属性を更新するより、連想リストの先頭に連想を追加するほうが速いです。 あるシンボルのすべての属性は同一の属性リストに格納してあるので、 1つの属性名を異なる目的に使うと衝突します。 (この理由から、プログラムで普通に使う 変数名や関数名の接頭辞で始まる属性名を選ぶなどして、 一意な属性名を選ぶのがよい。) 連想リストは、リストの先頭に要素を追加し、先頭から要素を削除するので、 スタックのように使えます。 属性リストでは、これは不可能です。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
(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) |
普通の使い方を意図していない特別なオブジェクト配列内のシンボルに対しては、 属性リストセルの非標準な使い方にも意味があろう。 実際、略語機構(see 節 35. 略語と略語の展開)ではそのようにしている。
nil
を返す。 つまり、nil
という値と属性の欠如を区別できない。
名前propertyは既存の属性名とeq
で比較するため、 どんなオブジェクトでも正当な属性である。
例については、put
を参照。
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 ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
シンボル以外の場所に保存した属性リストの操作に便利な2つの関数があります。
(plist-get '(foo 4) 'foo) => 4 |
(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))) => (bar t foo 69 quux (a)) |
つぎのようにして、plist-put
を用いてput
を定義できます。
(defun put (symbol prop value) (setplist symbol (plist-put (symbol-plist symbol) prop value))) |
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |