ちょっと変わったLisp入門:Gaucheでメタプログラミング(1)(5/5 ページ)
Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう(編集部)
eval、apply、eval_listの実行経過
ここまでの説明を、例を使って復習してみます。関数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.
関連記事
- いまさらアルゴリズムを学ぶ意味
コーディングに役立つ! アルゴリズムの基本(1) コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう - Zope 3の魅力に迫る
Zope 3とは何ぞや?(1) Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか? - 貧弱環境プログラミングのススメ
柴田 淳のコーディング天国 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く? - Haskellプログラミングの楽しみ方
のんびりHaskell(1) 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう - ちょっと変わったLisp入門
Gaucheでメタプログラミング(1) Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう