8

我刚刚阅读了Sharpsign冒号阅读器宏,听起来它与gensym的效果非常相似

Sharpsign 冒号:“引入了一个不受约束的符号”

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>

Sweet 所以它可以像 gensym 一样使用。

对我来说,这看起来比 gensym 更干净,结果明显相同。我确定我错过了一个重要的细节,所以我的问题是,它是什么?

4

2 回答 2

9

每次扩展宏时,都会使用相同的符号。

(defmacro foo () `(quote #:x))
(defmacro bar () `(quote ,(gensym)))

(eq (foo) (foo)) => t
(eq (bar) (bar)) => nil

Gensym 将在每次评估时创建一个新符号,但尖冒号只会在读取时创建一个新符号。

虽然使用尖冒号不太可能导致问题,但在极少数情况下使用它会导致几乎不可能找到错误。最好始终使用 gensym 以确保安全。

如果你想使用像冒号这样的东西,你应该看看defmacro!来自 Let Over Lambda 的宏。

于 2015-02-02T11:10:38.623 回答
7

GENSYM就像MAKE-SYMBOL。不同之处在于GENSYM通过计数来支持花哨的命名 -> 因此符号种类具有唯一的名称,这使得在宏扩展中使用 gensyms 时调试更容易一些。

#:foo是给读者的符号。

所以你有一个创建这些的函数和一个文字符号。请注意,当*print-circle*为真时,某种身份可能会保留在 s-expressions:#(#1=#:FOO #1#)中。

通常这类似于(a . b)and (cons 'a 'b), #(a b)and (vector 'a 'b)... 一个是文字数据,另一个是创建('cons')新对象的表单。

如果您查看宏,主要问题是它的嵌套使用可能会导致问题。无论是词汇还是动态。

  • 从词法上讲,它可能是同一个变量,即反弹。

  • 动态的,如果是特殊变量也可以反弹

在宏扩展时使用生成的符号将确保不同的扩展代码不会共享绑定。

于 2015-02-02T11:09:41.337 回答