Next: , Previous: , Up: Problems with Macros   [Contents][Index]


13.5.3 Local Variables in Macro Expansions

前のセクションでは、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のかわりに、式内のその位置に記述します。