用於防止巨集中名稱衝突的唯一符號

巨集的擴充套件通常需要使用未被使用者作為引數傳遞的符號(例如,作為區域性變數的名稱)。必須確保這些符號不會與使用者在周圍程式碼中使用的符號衝突。

這通常通過使用 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:避免不同包裝中的符號出現問題