用於防止巨集中名稱衝突的唯一符號
巨集的擴充套件通常需要使用未被使用者作為引數傳遞的符號(例如,作為區域性變數的名稱)。必須確保這些符號不會與使用者在周圍程式碼中使用的符號衝突。
這通常通過使用 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)