Sharpsign Colon和Gensym之间的差异

Bag*_*ers 7 common-lisp reader-macro

我刚刚阅读了尖锐的冒号读取器宏,听起来它与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}>.
Run Code Online (Sandbox Code Playgroud)

冷却,以便它应该失败.

现在进行宏观测试

(defmacro test (x)
  (let ((blah '#:jim))
    `(let ((,blah ,x))
       (print ,blah))))

CL-USER> (test 10)

10 
10
CL-USER>
Run Code Online (Sandbox Code Playgroud)

甜,所以它可以像gensym一样使用.

对我而言,这看起来比gensym更清晰,结果明显相同.我确定我错过了一个至关重要的细节所以我的问题是,它是什么?

mal*_*per 9

每次扩展宏时,它将使用相同的符号.

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

(eq (foo) (foo)) => t
(eq (bar) (bar)) => nil
Run Code Online (Sandbox Code Playgroud)

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

虽然使用尖锐的结肠不太可能引起问题,但有一些罕见的情况下使用它会导致几乎不可能找到错误.始终使用gensym最好是安全的.

如果你想使用尖锐的结肠,你应该看看defmacro!来自Lambda的宏.


Rai*_*wig 6

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

#:foo 是读者的记号.

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

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

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

  • 从词汇上讲,它可能是同一个变量,也就是反弹.

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

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