前節では、展開によってマクロ引数が適切な回数だけ評価されるよう、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のボディ内にあるmaxへの参照は、本来ユーザーによるmaxのバインドを指すはずですが、実際にはforによるバインドにアクセスしています。
これを修正するには、max の代わりにアンインターニングされたシンボルを使用します(シンボルの作成とインターニングを参照)。アンインターニングされたシンボルは、他のシンボルと同様にバインドや参照が可能ですが、for
によって作成されるため、ユーザーのプログラム内に既に存在することはないと分かります。インターニングされていないため、ユーザーが後からこれをプログラムに組み込む方法はありません。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)))))
これにより、max
という名前のアンインターニングされたシンボルが生成され、通常の式に現れる一般的なインターニング済みシンボル max の代わりに展開に組み込まれます。