[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
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 フォームの種類).
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
評価することを意図したLispオブジェクトをフォーム(form)と呼びます. Emacsがどのようにフォームを評価するかは, そのデータ型に依存します. Emacsには, 評価方法が異なる3種類のフォームがあります. シンボル, リスト, および, 『その他すべての型』です. 本節では, 3種類すべてについて1つ1つ説明します. まず, 自己評価型フォームである『その他すべての型』から説明します.
8.1.1 自己評価型フォーム | Forms that evaluate to themselves. | |
8.1.2 シンボルフォーム | Symbols evaluate as variables. | |
8.1.3 リストフォームの分類 | How to distinguish various sorts of list forms. | |
8.1.4 シンボルの関数間接 | When a symbol appears as the car of a list, we find the real function via the symbol. | |
8.1.5 関数フォームの評価 | Forms that call functions. | |
8.1.6 Lispマクロの評価 | Forms that call macros. | |
8.1.7 スペシャルフォーム | “Special forms” are idiosyncratic primitives, most of them extremely important. | |
8.1.8 自動ロード | Functions set up to load files containing their real definitions. |
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
自己評価型フォーム(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>
|
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
シンボルを評価するときには, シンボルを変数として扱います. その結果は, 値があれば, 変数の値です. (値セルが空であり)値がなければ, エラーを通知します. 変数の使い方について詳しくは, See section 変数.
つぎの例では, setq
を使ってシンボルの値を設定します.
そのあとでシンボルを評価すると, setq
で保存した値を取り出せます.
(setq a 123) ⇒ 123 (eval 'a) ⇒ 123 a ⇒ 123 |
シンボルnil
とt
は特別に扱い,
nil
の値はつねにnil
であり,
t
の値はつねにt
です.
これらに別の値を設定したり, 別の値を束縛することはできません.
したがって, eval
はこれらを他のシンボルと同様に扱いますが,
これら2つのシンボルは自己評価型フォームのようにふるまいます.
‘:’で始まる名前のシンボルも同じ意味で自己評価型であり,
同様に, その値を変更できません.
See section 変更不可能な変数.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
フォームが空ではないリストならば, その最初の要素に依存して, 関数呼び出し, マクロ呼び出し, スペシャルフォームのいずれかです. これらの3種類のフォームは, 以下に説明するように, 異なる方法で評価されます. リストの残りの要素は, 関数, マクロ, スペシャルフォームの 引数(arguments)になります.
空ではないリストを評価する最初の手順は, その先頭要素を調べることです. この要素は, それだけで, 空ではないリストのフォームの種類を決定し, リストの残りをどのように処理するかを決定します. SchemeなどのLispの一部の方言と違って, 先頭要素は評価しません.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
リストの先頭要素がシンボルであると, 評価処理ではシンボルの関数セルを調べ, もとのシンボルのかわりにその内容を使います. その内容が別のシンボルであると, シンボルの関数間接(symbol function indirection)と呼ばれる この処理をシンボルでないものを得るまで繰り返します. シンボルの関数セルに格納された関数名としてのシンボルの使い方について 詳しくは, See section 関数を命名する.
この処理の結果, 無限ループになる場合もあります.
つまり, シンボルの関数セルが同じシンボルを指している場合です.
あるいは, シンボルの関数セルが空の場合もありえます.
その場合, サブルーティンsymbol-function
は,
エラーvoid-function
を通知します.
いずれの場合でもなければ, 最終的にはシンボルでないものを取得し,
それは関数などの適切なオブジェクトであるはずです.
より正確にいえば, Lisp関数(ラムダ式), バイトコード関数,
基本関数, Lispマクロ, スペシャルフォーム, 自動ロードオブジェクトの
いずれかを取得しているはずです.
これらの各種類ごとに, 以下の1つ1つの節で説明します.
オブジェクトがこれらのいずれの型でもない場合には,
エラーinvalid-function
を通知します.
つぎの例は, シンボルの関数間接の処理を図示したものです.
fset
を使ってシンボルの関数セルに設定し,
symbol-function
を使って関数セルの内容を取り出します
(see section 関数セルの内容の参照).
具体的には, シンボルcar
をfirst
の関数セルに格納し,
シンボルfirst
をerste
の関数セルに格納します.
;; このような関数セルのリンクを作る
;; ------------- ----- ------- -------
;; | #<subr car> | <-- | car | <-- | first | <-- | erste |
;; ------------- ----- ------- -------
|
(symbol-function 'car) ⇒ #<subr car> (fset 'first 'car) ⇒ car (fset 'erste 'first) ⇒ first (erste '(1 2 3)) ; |
一方, つぎの例では, シンボルの関数間接を使わずに関数を呼び出します. というのは, 先頭引数はLispの無名関数であって, シンボルではないからです.
((lambda (arg) (erste arg)) '(1 2 3)) ⇒ 1 |
関数を実行することは, その本体を評価することです.
この過程では, erste
を呼び出すときにシンボルの関数間接が関わります.
組み込み関数indirect-function
は,
明示的にシンボルの関数間接を行う簡単な方法です.
この関数は, 関数としてのfunctionの意味を返す. functionがシンボルであればfunctionの関数定義を探し, その値から再度繰り返す. functionがシンボルでなければfunctionそのものを返す.
Lispでindirect-function
を定義するとつぎのようになる.
(defun indirect-function (function) (if (symbolp function) (indirect-function (symbol-function function)) function)) |
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
評価すべきリストの先頭要素が, Lisp関数オブジェクト,
バイトコードオブジェクト, 基本関数オブジェクトの場合,
そのリストは関数呼び出し(function call)です.
たとえば, つぎは, 関数+
の呼び出しです.
(+ 1 x) |
関数呼び出しを評価する最初の手順は,
リストの残りの要素を左から右へ順に評価することです.
その結果は実引数の値になり, 1つの値がリストの1つの要素に対応します.
つぎの手順は, 引数のリストで関数を呼び出すことで,
実質的には, 関数apply
(see section 関数呼び出し)を使います.
関数がLispで書いてあれば, 関数の引数変数を束縛するために引数を使います
(see section ラムダ式).
そして, 関数本体のフォーム群を順番に評価し,
本体の最後のフォームの値が関数呼び出しの値になります.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
評価すべきリストの先頭要素がマクロオブジェクトの場合, そのリストはマクロ呼び出し(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 マクロ.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
スペシャルフォーム(special form)は, その引数を評価しないように特別な印が付いた基本関数です. ほとんどのスペシャルフォームは, 制御構造を定義したり, 変数を束縛したりします. これらはどれも関数ではできないことです.
各スペシャルフォームには, どの引数は評価し, どの引数は評価せずに使うかといったそれぞれに独自の規則があります. 特定の引数を評価するかどうかは, 他の引数の評価結果に依存することもあります.
以下に, Emacs Lispのすべてのスペシャルフォームをアルファベット順に, 参照箇所とともにあげておきます.
and
see section 条件の組み合わせ
catch
see section 明示的な非ローカル脱出:
catch
とthrow
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では(複数の値はないため)関数である.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
自動ロード(autoload)は, 関数やマクロの関数定義をEmacsにまだロードしていなくても, 関数やマクロを呼び出せるようにする機構です. 定義を収めたファイルを指定します. シンボルの関数定義に自動ロードオブジェクトあるとき, そのシンボルを関数として呼び出すと, 指定したファイルを自動的にロードします. そうしてから, 当該ファイルからロードした実際の定義を呼び出します. See section 自動ロード.
[ < ] | [ > ] | [ << ] | [上] | [ >> ] | [冒頭] | [目次] | [見出し] | [ ? ] |
スペシャルフォームquote
は, 単一の引数を
評価せずに書かれたとおりに返します.
これは, 自己評価型オブジェクトではない
定数シンボルや定数リストをプログラム内に書く手段です.
(数, 文字列, ベクトルなどの自己評価型オブジェクトをクォートする必要はない. )
このフォームは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を用いて生成されました。