我刚刚阅读了尖锐的冒号读取器宏,听起来它与gensym的效果非常相似
Sharpsign Colon:“介绍一个未加工的符号”
Gensym:“创造并返回一个新的,没有内嵌的符号”
这是一个简单的测试
CL-USER> #:dave
; Evaluation aborted on #<UNBOUND-VARIABLE DAVE {1002FF77D3}>.
CL-USER> (defparameter #:dave 1)
#:DAVE
CL-USER> #:dave
; Evaluation aborted on #<UNBOUND-VARIABLE DAVE {100324B493}>.
冷却,因为它应该失败。
现在进行宏测试
(defmacro test (x)
(let ((blah '#:jim))
`(let ((,blah ,x))
(print ,blah))))
CL-USER> (test 10)
10
10
CL-USER>
很好,所以它可以像gensym一样使用。
对我而言,这看起来比gensym更清晰,结果明显相同。我确定我错过了一个至关重要的细节,所以我的问题是,它是什么?
答案 0 :(得分:8)
每次扩展宏时,它都将使用相同的符号。
(defmacro foo () `(quote #:x))
(defmacro bar () `(quote ,(gensym)))
(eq (foo) (foo)) => t
(eq (bar) (bar)) => nil
Gensym每次评估时都会创建一个新符号,但尖锐冒号只会在读取时创建一个新符号。
虽然使用尖锐的结肠不太可能引起问题,但在极少数情况下使用它会导致几乎不可能发现错误。始终使用gensym最好是安全的。
如果你想使用尖锐的冒号,你应该看看Let Over Lambda的defmacro!宏。
答案 1 :(得分:6)
GENSYM
与MAKE-SYMBOL
类似。区别在于GENSYM
通过向上计数支持花式命名 - &gt;因此符号具有唯一的名称,这使得在宏扩展中具有gensyms时调试更容易。
#:foo
是读者的符号。
所以你有一个创建这些和文字符号的函数。请注意,当*print-circle*
为真时,某些类型的标识可能会保留在s表达式中:#(#1=#:FOO #1#)
。
通常这类似于(a . b)
和(cons 'a 'b)
,#(a b)
和(vector 'a 'b)
...一个是文字数据,另一个是将创建的形式(& #39;缺点对象。
如果查看宏,主要问题是嵌套使用它可能会导致问题。无论是词法还是动态。
词汇上它可能是同一个变量,也就是反弹。
动态地,如果是特殊变量,它也可以反弹
在宏扩展时使用生成的符号将确保不同的和扩展的代码不会共享绑定。