前のセクションでは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)シンボルを使用することです(シンボルの作成とinternを参照)。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
のかわりに、式内のその位置に記述します。