在Clojure中用嵌套语法引用协调自动gensym

时间:2012-10-01 15:40:13

标签: clojure gensym

在Clojure中,您需要使用gensym创建符号供内部使用,以保证它们的卫生。但是,有时您需要在嵌套语法引号中使用相同的符号。例如,如果我想将值绑定到带有let的符号并在展开的循环中打印三次,我会这样做

`(let [x# 1]
   ~@(repeat 3
             `(println x#)))

但那会产生

(clojure.core/let [x__2__auto__ 1]
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__))

x#生成let形式的不同符号,而不是嵌套在其中的println形式 - 因为它们是使用不同的语法引号创建的。

要解决这个问题,我可以预先生成符号并将其注入语法引号:

(let [x (gensym)]
  `(let [~x 1]
     ~@(repeat 3
               `(println ~x)))
) 

这将产生正确的结果,在任何地方都需要相同的符号:

(clojure.core/let [G__7 1]
                  (clojure.core/println G__7)
                  (clojure.core/println G__7)
                  (clojure.core/println G__7))

现在,虽然它确实产生了正确的结果,但代码本身看起来很丑陋且冗长。我不喜欢“声明”一个符号,注入语法使它看起来像是来自宏之外,或者在其中的某个地方计算。我希望能够使用auto-gensym语法,这清楚地表明这些是宏内部符号。

那么,有没有办法使用带有嵌套语法引号的auto-gensym并使它们生成相同的符号?

3 个答案:

答案 0 :(得分:11)

自动gensym'd符号仅在定义它们的语法引用中有效,并且它们不适用于不带引号的代码,因为它不是语法引用的一部分。

这里符号x#被它的gensym取代,因为它在语法引用的范围内:

core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)

如果您取消引用它,则不再将其翻译成语法引用:

core> `(let [x# 1] ~@x#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1) 

自动gensyms是语法引用中非常方便的快捷方式,在您显然需要直接使用gensym的任何其他位置,就像您后面的示例一样。

还有其他方法来构造这个宏,所以autogensyms可以工作,虽然在Clojure和其他lisps中声明宏顶部的let中的gensymed符号是非常正常的。

答案 1 :(得分:8)

你的方法(调用gensym)是正确的。

但在某些情况下,您可以巧妙地使用doto->->>。参见:

 `(let [x# 1]
   (doto x#
     ~@(repeat 3 `println)))

答案 2 :(得分:0)

更一般地说,您可以在遇到这种情况时执行以下操作:

(let [x `x#]
   `(let [~x 1]
      ~@(repeat 3
                `(println ~x))))

明确地说,您创建自动生成符号并将其绑定到语法引用形式之外,然后在任何需要它的嵌套形式中使用语法取消引用。