我刚刚阅读了锐号冒号阅读器宏,听起来它与gensym的效果非常相似
尖符号冒号:"引入了一个未被拘禁的符号"
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 更干净,结果明显相同。我确定我错过了一个重要的细节,所以我的问题是,它是什么?
每次展开宏时,它都会使用相同的符号。
(defmacro foo () `(quote #:x))
(defmacro bar () `(quote ,(gensym)))
(eq (foo) (foo)) => t
(eq (bar) (bar)) => nil
Gensym 每次被评估时都会创建一个新符号,但尖冒号只会在读取时创建一个新符号。
虽然使用尖锐的冒号不太可能引起问题,但在极少数情况下,使用它会导致几乎不可能找到错误。最好从始终使用 gensym 开始安全。
如果你想使用像尖冒号这样的东西,你应该看看 Let Over Lambda 的 defmacro! macro。
GENSYM
就像MAKE-SYMBOL
。不同之处在于,GENSYM
通过向上计数来支持花哨的命名 ->因此符号具有唯一的名称,这使得在具有 gensyms 时调试更容易一些,例如在宏扩展中。
#:foo
是读者的符号。
所以你有一个创建这些的函数和一个文字符号。请注意,当*print-circle*
为真时,某种身份可能会保留在 s 表达式中:#(#1=#:FOO #1#)
。
一般来说,这类似于(a . b)
和(cons 'a 'b)
,#(a b)
和(vector 'a 'b)
...一个是文字数据,另一个是将创建("cons")新对象的形式。
如果您查看宏,主要问题是它的嵌套使用可能会导致问题。无论是词汇还是动态。
-
从词法上讲,它可以是同一个变量,即反弹。
-
动态地,如果它是一个特殊变量,它也可以反弹
在宏扩展时使用生成的符号将确保不同的和扩展的代码不会共享绑定。