[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

7. シンボル

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Symbols"
"texi/elisp21/シンボル"へのコメント(無し)

シンボル(symbol)とは、一意な名前が付いたオブジェクトです。 本章では、シンボル、その構成要素、属性リスト、作成方法とインターン方法に ついて説明します。 シンボルの変数としての使用方法、関数名としての使用方法について説明した 別の章もあります。 10. 変数とSee 節 11. 関数。 シンボルの正確な入力構文については、See 節 2.3.4 シンボル型

symbolpで、任意のLispオブジェクトがシンボルかどうか調べられます。

Function: symbolp object
この関数は、objectがシンボルならばtを返し、 さもなければnilを返す。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

7.1 シンボルの構成要素

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Symbol%20Components"
"texi/elisp21/シンボルの構成要素"へのコメント(無し)

各シンボルには4つの構成要素(つまり、『セル』)があり、 それぞれで別のオブジェクトを参照します。

表示名(print name)
表示名セル(print name cell)には、 シンボルの入力や表示に使う名前である文字列が入っている。 7.3 シンボルの作成とインターンsymbol-nameを参照。

値(value)
値セル(value cell)には、 シンボルの変数としての現在値が入っている。 シンボルをフォームとして使用したとき、 フォームの値はシンボルの値セルの内容である。 10.7 変数値の参照symbol-valueを参照。

関数(function)
関数セル(function cell)には、シンボルの関数定義が入っている。 シンボルを関数として使用したとき、その関数定義を使う。 シンボルが、編集コマンドを実行するためのキーマップやキーボードマクロを 表すときもこのセルを使う。 各シンボルには値セルと関数セルが別々にあるので、 変数名と関数名は衝突しない。 11.8 関数セルの内容の参照symbol-functionを参照。

属性リスト(property list)
属性リストセル(property list cell)には、 シンボルの属性リストが入っている。 7.4 属性リスト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-namesymbol-valuesymbol-plistsymbol-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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

7.2 シンボルを定義する

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Definitions"
"texi/elisp21/シンボルを定義する"へのコメント(無し)

Lispにおける定義(definition)とは、 特定のシンボルをどのように使うかを意思表示するスペシャルフォームです。 Emacs Lispでは、シンボルを変数と定義したり、 関数(あるいはマクロ)と定義したり、あるいは、それらを独立に定義できます。

定義を行う構文では、典型的には、値を指定したり、 シンボルを特定の使い方をすると指定したりし、 さらに、そのような使い方をしたときの意味を表すための説明文字列を指定します。 したがって、シンボルを変数として定義するときには、 変数に初期値を与え、その変数の説明文字列を指定できます。

defvardefconstは、シンボルをグローバル変数として定義する スペシャルフォームです。 これらは10.5 グローバル変数を定義するで詳しく説明してあります。 カスタマイズ可能なようにユーザーオプション用の変数を定義するには、 defcustom(see 節 13. カスタマイズ定義の書き方)を使います。

defunは、シンボルを関数として定義し、 ラムダ式を作ってシンボルの関数セルに格納します。 したがって、このラムダ式がシンボルの関数定義になります。 (用語『関数定義』は、関数セルの内容を意味し、 defunがシンボルに関数としての定義を与えることからきている。) defsubstdefaliasは、関数を定義する別の2つの方法です。 See 節 11. 関数

defmacroは、シンボルをマクロとして定義します。 マクロオブジェクトを作ってシンボルの関数セルに格納します。 シンボルは、マクロか関数のいずれかであって、 同時に両方にはならないことに注意してください。 というのは、マクロ定義も関数定義も関数セルに収められ、 そのセルにはどんなときでもたった1つのLispオブジェクトしか 保持できないからです。 See 節 12. マクロ

Emacs Lispでは、シンボルを変数や関数として使うための 定義は必須ではありません。 したがって、シンボルをあらかじめ定義しようがしまいが、 setqを使ってシンボルをグローバル変数にできます。 定義の真の目的は、プログラマに対する指針であり、プログラミングツールなのです。 これらは、コードを読むプログラマに対して、 特定のシンボルを変数として使うのか関数として使うのか、 その意図を伝えます。 さらに、`etags'や`make-docfile'などのユーティリティは、 定義を認識してタグテーブルやファイル`DOC-version'に 適切な情報を追加します。 See 節 23.2 説明文字列の参照



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

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

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Creating%20Symbols"
"texi/elisp21/シンボルの作成とインターン"へのコメント(無し)

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を使った別の例については、 23.2 説明文字列の参照documentationを参照。

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

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

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



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

7.4 属性リスト

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Property%20Lists"
"texi/elisp21/属性リスト"へのコメント(無し)

属性リスト(property list、略してplist)とは、 シンボルの属性リストセルに格納された対になった要素から成るリストです。 各対は、属性名(通常、シンボル)を属性、すなわち、属性値に対応付けます。 属性リストは、一般に、シンボルに関する情報を記録します。 変数としての説明文字列、定義されているファイルの名前、 言語理解システムにおいては(語を表す)シンボルの文法クラスなどです。

文字列内やバッファ内の文字位置も属性リストを持てます。 See 節 31.19 テキスト属性

属性リスト内の属性名と属性値は、任意のLispオブジェクトでかまいませんが、 普通、属性名はシンボルです。 属性リスト関数は、eqを使って属性名を比較します。 コンパイラをロードした際のシンボルprognの属性リストをつぎに示します。

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

ここで、lisp-indent-functionbyte-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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

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

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Plists%20and%20Alists"
"texi/elisp21/属性リストと連想リスト"へのコメント(無し)

連想リスト(see 節 5.8 連想リスト)は、 属性リストに非常によく似ています。 連想リストと異なり、属性名は一意である必要があるので、 属性リスト内での対の出現順序は関係ありません。

さまざまなLisp関数やLisp変数に情報を付加するには、 属性リストは連想リストより優れています。 読者のプログラムで1つの連想リストにすべての連想を入れておいたとすると、 1つの連想を探すたびに、リスト全体を探索する必要があります。 これには時間がかかります。 一方、同じ情報を関数名や変数自身の属性リストに保持しておけば、 各探索では1つの属性リストを走査するだけでよく、 属性リストは、普通、短いものです。 このため、変数の説明文字列をvariable-documentationという名前の 属性に記録しているのです。 同様に、バイトコンパイラも、 特別な処理が必要な関数を属性を使って記録しています。

しかしながら、連想リストにもそれ独自の利点があります。 読者のアプリケーションに依存しますが、 属性を更新するより、連想リストの先頭に連想を追加するほうが速いです。 あるシンボルのすべての属性は同一の属性リストに格納してあるので、 1つの属性名を異なる目的に使うと衝突します。 (この理由から、プログラムで普通に使う 変数名や関数名の接頭辞で始まる属性名を選ぶなどして、 一意な属性名を選ぶのがよい。) 連想リストは、リストの先頭に要素を追加し、先頭から要素を削除するので、 スタックのように使えます。 属性リストでは、これは不可能です。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

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

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Symbol%20Plists"
"texi/elisp21/シンボル向け属性リスト関数"へのコメント(無し)

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)

普通の使い方を意図していない特別なオブジェクト配列内のシンボルに対しては、 属性リストセルの非標準な使い方にも意味があろう。 実際、略語機構(see 節 35. 略語と略語の展開 (2003/10/30))ではそのようにしている。

Function: get symbol property
この関数は、symbolの属性リストから propertyという名前の属性の値を探す。 そのような属性がなければ、nilを返す。 つまり、nilという値と属性の欠如を区別できない。

名前propertyは既存の属性名とeqで比較するため、 どんなオブジェクトでも正当な属性である。

例については、putを参照。

Function: put symbol property value
この関数は、symbolの属性リストにおいて、 属性名propertyの古い属性値をvalueで置き換える。 関数putvalueを返す。

 
(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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端]

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

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp21&node=Other%20Plists"
"texi/elisp21/シンボルの外部の属性リスト"へのコメント(無し)

シンボル以外の場所に保存した属性リストの操作に便利な2つの関数があります。

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)))
     => (bar t foo 69 quux (a))

つぎのようにして、plist-putを用いてputを定義できます。

 
(defun put (symbol prop value)
  (setplist symbol
            (plist-put (symbol-plist symbol) prop value)))

[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端]