在上一节中,我们对 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。