Next: Eval During Expansion, Previous: Argument Evaluation, Up: Problems with Macros [Contents][Index]
前のセクションでは、for
の定義を、展開形がマクロ引数を正しい回数評価するように訂正しました:
(defmacro for (var from init to final do &rest body) "Execute a simple for loop: (for i from 1 to 10 do (print i))."
`(let ((,var ,init) (max ,final)) (while (<= ,var max) ,@body (inc ,var))))
for
の新しい定義には、新たな問題があります。この定義は、ユーザーが意識していない、max
という名前のローカル変数を導入しています。これは、以下の例で示すようなトラブルを招きます:
(let ((max 0)) (for x from 0 to 10 do (let ((this (frob x))) (if (< max this) (setq max this)))))
for
のbodyの内部のmax
への参照は、max
のユーサーバインディングの参照を意図したものですが、実際にはfor
により作られたバインディングにアクセスします。
これを修正する方法は、max
のかわりにinternされていない(uninterned)シンボルを使用することです(Creating Symbolsを参照してください)。internされていないシンボルは他のシンボルと同じようにバインドして参照することができますが、for
により作成されるので、わたしたちはすでにユーザーのプログラムに存在するはずがないことを知ることができます。これはinternされていないので、プログラムの後続の部分でそれを配置する方法はありません。これはfor
により配置された場所をのぞき、他の場所で配置されることはありません。以下はこの方法で機能するfor
の定義です:
(defmacro for (var from init to final do &rest body) "Execute a simple for loop: (for i from 1 to 10 do (print i))." (let ((tempvar (make-symbol "max"))) `(let ((,var ,init) (,tempvar ,final)) (while (<= ,var ,tempvar) ,@body (inc ,var)))))
作成されたinternされていないシンボルの名前はmax
で、これを通常のinternされたシンボルmax
のかわりに、式内のその位置に記述します。