マクロを書くときは、評価の順番に気をつけよう (Common-Lisp)
「On Lisp」の読書メモ。
(1) forマクロ — 評価の順番が不適切
以下のような forマクロがこの本の p138 に載っている。
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,gstop ,stop)
(,var ,start (1+ ,var)))
((> ,var ,gstop))
,@body)))
このマクロについて、以下のようなコードを書いてみる。
(for (i 1 13)
(princ i))
12345678910111213
NIL
これは期待した動きである。
しかし、以下のようにすると、どうか。
(let ((x 1))
(for (i x (setq x 13))
(princ i)))
forマクロに値ではなく変数を与えるのである。
結果は、以下。
13
NIL
なぜこうなるのか。
do以下の処理を見てみると、まず、以下の行が処理されるとき、
ここで、stopに渡された式 (setq x 13) が評価されて、
シンボルgstop に束縛される。
`(do ((,gstop ,stop)
つまり、この時点で x は 13 になってしまっている。
そのあと、次の式が評価されるのだが、
(,var ,start (1+ ,var)))
start には x が渡されているから、xが評価されて 13 になってしまっている。
(i 13 (1+ i))
という式になってしまっている。
この状態で次の終了条件が評価される。
((> ,var ,gstop))
これは、以下の式になっている。
(> i 13)
i が13よりも大きくなったら終了である。
つまり、for文の中のくりかえしは 1回しか実行されない。
その結果、
13
NIL
と出力されるのである。
(2) 適切な forマクロ
forマクロは、以下のように書かれなければならない。
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop)) ; stopはここで評価されなければならない
((> ,var ,gstop))
,@body)))
今回の場合も、マクロに渡されるのは値だけではなく、変数の場合もあるということを忘れてはならない、ということかな。
参考
『On Lisp』 p.139
Paul Graham 著 / 野田 開 訳 / H19.3.23 第1版第1刷 オーム社
カテゴリー: Lisp, memo
タグ: common-lisp, forマクロ, for文, 評価の順番
カウント: 74