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


13.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の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のかわりに、式内のその位置に記述します。