我在REPL中发生了以下情况:
mathematics.core> (let [zebra 1] (resolve 'zebra))
nil
mathematics.core> (def zebra 1)
#'mathematics.core/zebra
mathematics.core> (let [zebra 2] (when (resolve 'zebra) (eval 'zebra)))
1
基本上,我想使用像let
形式之类的东西动态地将值绑定到变量,并且该形式内的函数能够访问变量绑定的值。
mathematics.core> (def ^:dynamic zebra 1)
#'mathematics.core/zebra
mathematics.core> (binding [zebra 2] (when (resolve 'zebra) (eval 'zebra)))
2
binding
似乎可以实现我想要的技巧,但AFAIK需要首先使用:dynamic
元数据定义变量。我希望能够使用之前从未定义过的变量,并且表单中的表达式能够访问该变量,就像它实际定义一样。
为了说明,我想要这样的事情:
mathematics.core> (let-dynamic [undefined-variable 1]
(when (resolve 'undefined-variable) (eval 'unresolved-variable)))
1
有一种简单的方法吗?或者使用宏来实现这一目标?
答案 0 :(得分:4)
这不会特别好用。如果未定义符号,则Clojure编译器无法编译使用它的任何代码。你可能会遇到某种破解工作的宏在需要的时候懒得调用def,但这会是一些非常讨厌的代码......
我建议只使用绑定,并提前定义你的变量。您应该能够以这种方式编写代码。
我认为“动态”定义变量是个坏主意。我认为你不应该真的需要这个 - 如果你在代码中使用变量,那么事先为你使用的每个变量做一个(def ^:dynamic ...)
就好了吗?
答案 1 :(得分:3)
我希望能够使用以前从未定义过的变量 在运行中,并在表单中有表达能够访问 变量好像它实际上是定义的。
这对我而言并不像是对clojure的vars或let-bound值的良好匹配,如果你正在生成和评估整个表单无论如何,为什么不呢?使用简单的地图来存储符号 - >使用映射查找值映射并替换整个resolve / eval方案?这样你就可以动态生成任意符号而不会有任何模糊的命名空间技巧,这可能会以一些难以找到的方式破坏你的代码:
(let [my-resolve {'zebra 1}]
(println "zebra is " (my-resolve 'zebra)))
答案 2 :(得分:2)
虽然这不是一个完整的解决方案,但这是一次尝试:
(defmacro let-dynamic
([[sym val & more] & body]
`(do (when (not (:dynamic (meta (resolve '~sym))))
(def ~(with-meta sym {:dynamic true}) ~sym))
(binding [~sym ~val]
~@(if (empty? more)
body
`((let-dynamic ~more ~@body)))))))
小测试:
blub> (def ^:dynamic already-dynamic 'dynamic)
#'blub/already-dynamic
blub> (def not-dynamic 'not-dynamic)
#'blub/not-dynamic
blub> (let-dynamic [already-dynamic 2] already-dynamic)
2
blub> (let-dynamic [not-dynamic 2] not-dynamic)
2
blub> (let-dynamic [not-dynamic-and-not-defined 2] not-dynamic-and-not-defined)
2
blub>
这有几个问题: