Next: 15 制御文マクロ
Up: ソフトウェア第三 講義資料 Lisp, リスト処理,集合
Previous: 13.4 setfマクロ
if式 は,スペシャルフォームを作ります.ifの最初の引数の評価結果によっ
て,第二引数,第三引数の評価がなされたりなされなかったりするわけですが,
関数として if を定義すると,第二,第三引数を必ず評価してしまうことになっ
て,評価してほしくない場合にも評価されてしまうということになってしまい
ます.
<cl> (if (car '(nil t)) (print 1) (print 2))
2
2
<cl>
たとえば,上の場合,条件が nil なので,第二引数の (print 1)は評価され
ずに,(print 2)だけが評価されます.最初の2が printによる2で,後の2
が(print 2)の結果値の2です.
この if文を 関数で定義してみたとします.
<cl> (defun if2 (cond action1 action2)
(if cond action1 action2))
IF2
<cl> (if2 (car '(nil t)) (print 1) (print 2))
1
2
2
という具合になります.関数定義なので,引数の (print 1)も評価されて関数
定義本体の action1 にその値が渡されます.condの値は,nil なので,
action2 の値がif2の値になります.
つまり,画面に表示された最初の1は第二引数の(print 1)が評価された時の
出力で,次の2はif2の第三引数(print 2)が評価された時の出力です.
最後の2が(print 2)の評価後の値がaction2に入り,その値がif2式の評価値
として表示されたものです.
次に,マクロで定義をしてみます.
<cl> (defmacro if2 (cond action1 action2)
(list 'if cond action1 action2))
IF2
<cl> (if2 (car '(nil t)) (print 1) (print 2))
2
2
<cl> (if (car '(t nil)) (print 1) (print 2))
1
1
<cl> (if2 (car '(t nil)) (print 1) (print 2))
1
1
<cl>
このようにマクロの場合には,関数とは違って,3つの引数に渡されるものを
評価せずに,そのまま,それぞれ cond, action1, action2 へ渡します.そこ
で,if2の定義である (list 'if cond action1 action2) にそれが渡されたと
ころで,cond, action1, action2 がそれぞれ評価されると,それぞれ,(car '
(t nil)), (print 1), (print 2) になり,定義本体が,list式なので,(if
(car '(t nil)) (print 1) (print 2)) という形になります.引数を置き換え
るために この list式が一度評価されたことになります.これがいわゆる引数
をマクロ定義の中で展開するということです.マクロ展開と呼ばれます.マク
ロ式の評価は,この展開された形をもう一度評価して結果を返すということを
EVALが行なうわけです.
<cl> (macroexpand
'(if2 (car '(nil t)) (print 1) (print 2)))
(IF (CAR '(NIL T)) (PRINT 1) (PRINT 2))
T
<cl>
つまり,EVALは,evalのフォームがマクロフォームであれば,それを
macroexpandしてEVALするということを行ないます.今の場合,if2の式を
マクロ展開してifの式になり,これがスペシャルフォームになるので,
EVALはifの処理を行ないます.このように,EVAL自体も再帰的に定義されてい
ます.
制御文である,condは実は,組み込みマクロです.
スペシャルフォームのように個別にその処理を書き下
して特別扱いする必要のあるものを極力少なくすれば,コンパイラ
などの処理系の作成を簡単化できるという意味があります.
つまり,マクロはすべてマクロ展開を行なった後評価を行なうという統一的な
構造を持っているため,Lispのコンパイラなどを作る時は,マクロをコンパイ
ル方法だけ記述してあればよく,それだけで,すべてのマクロをコンパイルす
ることができるからです.
generated through LaTeX2HTML. M.Inaba 平成18年5月7日