用于防止宏中名称冲突的唯一符号
宏的扩展通常需要使用未被用户作为参数传递的符号(例如,作为局部变量的名称)。必须确保这些符号不会与用户在周围代码中使用的符号冲突。
这通常通过使用 GENSYM
来实现, GENSYM
是一个返回一个新的未加工符号的函数。
坏
考虑下面的宏。它创建了一个 DOTIMES
循环,它还将正文结果收集到一个列表中,最后返回。
(defmacro dotimes+collect ((var count) &body body)
`(let ((result (list)))
(dotimes (,var ,count (nreverse result))
(push (progn ,@body) result))))
(dotimes+collect (i 5)
(format t "~a~%" i)
(* i i))
; 0
; 1
; 2
; 3
; 4
;=> (0 1 4 9 16)
这似乎适用于这种情况,但如果用户碰巧有一个变量名称 RESULT
,它们在正文中使用,结果可能不是用户期望的结果。考虑这种尝试编写一个函数来收集所有整数的总和,直到 N
:
(defun sums-upto (n)
(let ((result 0))
(dotimes+collect (i n)
(incf result i))))
(sums-upto 10) ;=> Error!
好
要解决这个问题,我们需要使用 GENSYM
为宏扩展中的 RESULT
变量生成一个唯一的名称。
(defmacro dotimes+collect ((var count) &body body)
(let ((result-symbol (gensym "RESULT")))
`(let ((,result-symbol (list)))
(dotimes (,var ,count (nreverse ,result-symbol))
(push (progn ,@body) ,result-symbol)))))
(sums-upto 10) ;=> (0 1 3 6 10 15 21 28 36 45)