动态让Clojure?

时间:2012-01-20 08:10:36

标签: dynamic clojure

我在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

有一种简单的方法吗?或者使用宏来实现这一目标?

3 个答案:

答案 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> 

这有几个问题:

  1. 由于某些原因,将多个符号 - 值对传递给LET-DYNAMIC不能按预期工作。他们只会在第二次打电话给LET-DYNAMIC。这可以通过更改宏来将所有符号 - 值对传递到第一个绑定表单中来解决,而不是像我在上面的代码中那样以递归方式构建绑定
  2. 之前不存在的Vars将不会被清除。您可以通过保留已定义内容和未定义内容的映射来解决此问题。
  3. 我不知道这在多线程环境中会如何发展。你必须自己评估。