[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Emacs Lispにおける式の評価(evaluation)は、 Lispインタープリタ(Lisp interpreter)が行います。 これは、入力としてLispオブジェクトを受け取り、 式としての値を計算するプログラムです。 計算方法は、本章で述べる規則に従ってオブジェクトのデータ型に依存します。 インタープリタは、読者のプログラムのある部分を評価するために 自動的に動作しますが、Lisp基本関数eval
を介して インタープリタを明示的に呼ぶ出すこともできます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispインタープリタ、つまり、エバリュエータは、 与えられた式の値を計算するプログラムです。 Lispで書いた関数を呼び出すと、エバリュエータは、その関数本体内の 式を評価することで関数の値を計算します。 したがって、どんなLispプログラムの実行でも、 Lispインタープリタを実行することを意味します。
エバリュエータによるオブジェクトの扱い方は、 主にオブジェクトのデータ型に依存します。
評価することを意図したLispオブジェクトを 式(expression)とかフォーム(form)と呼びます。 式はデータオブジェクトであり単なるテキストではないという事実は、 Lisp様言語と典型的なプログラム言語との基本的な違いの1つです。 どんなオブジェクトでも評価できますが、実用上は、 数、シンボル、リスト、文字列を評価することが多いのです。
Lisp式を読み取りその式を評価することはとても一般的なことですが、 読み取りと評価は別々の動作であり、それぞれを別々に実行することもできます。 読み取り自体では、なにも評価しません。 Lispオブジェクトの表示表現をオブジェクトそのものに変換します。 このオブジェクトを評価すべきフォームとするか、 まったく別の目的に使うかは、read
の呼び出し側で決まります。
評価とコマンドキーの解釈を混同しないでください。 エディタコマンドループは、有効なキーマップを用いて キーボード入力をコマンド(対話的に呼び出し可能な関数)に変換し、 call-interactively
を使ってコマンドを起動します。 コマンドがLispで書いてあれば、 コマンド自体の実行には評価が関わってきますが、 そのことは、コマンドキーの解釈自体には含まれていません。
評価は再帰的な処理です。 つまり、フォームの評価では、 eval
を呼び出してそのフォームの一部分を評価することもあります。 たとえば、関数呼び出しの評価においては、まず、 関数呼び出しの各引数を評価してから、関数本体の各フォームを評価します。 (car x)
の評価を考えてみましょう。 まず最初にx
を再帰的に評価する必要があります。 その値を関数car
の引数として渡せるようにするのです。
関数呼び出しの評価においては、最終的に指定した関数を呼び出します。 See 節 11. 関数。 関数の実行そのものも、関数定義を評価する場合もあります。 あるいは、関数はC言語で実装されたLisp基本関数かもしれませんし、 バイトコード関数かもしれません(see 節 15. バイトコンパイル)。
フォームの評価は、環境(environment)と呼ばれる文脈において 行われます。 環境とは、すべてのLisp変数の現在値と束縛です (7)。 フォームが新たな束縛を作らずに変数を参照する場合には、 現在の環境におけるその変数の束縛の値を使います。 See 節 10. 変数。
フォームを評価すると、変数(see 節 10.3 ローカル変数)を束縛して、 再帰的評価のための新たな環境を作ることがあります。 これらの環境は一時的なもので、そのフォームの評価を完了すると 消えてしまいます。 フォームは恒久的な変更を行ってもかまいません。 このような変更を副作用(side effects)と呼びます。 副作用を持つフォームの例は、(setq foo 1)
です。
フォームの各種類ごとの評価の意味の詳細は、 以下で説明します(see 節 8.2 フォームの種類)。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
評価することを意図したLispオブジェクトをフォーム(form)と呼びます。 Emacsがどのようにフォームを評価するかは、そのデータ型に依存します。 Emacsには、評価方法が異なる3種類のフォームがあります。 シンボル、リスト、および、『その他すべての型』です。 本節では、3種類すべてについて1つ1つ説明します。 まず、自己評価型フォームである『その他すべての型』から説明します。
8.2.1 自己評価型フォーム Forms that evaluate to themselves. 8.2.2 シンボルフォーム Symbols evaluate as variables. 8.2.3 リストフォームの分類 How to distinguish various sorts of list forms. 8.2.4 シンボルの関数間接 When a symbol appears as the car of a list, we find the real function via the symbol. 8.2.5 関数フォームの評価 Forms that call functions. 8.2.6 Lispマクロの評価 Forms that call macros. 8.2.7 スペシャルフォーム "Special forms" are idiosyncratic primitives, most of them extremely important. 8.2.8 自動ロード Functions set up to load files containing their real definitions.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
自己評価型フォーム(self-evaluating form)とは、 リストでもシンボルでもない任意のフォームのことです。 自己評価型フォームはそれ自身に評価され、 評価結果は評価されるオブジェクトと同じものです。 つまり、数25は25と評価され、 文字列"foo"
は文字列"foo"
と評価されます。 同様に、ベクトルを評価してもベクトルの個々の要素を評価することはありません。 その内容をまったく変更することなく、同じベクトルを返します。
'123 ; 評価していない数 => 123 123 ; 普通どおり評価。結果は同じ => 123 (eval '123) ; 『手で』評価。結果は同じ => 123 (eval (eval '123)) ; 2回評価してもなにも変わらない => 123 |
Lispコードにおいては、数、文字、文字列、さらにベクトルでさえも、 それらが自己評価型である事実を利用して書くのが普通です。 しかし、入力構文を持たない型については、このようにしません。 というのは、それらをテキストとして書く方法がないからです。 そのような型を含むLisp式を構成するには、Lispプログラムを使います。
;; バッファオブジェクトを含む式を作る (setq print-exp (list 'print (current-buffer))) => (print # |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
シンボルを評価するときには、シンボルを変数として扱います。 その結果は、値があれば、変数の値です。 (値セルが空であり)値がなければ、エラーを通知します。 変数の使い方について詳しくは、See 節 10. 変数。
つぎの例では、setq
を使ってシンボルの値を設定します。 そのあとでシンボルを評価すると、setq
で保存した値を取り出せます。
(setq a 123) => 123 (eval 'a) => 123 a => 123 |
シンボルnil
とt
は特別に扱い、 nil
の値はつねにnil
であり、 t
の値はつねにt
です。 これらに別の値を設定したり、別の値を束縛することはできません。 したがって、eval
はこれらを他のシンボルと同様に扱いますが、 これら2つのシンボルは自己評価型フォームのようにふるまいます。 `:'で始まる名前のシンボルも同じ意味で自己評価型であり、 同様に、その値を変更できません。 See 節 10.2 変更不可能な変数。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
フォームが空ではないリストならば、その最初の要素に依存して、 関数呼び出し、マクロ呼び出し、スペシャルフォームのいずれかです。 これらの3種類のフォームは、以下に説明するように、異なる方法で評価されます。 リストの残りの要素は、関数、マクロ、スペシャルフォームの 引数(arguments)になります。
空ではないリストを評価する最初の手順は、 その先頭要素を調べることです。 この要素は、それだけで、空ではないリストのフォームの種類を決定し、 リストの残りをどのように処理するかを決定します。 SchemeなどのLispの一部の方言と違って、先頭要素は評価しません。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
リストの先頭要素がシンボルであると、 評価処理ではシンボルの関数セルを調べ、 もとのシンボルのかわりにその内容を使います。 その内容が別のシンボルであると、 シンボルの関数間接(symbol function indirection)と呼ばれる この処理をシンボルでないものを得るまで繰り返します。 シンボルの関数セルに格納された関数名としてのシンボルの使い方について 詳しくは、See 節 11.3 関数を命名する。
この処理の結果、無限ループになる場合もあります。 つまり、シンボルの関数セルが同じシンボルを指している場合です。 あるいは、シンボルの関数セルが空の場合もありえます。 その場合、サブルーティンsymbol-function
は、 エラーvoid-function
を通知します。 いずれの場合でもなければ、最終的にはシンボルでないものを取得し、 それは関数などの適切なオブジェクトであるはずです。
より正確にいえば、Lisp関数(ラムダ式)、バイトコード関数、 基本関数、Lispマクロ、スペシャルフォーム、自動ロードオブジェクトの いずれかを取得しているはずです。 これらの各種類ごとに、以下の1つ1つの節で説明します。 オブジェクトがこれらのいずれの型でもない場合には、 エラーinvalid-function
を通知します。
つぎの例は、シンボルの関数間接の処理を図示したものです。 fset
を使ってシンボルの関数セルに設定し、 symbol-function
を使って関数セルの内容を取り出します (see 節 11.8 関数セルの内容の参照)。 具体的には、シンボルcar
をfirst
の関数セルに格納し、 シンボルfirst
をerste
の関数セルに格納します。
;; このような関数セルのリンクを作る ;; ------------- ----- ------- ------- ;; | # |
(symbol-function 'car) => # |
一方、つぎの例では、シンボルの関数間接を使わずに関数を呼び出します。 というのは、先頭引数はLispの無名関数であって、 シンボルではないからです。
((lambda (arg) (erste arg)) '(1 2 3)) => 1 |
関数を実行することは、その本体を評価することです。 この過程では、erste
を呼び出すときにシンボルの関数間接が関わります。
組み込み関数indirect-function
は、 明示的にシンボルの関数間接を行う簡単な方法です。
Lispでindirect-function
を定義するとつぎのようになる。
(defun indirect-function (function) (if (symbolp function) (indirect-function (symbol-function function)) function)) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
評価すべきリストの先頭要素が、Lisp関数オブジェクト、 バイトコードオブジェクト、基本関数オブジェクトの場合、 そのリストは関数呼び出し(function call)です。 たとえば、つぎは、関数+
の呼び出しです。
(+ 1 x) |
関数呼び出しを評価する最初の手順は、 リストの残りの要素を左から右へ順に評価することです。 その結果は実引数の値になり、1つの値がリストの1つの要素に対応します。 つぎの手順は、引数のリストで関数を呼び出すことで、 実質的には、関数apply
(see 節 11.5 関数呼び出し)を使います。 関数がLispで書いてあれば、関数の引数変数を束縛するために引数を使います (see 節 11.2 ラムダ式)。 そして、関数本体のフォーム群を順番に評価し、 本体の最後のフォームの値が関数呼び出しの値になります。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
評価すべきリストの先頭要素がマクロオブジェクトの場合、 そのリストはマクロ呼び出し(macro call)です。 マクロ呼び出しを評価するときは、リストの残りの要素を評価しません。 そのかわりに、要素そのものをマクロの引数として使います。 マクロ定義は、 マクロの展開形(expansion)と呼ばれる置換フォームを計算し、 もとのフォームのかわりに展開形を評価します。 展開形は、どんな種類のフォームでもかまいません。 自己評価型の定数、シンボル、あるいは、リストです。 展開形そのものがマクロ呼び出しであると、 マクロ呼び出し以外のフォームを得られるまで、 展開形を得る処理を繰り返します。
通常のマクロ呼び出しの評価は、展開形を評価することで完了します。 しかし、マクロの展開形を必ずしもただちに評価する必要はなく、 まったく評価しなくてもかまいません。 というのは、別のプログラムもマクロ呼び出しを展開し、 それらは展開形を評価するものもあれば、評価しないものもあるからです。
普通、引数の式は、マクロ展開の計算過程では評価せず、 展開形の一部として現れます。 そして、展開形を評価するときに引数が計算されます。
たとえば、つぎのようなマクロ定義があったとします。
(defmacro cadr (x) (list 'car (list 'cdr x))) |
(cadr (assq 'handler list))
のような式はマクロ呼び出しであり、 つぎのような展開形になります。
(car (cdr (assq 'handler list))) |
引数(assq 'handler list)
が展開形に現れていることに 注意してください。
Emacs Lispのマクロに関する完全な記述は、See 節 12. マクロ。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
スペシャルフォーム(special form)は、 その引数を評価しないように特別な印が付いた基本関数です。 ほとんどのスペシャルフォームは、制御構造を定義したり、 変数を束縛したりします。 これらはどれも関数ではできないことです。
各スペシャルフォームには、どの引数は評価し、 どの引数は評価せずに使うかといったそれぞれに独自の規則があります。 特定の引数を評価するかどうかは、他の引数の評価結果に依存することもあります。
以下に、Emacs Lispのすべてのスペシャルフォームをアルファベット順に、 参照箇所とともにあげておきます。
and
catch
catch
とthrow
cond
condition-case
defconst
defmacro
defun
defvar
function
if
interactive
let
let*
or
prog1
prog2
progn
quote
save-current-buffer
save-excursion
save-restriction
save-window-excursion
setq
setq-default
track-mouse
unwind-protect
while
with-output-to-temp-buffer
Common Lispに関した注意:GNU Emacs LispとCommon Lispのスペシャルフォームを比較してみる。
setq
、if
、および、catch
は、Emacs Lispでも Common Lispでもスペシャルフォームである。defun
は、Emacs Lispではスペシャルフォームであるが、 Common Lispではマクロである。save-excursion
は、Emacs Lispではスペシャルフォームであるが、 Common Lispには存在しない。throw
は、Common Lispでは(複数の値を返す必要があるため) スペシャルフォームであるが、 Emacs Lispでは(複数の値はないため)関数である。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
自動ロード(autoload)は、 関数やマクロの関数定義をEmacsにまだロードしていなくても、 関数やマクロを呼び出せるようにする機構です。 定義を収めたファイルを指定します。 シンボルの関数定義に自動ロードオブジェクトあるとき、 そのシンボルを関数として呼び出すと、指定したファイルを自動的にロードします。 そうしてから、当該ファイルからロードした実際の定義を呼び出します。 See 節 14.4 自動ロード。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
スペシャルフォームquote
は、単一の引数を 評価せずに書かれたとおりに返します。 これは、自己評価型オブジェクトではない 定数シンボルや定数リストをプログラム内に書く手段です。 (数、文字列、ベクトルなどの自己評価型オブジェクトをクォートする必要はない。)
quote
はプログラム内で頻繁に使うので、 Lispには便利な入力構文が用意してあります。 アポストロフ文字(`'')に続けた(入力構文で書いた)Lispオブジェクトは、 先頭要素がquote
であり2番目の要素がそのオブジェクトである リストに展開されます。 したがって、入力構文'x
は、(quote x)
の省略形です。
quote
を使った式の例をいくつかあげておきます。
(quote (+ 1 2)) => (+ 1 2) (quote foo) => foo 'foo => foo ''foo => (quote foo) '(quote foo) => (quote foo) ['foo] => [(quote foo)] |
他のクォートの書き方には、function
(see 節 11.7 無名関数)が あります。 これは、Lispで書いた無名ラムダ式をコンパイルするようにします。 また、``'(see 節 12.5 バッククォート)は、 リストの一部分をクォートし、他の部分は計算結果で置き換えるために使います。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
ほとんどの場合、実行中のプログラムにフォームが現れると フォームは自動的に評価されます。 稀なことですが、実行時に計算したフォームを評価するように コードを書く必要があるかもしれません。 たとえば、編集中のテキストからフォームを読み取ったり、 属性リストからフォームを取り出した場合などです。 このような場合には、関数eval
を使います。
本節で説明した関数や変数は、フォームを評価したり、 評価処理に制限を課したり、最後の戻り値を記録したりします。 ファイルをロードしても評価が行われます(see 節 14. ロード)。
注意: データ構造の中に関数を格納して それを
funcall
やapply
で呼び出すほうが、 データ構造の中に式を格納してそれを評価するより、 一般に明確で柔軟性があります。 関数を使うとそれらに引数として情報を渡すことができます。
eval
は関数なので、eval
の呼び出しに現れる 引数の式は2度評価される。 eval
を呼び出すまえの準備で1回、 関数eval
自身による評価でもう1回である。 例を示す。
(setq foo 'bar) => bar (setq bar 'baz) => baz ;; |
eval
の呼び出しの深さは、 max-lisp-eval-depth
(下記参照)に制限される。
eval
を呼び出すことを リージョンの末尾に達するまで、あるいは、処理されないエラーが通知されるまで 繰り返す。
streamがnil
以外ならば、 リージョン内の式を評価した結果の値はstreamを使って表示する。 see 節 18.4 出力ストリーム。
read-functionがnil
以外にならば、 それは関数である必要があり、 read
のかわりに式を1つ1つ読むために使われる。 この関数は、入力用のストリームである1つの引数で呼び出される。 変数load-read-function
(see 節 14.1 プログラムからのロード方法)を 使ってこの関数を指定することもできるが、 引数read-functionを用いたほうが堅牢である。
eval-region
はつねにnil
を返す。
eval-region
と同様だが、バッファ全体に作用する。"Lisp nesting exceeds max-lisp-eval-depth"
で) エラーを通知までのeval
、apply
、funcall
の呼び出しの 最大の深さを制限する。 この制限、および、これを超えたときのエラーは、 不正に定義された関数によってLispが無限に再帰することを防止する 1つの方法である。
深さ制限は、Lispコードによる明示的な呼び出しに加えて、 Lisp式で書かれた関数の呼び出しや関数呼び出しの引数や関数本体のフォームの 再帰的な評価などの内部的なeval
、apply
、funcall
の 呼び出しも数える。
この変数のデフォルト値は300。 これに100未満の値を設定すると、指定した値に達するとLispは100に設定し直す。 Lispデバッガに入ったとき、 制限に近い場合にはデバッガ自身が実行できることを保証するために値を増やす。
max-specpdl-size
は、入れ子の深さを制限する別の方法である。 see 節 10.3 ローカル変数。
(setq x 1) => 1 (list 'A (1+ 2) auto-save-default) => (A 3 t) values => ((A 3 t) 1 ...) |
この変数は、最近評価したフォームの値を参照するのに便利である。 values
そのものの値の表示は非常に長くなる可能性があるので、 その値を表示するのはよくない。 そのかわりに、つぎのようにして特定の要素を調べる。
;; もっとも最近の評価結果を参照する (nth 0 values) => (A 3 t) ;; こうすると、新たな要素が追加され、 ;; すべての要素が1つうしろへさがる (nth 1 values) => (A 3 t) ;; この例を実行するまえの最新のもののつぎの要素を取得する (nth 3 values) => 1 |
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |