Sharpsign Colon 和 Gensym 之间的区别

Differences between Sharpsign Colon and Gensym

我刚刚阅读了 sharpsign 冒号 reader 宏,听起来它与 gensym

的效果非常相似

Sharpsign Colon: "introduces an uninterned symbol"

Gensym: "Creates and returns a fresh, uninterned symbol"

这么简单的测试

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 更干净,结果明显相同。我确信我遗漏了一个重要的细节,所以我的问题是,它是什么?

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

#:foo 是 reader.

的符号

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

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

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

  • 词法上可以是同一个变量,就是rebound

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

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

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

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

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

Gensym 每次求值时都会创建一个新符号,但是尖冒号只会在读取时创建一个新符号。

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

如果您想使用尖冒号之类的东西,您应该查看 Let Over Lambda 中的 defmacro! 宏。