14.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 宏体内部对 max 的引用,本应指向用户定义的 max 绑定,实际却访问到了 for 宏创建的 max 绑定。

修正该问题的方法是使用未注册符号(uninterned symbol) 替代 max(see 创建与编入符号)。未注册符号的绑定和引用方式与普通符号完全一致,但由于它是由 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


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike