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

8. 評価

Emacs Lispにおける式の評価(evaluation)は、 Lispインタープリタ(Lisp interpreter)が行います。 これは、入力としてLispオブジェクトを受け取り、 式としての値を計算するプログラムです。 計算方法は、本章で述べる規則に従ってオブジェクトのデータ型に依存します。 インタープリタは、読者のプログラムのある部分を評価するために 自動的に動作しますが、Lisp基本関数evalを介して インタープリタを明示的に呼ぶ出すこともできます。

8.1 評価とは  Evaluation in the scheme of things.
8.2 フォームの種類  How various sorts of objects are evaluated.
8.3 クォート  Avoiding evaluation (to put constants in the program).
8.4 評価(eval)  How to invoke the Lisp interpreter explicitly.


8.1 評価とは

Lispインタープリタ、つまり、エバリュエータは、 与えられた式の値を計算するプログラムです。 Lispで書いた関数を呼び出すと、エバリュエータは、その関数本体内の 式を評価することで関数の値を計算します。 したがって、どんなLispプログラムの実行でも、 Lispインタープリタを実行することを意味します。

エバリュエータによるオブジェクトの扱い方は、 主にオブジェクトのデータ型に依存します。

評価することを意図したLispオブジェクトを (expression)とかフォーム(form)と呼びます。 式はデータオブジェクトであり単なるテキストではないという事実は、 Lisp様言語と典型的なプログラム言語との基本的な違いの1つです。 どんなオブジェクトでも評価できますが、実用上は、 数、シンボル、リスト、文字列を評価することが多いのです。

Lisp式を読み取りその式を評価することはとても一般的なことですが、 読み取りと評価は別々の動作であり、それぞれを別々に実行することもできます。 読み取り自体では、なにも評価しません。 Lispオブジェクトの表示表現をオブジェクトそのものに変換します。 このオブジェクトを評価すべきフォームとするか、 まったく別の目的に使うかは、readの呼び出し側で決まります。

評価とコマンドキーの解釈を混同しないでください。 エディタコマンドループは、有効なキーマップを用いて キーボード入力をコマンド(対話的に呼び出し可能な関数)に変換し、 call-interactivelyを使ってコマンドを起動します。 コマンドがLispで書いてあれば、 コマンド自体の実行には評価が関わってきますが、 そのことは、コマンドキーの解釈自体には含まれていません。

評価は再帰的な処理です。 つまり、フォームの評価では、 evalを呼び出してそのフォームの一部分を評価することもあります。 たとえば、関数呼び出しの評価においては、まず、 関数呼び出しの各引数を評価してから、関数本体の各フォームを評価します。 (car x)の評価を考えてみましょう。 まず最初にxを再帰的に評価する必要があります。 その値を関数carの引数として渡せるようにするのです。

関数呼び出しの評価においては、最終的に指定した関数を呼び出します。 See section 11. 関数。 関数の実行そのものも、関数定義を評価する場合もあります。 あるいは、関数はC言語で実装されたLisp基本関数かもしれませんし、 バイトコード関数かもしれません(see section 15. バイトコンパイル)。

フォームの評価は、環境(environment)と呼ばれる文脈において 行われます。 環境とは、すべてのLisp変数の現在値と束縛です (7)。 フォームが新たな束縛を作らずに変数を参照する場合には、 現在の環境におけるその変数の束縛の値を使います。 See section 10. 変数

フォームを評価すると、変数(see section 10.3 ローカル変数)を束縛して、 再帰的評価のための新たな環境を作ることがあります。 これらの環境は一時的なもので、そのフォームの評価を完了すると 消えてしまいます。 フォームは恒久的な変更を行ってもかまいません。 このような変更を副作用(side effects)と呼びます。 副作用を持つフォームの例は、(setq foo 1)です。

フォームの各種類ごとの評価の意味の詳細は、 以下で説明します(see section 8.2 フォームの種類)。


8.2 フォームの種類

評価することを意図した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.


8.2.1 自己評価型フォーム

自己評価型フォーム(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 #<buffer eval.texi>)
;; それを評価する
(eval print-exp)
     -| #<buffer eval.texi>
     => #<buffer eval.texi>


8.2.2 シンボルフォーム

シンボルを評価するときには、シンボルを変数として扱います。 その結果は、値があれば、変数の値です。 (値セルが空であり)値がなければ、エラーを通知します。 変数の使い方について詳しくは、See section 10. 変数

つぎの例では、setqを使ってシンボルの値を設定します。 そのあとでシンボルを評価すると、setqで保存した値を取り出せます。

 
(setq a 123)
     => 123
(eval 'a)
     => 123
a
     => 123

シンボルniltは特別に扱い、 nilの値はつねにnilであり、 tの値はつねにtです。 これらに別の値を設定したり、別の値を束縛することはできません。 したがって、evalはこれらを他のシンボルと同様に扱いますが、 これら2つのシンボルは自己評価型フォームのようにふるまいます。 `:'で始まる名前のシンボルも同じ意味で自己評価型であり、 同様に、その値を変更できません。 See section 10.2 変更不可能な変数


8.2.3 リストフォームの分類

フォームが空ではないリストならば、その最初の要素に依存して、 関数呼び出し、マクロ呼び出し、スペシャルフォームのいずれかです。 これらの3種類のフォームは、以下に説明するように、異なる方法で評価されます。 リストの残りの要素は、関数、マクロ、スペシャルフォームの 引数(arguments)になります。

空ではないリストを評価する最初の手順は、 その先頭要素を調べることです。 この要素は、それだけで、空ではないリストのフォームの種類を決定し、 リストの残りをどのように処理するかを決定します。 SchemeなどのLispの一部の方言と違って、先頭要素は評価しません


8.2.4 シンボルの関数間接

リストの先頭要素がシンボルであると、 評価処理ではシンボルの関数セルを調べ、 もとのシンボルのかわりにその内容を使います。 その内容が別のシンボルであると、 シンボルの関数間接(symbol function indirection)と呼ばれる この処理をシンボルでないものを得るまで繰り返します。 シンボルの関数セルに格納された関数名としてのシンボルの使い方について 詳しくは、See section 11.3 関数を命名する

この処理の結果、無限ループになる場合もあります。 つまり、シンボルの関数セルが同じシンボルを指している場合です。 あるいは、シンボルの関数セルが空の場合もありえます。 その場合、サブルーティンsymbol-functionは、 エラーvoid-functionを通知します。 いずれの場合でもなければ、最終的にはシンボルでないものを取得し、 それは関数などの適切なオブジェクトであるはずです。

より正確にいえば、Lisp関数(ラムダ式)、バイトコード関数、 基本関数、Lispマクロ、スペシャルフォーム、自動ロードオブジェクトの いずれかを取得しているはずです。 これらの各種類ごとに、以下の1つ1つの節で説明します。 オブジェクトがこれらのいずれの型でもない場合には、 エラーinvalid-functionを通知します。

つぎの例は、シンボルの関数間接の処理を図示したものです。 fsetを使ってシンボルの関数セルに設定し、 symbol-functionを使って関数セルの内容を取り出します (see section 11.8 関数セルの内容の参照)。 具体的には、シンボルcarfirstの関数セルに格納し、 シンボルfirstersteの関数セルに格納します。

 
;; このような関数セルのリンクを作る
;;   -------------       -----        -------        -------
;;  | #<subr car> | <-- | car |  <-- | first |  <-- | erste |
;;   -------------       -----        -------        -------

 
(symbol-function 'car)
     => #<subr car>
(fset 'first 'car)
     => car
(fset 'erste 'first)
     => first
(erste '(1 2 3))   ; ersteが指す関数を呼び出す
     => 1

一方、つぎの例では、シンボルの関数間接を使わずに関数を呼び出します。 というのは、先頭引数はLispの無名関数であって、 シンボルではないからです。

 
((lambda (arg) (erste arg))
 '(1 2 3)) 
     => 1

関数を実行することは、その本体を評価することです。 この過程では、ersteを呼び出すときにシンボルの関数間接が関わります。

組み込み関数indirect-functionは、 明示的にシンボルの関数間接を行う簡単な方法です。

Function: indirect-function function
この関数は、関数としてのfunctionの意味を返す。 functionがシンボルであればfunctionの関数定義を探し、 その値から再度繰り返す。 functionがシンボルでなければfunctionそのものを返す。

Lispでindirect-functionを定義するとつぎのようになる。

 
(defun indirect-function (function)
  (if (symbolp function)
      (indirect-function (symbol-function function))
    function))


8.2.5 関数フォームの評価

評価すべきリストの先頭要素が、Lisp関数オブジェクト、 バイトコードオブジェクト、基本関数オブジェクトの場合、 そのリストは関数呼び出し(function call)です。 たとえば、つぎは、関数+の呼び出しです。

 
(+ 1 x)

関数呼び出しを評価する最初の手順は、 リストの残りの要素を左から右へ順に評価することです。 その結果は実引数の値になり、1つの値がリストの1つの要素に対応します。 つぎの手順は、引数のリストで関数を呼び出すことで、 実質的には、関数apply(see section 11.5 関数呼び出し)を使います。 関数がLispで書いてあれば、関数の引数変数を束縛するために引数を使います (see section 11.2 ラムダ式)。 そして、関数本体のフォーム群を順番に評価し、 本体の最後のフォームの値が関数呼び出しの値になります。


8.2.6 Lispマクロの評価

評価すべきリストの先頭要素がマクロオブジェクトの場合、 そのリストはマクロ呼び出し(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 section 12. マクロ


8.2.7 スペシャルフォーム

スペシャルフォーム(special form)は、 その引数を評価しないように特別な印が付いた基本関数です。 ほとんどのスペシャルフォームは、制御構造を定義したり、 変数を束縛したりします。 これらはどれも関数ではできないことです。

各スペシャルフォームには、どの引数は評価し、 どの引数は評価せずに使うかといったそれぞれに独自の規則があります。 特定の引数を評価するかどうかは、他の引数の評価結果に依存することもあります。

以下に、Emacs Lispのすべてのスペシャルフォームをアルファベット順に、 参照箇所とともにあげておきます。

and
see section 9.3 条件の組み合わせ

catch
see section 9.5.1 明示的な非ローカル脱出: catchthrow

cond
see section 9.2 条件付き実行

condition-case
see section 9.5.3.3 エラーハンドラの書き方

defconst
see section 10.5 グローバル変数を定義する

defmacro
see section 12.4 マクロ定義

defun
see section 11.4 関数を定義する

defvar
see section 10.5 グローバル変数を定義する

function
see section 11.7 無名関数

if
see section 9.2 条件付き実行

interactive
see section 20.3 対話的呼び出し

let
let*
see section 10.3 ローカル変数

or
see section 9.3 条件の組み合わせ

prog1
prog2
progn
see section 9.1 逐次実行

quote
see section 8.3 クォート

save-current-buffer
see section 26.2 カレントバッファ

save-excursion
see section 29.3 エクスカージョン

save-restriction
see section 29.4 ナロイング

save-window-excursion
see section 27.16 ウィンドウ構成

setq
see section 10.8 変数値の変更

setq-default
see section 10.10.2 バッファローカルな束縛の作成と削除

track-mouse
see section 28.13 マウスの追跡

unwind-protect
see section 9.5 非ローカル脱出

while
see section 9.4 繰り返し

with-output-to-temp-buffer
see section 38.7 一時的な表示

Common Lispに関した注意: GNU Emacs LispとCommon Lispのスペシャルフォームを比較してみる。 setqif、および、catchは、Emacs Lispでも Common Lispでもスペシャルフォームである。 defunは、Emacs Lispではスペシャルフォームであるが、 Common Lispではマクロである。 save-excursionは、Emacs Lispではスペシャルフォームであるが、 Common Lispには存在しない。 throwは、Common Lispでは(複数の値を返す必要があるため) スペシャルフォームであるが、 Emacs Lispでは(複数の値はないため)関数である。


8.2.8 自動ロード

自動ロード(autoload)は、 関数やマクロの関数定義をEmacsにまだロードしていなくても、 関数やマクロを呼び出せるようにする機構です。 定義を収めたファイルを指定します。 シンボルの関数定義に自動ロードオブジェクトあるとき、 そのシンボルを関数として呼び出すと、指定したファイルを自動的にロードします。 そうしてから、当該ファイルからロードした実際の定義を呼び出します。 See section 14.4 自動ロード


8.3 クォート

スペシャルフォームquoteは、単一の引数を 評価せずに書かれたとおりに返します。 これは、自己評価型オブジェクトではない 定数シンボルや定数リストをプログラム内に書く手段です。 (数、文字列、ベクトルなどの自己評価型オブジェクトをクォートする必要はない。)

Special Form: quote object
このフォームはobjectを評価せずに返す。

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 section 11.7 無名関数)が あります。 これは、Lispで書いた無名ラムダ式をコンパイルするようにします。 また、``'(see section 12.5 バッククォート)は、 リストの一部分をクォートし、他の部分は計算結果で置き換えるために使います。


8.4 評価(eval)

ほとんどの場合、実行中のプログラムにフォームが現れると フォームは自動的に評価されます。 稀なことですが、実行時に計算したフォームを評価するように コードを書く必要があるかもしれません。 たとえば、編集中のテキストからフォームを読み取ったり、 属性リストからフォームを取り出した場合などです。 このような場合には、関数evalを使います。

本節で説明した関数や変数は、フォームを評価したり、 評価処理に制限を課したり、最後の戻り値を記録したりします。 ファイルをロードしても評価が行われます(see section 14. ロード)。

注意: データ構造の中に関数を格納して それをfuncallapplyで呼び出すほうが、 データ構造の中に式を格納してそれを評価するより、 一般に明確で柔軟性があります。 関数を使うとそれらに引数として情報を渡すことができます。

Function: eval form
この関数は、式を評価する基本的な関数である。 formを現在の環境において評価し、その結果を返す。 評価処理はオブジェクトの型に依存する(see section 8.2 フォームの種類)。

evalは関数なので、evalの呼び出しに現れる 引数の式は2度評価される。 evalを呼び出すまえの準備で1回、 関数eval自身による評価でもう1回である。 例を示す。

 
(setq foo 'bar)
     => bar
(setq bar 'baz)
     => baz
;; evalは引数fooを受け取る
(eval 'foo)
     => bar
;; evalは引数barを受け取る。それはfooの値
(eval foo)
     => baz

evalの呼び出しの深さは、 max-lisp-eval-depth(下記参照)に制限される。

コマンド: eval-region start end &optional stream read-function
この関数は、カレントバッファのstartendで指定した リージョン内のフォーム群を評価する。 リージョンからフォームを読み取り、 それらに対してevalを呼び出すことを リージョンの末尾に達するまで、あるいは、処理されないエラーが通知されるまで 繰り返す。

streamnil以外ならば、 リージョン内の式を評価した結果の値はstreamを使って表示する。 see section 18.4 出力ストリーム

read-functionnil以外にならば、 それは関数である必要があり、 readのかわりに式を1つ1つ読むために使われる。 この関数は、入力用のストリームである1つの引数で呼び出される。 変数load-read-function(see section 14.1 プログラムからのロード方法)を 使ってこの関数を指定することもできるが、 引数read-functionを用いたほうが堅牢である。

eval-regionはつねにnilを返す。

コマンド: eval-current-buffer &optional stream
これはeval-regionと同様だが、バッファ全体に作用する。

Variable: max-lisp-eval-depth
この変数は、 (エラーメッセージ"Lisp nesting exceeds max-lisp-eval-depth"で) エラーを通知までのevalapplyfuncallの呼び出しの 最大の深さを制限する。 この制限、および、これを超えたときのエラーは、 不正に定義された関数によってLispが無限に再帰することを防止する 1つの方法である。

深さ制限は、Lispコードによる明示的な呼び出しに加えて、 Lisp式で書かれた関数の呼び出しや関数呼び出しの引数や関数本体のフォームの 再帰的な評価などの内部的なevalapplyfuncallの 呼び出しも数える。

この変数のデフォルト値は300。 これに100未満の値を設定すると、指定した値に達するとLispは100に設定し直す。 Lispデバッガに入ったとき、 制限に近い場合にはデバッガ自身が実行できることを保証するために値を増やす。

max-specpdl-sizeは、入れ子の深さを制限する別の方法である。 see section 10.3 ローカル変数

Variable: values
この変数の値は、 バッファから式を読み取り、評価し、結果を表示するEmacsの標準コマンドが行った すべての式の戻り値のリストである。 リストの順序は、最新のものが最初にくる。

 
(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


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

This document was generated by Akihiro Sagawa on January, 21 2003 using texi2html