在Clojure中,您需要gensym使用在宏中创建内部使用的符号以保持其卫生.但是,有时您需要在嵌套语法引号中使用相同的符号.例如,如果我想将一个值绑定到符号let并在展开的循环中打印三次,我会这样做
`(let [x# 1]
~@(repeat 3
`(println x#)))
Run Code Online (Sandbox Code Playgroud)
但那会产生
(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__))
Run Code Online (Sandbox Code Playgroud)
x#在let表单中生成一个不同于println嵌套在其中的表单中的符号- 因为它们是从不同的语法引号创建的.
为了解决这个问题,我可以预先生成符号并将其注入语法引号:
(let [x (gensym)]
`(let [~x 1]
~@(repeat 3
`(println ~x)))
)
Run Code Online (Sandbox Code Playgroud)
这将产生正确的结果,在任何地方需要相同的符号:
(clojure.core/let [G__7 1]
(clojure.core/println G__7)
(clojure.core/println G__7)
(clojure.core/println G__7))
Run Code Online (Sandbox Code Playgroud)
现在,虽然它确实产生了正确的结果,但代码本身看起来很丑陋且冗长.我不喜欢"声明"一个符号,注入语法使它看起来像是来自宏外部,或者在其中的某个地方计算.我希望能够使用auto-gensym语法,这清楚地表明这些是宏内部符号.
那么,有没有办法使用嵌套语法引号的auto-gensym并使它们产生相同的符号?
Art*_*ldt 11
自动gensym'd符号仅在定义它们的语法引用中有效,并且它们不在未加引号的代码中工作,因为它不是语法引用的一部分.
这里的符号x#被它的gensym取代,因为它在语法引用的范围内:
core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)
Run Code Online (Sandbox Code Playgroud)
如果你取消引用它,它就不再被翻译成它的语法引用:
core> `(let [x# 1] ~@x#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1)
Run Code Online (Sandbox Code Playgroud)
自动gensyms是语法引用中非常方便的快捷方式,在您显然需要gensym直接使用的任何其他地方,就像您的后续示例一样.
还有其他方法来构造这个宏,所以autogensyms可以工作,虽然在Clojure和其他lisps中声明宏顶部的let中的gensymed符号是非常正常的.
你的方法(调用gensym)是正确的.
但是在某些情况下,你可以巧妙地使用doto,->或者->>.看到:
`(let [x# 1]
(doto x#
~@(repeat 3 `println)))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1509 次 |
| 最近记录: |