[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8. 評価

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

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

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

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

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

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

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

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

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1 フォームの種類

評価することを意図したLispオブジェクトをフォーム(form)と呼びます. Emacsがどのようにフォームを評価するかは, そのデータ型に依存します. Emacsには, 評価方法が異なる3種類のフォームがあります. シンボル, リスト, および, 『その他すべての型』です. 本節では, 3種類すべてについて1つ1つ説明します. まず, 自己評価型フォームである『その他すべての型』から説明します.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.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.1.2 シンボルフォーム

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

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

 
(setq a 123)
     ⇒ 123
(eval 'a)
     ⇒ 123
a
     ⇒ 123

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.3 リストフォームの分類

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

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.4 シンボルの関数間接

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

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

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

つぎの例は, シンボルの関数間接の処理を図示したものです. fsetを使ってシンボルの関数セルに設定し, symbol-functionを使って関数セルの内容を取り出します (see section 関数セルの内容の参照). 具体的には, シンボル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.1.5 関数フォームの評価

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

 
(+ 1 x)

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.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 マクロ.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.7 スペシャルフォーム

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

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

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

and

see section 条件の組み合わせ

catch

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

cond

see section 条件付き実行

condition-case

see section エラーハンドラの書き方

defconst

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

defmacro

see section マクロ定義

defun

see section 関数を定義する

defvar

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

function

see section 無名関数

if

see section 条件付き実行

interactive

see section 対話的呼び出し

let
let*

see section ローカル変数

or

see section 条件の組み合わせ

prog1
prog2
progn

see section 逐次実行

quote

see section クォート

save-current-buffer

see section カレントバッファ

save-excursion

see section エクスカージョン

save-restriction

see section ナロイング

save-window-excursion

see section ウィンドウ構成

setq

see section 変数値の変更

setq-default

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

track-mouse

see section マウスの追跡

unwind-protect

see section 非ローカル脱出

while

see section 繰り返し

with-output-to-temp-buffer

see section 一時的な表示

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では(複数の値はないため)関数である.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.8 自動ロード

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


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2 クォート

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



[ << ] [ >> ]           [冒頭] [目次] [見出し] [ ? ]

この文書は新堂 安孝によって2009年9月22日texi2html 1.82を用いて生成されました。