ここまでの説明を、例を使って復習してみます。関数addが(define (add a b) (+ a b))のように定義されています。ここで(add 2 x)を計算します。シンボル「x」の値は3になっています。
eval、apply、eval_listの実行経過(呼び出しと戻り値)は次のようになります。
> eval((add 2 x), (x.3)) > apply(add, (2 x), (x.3)) > eval(add) = (lambda (a b) (+ a b)) -- add関数の定義 > eval_list((2 x), (x.3)) > eval(2, (x.3)) = 2 -- 2の値は2 > eval(x, (x.3)) = 3 -- 3の値は3 = (2 3) -- eval_list((2 x)...)の値 > eval((+ a b), ((a.2)(b.3)(x.3))) > apply(+, (a b), ((a.2)(b.3)(x.3))) > eval_list((a b), ((a.2)(b.3)(x.3))) > eval(a, ((a.2)(b.3)(x.3))) = 2 -- 変数aの値 > eval(b, ((a.2)(b.3)(x.3))) = 3 -- 変数bの値 = (2 3) -- eval_list((a b)...)の値 - (+ 2 3) -- apply内部でCで書かれた+関数が呼び出される = 5 -- apply(+, (a b)...)の値 = 5 -- eval((+ a b)...)の値 = 5 -- apply(add...)の値 = 5 -- eval((add 2 x)...)の値
このように途中経過を手を動かして書いてみると、処理の流れや関数の役割分担が理解しやすいと思います。
最後に、スペシャルフォームとマクロの説明をします。
・スペシャルフォーム
スペシャルフォームは、apply内部で引数を評価せず、そのままの引数がC言語で書かれた関数に渡されます。ifは次のようになります。
cell if(cell e, cell env) { if (eval(CAR(e), env) != NIL) { // 条件式を評価 return eval(CAR(CDR(e)), env) // 真なら2番目の引数を評価 } else { return eval(CAR(CDR(CDR(e))), env) // 偽なら3番目の引数を評価 } }
・マクロ
マクロはLispの強力な機能の1つです。マクロの動作は、
となります。
PerlやRubyにあるunless文のような、条件が成り立っていなければ、次の式を実行するマクロを定義してみます。
lisp> (defmacro unless(cond exp) (list (quote if) cond nil exp)) unless lisp> (unless (= 1 2) 111) 111 lisp> (unless (= 1 1) 111) nil
apply関数の中でマクロは次のようになっています。Lambda式の評価との違いは、引数を評価せず(eval_listを呼び出さず)マクロ定義に渡すことと、マクロ定義の戻り値を再度評価(eval)する点です。
cell apply(cell func, cell args, cell env) { cell fbody = eval(func, env); cell ftype = CAR(fbody); if (ftype == macro_sym) { cell params = CAR(CDR(fbody)); cell e = CAR(CDR(CDR(fbody))); cell r = eval(e, concat2(pairlis(params,args)), env)); return eval(r, env) } }
先ほどのunlessの例の実行経過を見てみましょう。
> eval((unless (= 1 2) 111), ()) > apply(unless, ((= 1 2) 111), ()) > eval(unless, ()) = (macro (cond exp) (list (quote if) cond nil exp))) > eval((list (quote if) cond nil exp), ((cond.(= 1 2)) (exp.111))) - 一部省略 - (list if (= 1 2) nil 111) が評価され = (if (= 1 2) nil 111) > eval((if (= 1 2) nil 111), ()) - 一部省略 - (if nil nil 111), ()) が評価され = 111 = 111 > eval((unless (= 1 1) 111), ()) > apply(unless, ((= 1 2) 111), ()) > eval(unless, ()) = (macro (cond exp) (list (quote if) cond nil exp))) > eval((list (quote if) cond nil exp), ((cond.(= 1 1)) (exp.111))) - 一部省略 - (list if (= 1 1) nil 111) が評価され = (if (= 1 1) nil 111) > eval((if (= 1 1) nil 111), ()) - 一部省略 - (if t nil 111), ()) が評価され = nil = nil = nil
今回は、C言語で書かれたLispの処理系を説明することで、Lispの動作を説明してみました。Lispの処理系が非常にシンプルであることが分かっていただけたらうれしいです。
今回説明したLispのコードはsvn coで取得できます。また、ソースを見るだけであればこちらのページにアクセスしてみてください。
Copyright © ITmedia, Inc. All Rights Reserved.