用于防止宏中名称冲突的唯一符号

宏的扩展通常需要使用未被用户作为参数传递的符号(例如,作为局部变量的名称)。必须确保这些符号不会与用户在周围代码中使用的符号冲突。

这通常通过使用 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)

TODO:如何从字符串中创建符号

TODO:避免不同包装中的符号出现问题