GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Surprising-Local-Vars.html ・ emacs-doc.conao3.com

14.5.3 マクロ展開におけるローカル変数

前節では、展開によってマクロ引数が適切な回数だけ評価されるよう、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 の代わりに展開に組み込まれます。