clojure 和 ^:dynamic [英] clojure and ^:dynamic

查看:17
本文介绍了clojure 和 ^:dynamic的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解动态变量和绑定函数,所以我尝试了这个(clojure 1.3):

I tried to understand dynamic variables and binding function so I tried this (clojure 1.3):

user=> (defn f [] 
           (def ^:dynamic x 5) 
           (defn g [] (println x)) 
           (defn h [] (binding [x 3] (g))) 
           (h))
#'user/f
user=> (f)     
5
nil

困惑,我尝试了这个稍微简单的代码:

Confused, I tried this somewhat simpler code:

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn g [] (println y))
#'user/g
user=> (defn h [] (binding [y 3] (g)))
#'user/h
user=> (h)
3
nil

这两段代码有什么区别?为什么第二个例子可以,而第一个不行?

What is the difference between the two pieces of code? Why does the second example work but the first does not?

提示:我刚刚意识到以下有效(仍然不完全理解为什么):

Hint: I just realized that the following works (still don't fully understand why):

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h))
#'user/f
user=> (f)
3
nil
user=> 

推荐答案

当我在 Clojure 1.4 中运行您的第一个示例时,我得到了 3 个结果(正如您所料)....?

I get 3 as a result (as you would expect) when I run your first example in Clojure 1.4.... have you tried this with a fresh REPL?

^:dynamic 是对 Clojure 编译器的一条指令,即一个符号(由 def 定义)旨在动态重新绑定(使用 binding代码>).

^:dynamic is an instruction to the Clojure compiler that a symbol (as defined with def) is intended to be dynamically rebound (with binding).

示例:

(def foo 1)
(binding [foo 2] foo)
=> IllegalStateException Can't dynamically bind non-dynamic var: ...

(def ^:dynamic bar 10)
(binding [bar 20] bar)    ;; dynamically bind bar within the scope of the binding
=> 20
bar                       ;; check underlying value of bar (outside the binding)
=> 10

请注意,binding 在调用线程中具有动态范围 - 在绑定中调用的任何函数都将看到 bar (20) 的修改值,但任何其他线程将仍然看到 10 不变的根值.

Note that binding has dynamic scope within the calling thread - any functions called within the binding will see the modified value of bar (20), but any other threads will still see the unchanged root value of 10.

最后几个可能对您有帮助的风格要点:

Finally a couple of style points that you may find helpful:

  • defdefn 放在函数中通常被认为是个坏主意,因为它们会影响封闭的命名空间.在函数中,您应该使用 (let [foo bar] ...) 代替.
  • 当您发现自己想要使用 binding 时,您通常应该考虑是否可以使用高阶函数来实现相同的结果.binding 在某些情况下很有用,但它通常不是传递参数的好方法 - 从长远来看,函数组合通常更好.这样做的原因是 binding 创建了执行函数所需的隐式上下文,这可能难以测试/调试.
  • It's generally considered bad idea to put def and defn within functions as they affect the enclosing namespace. Within functions you should use (let [foo bar] ...) instead.
  • When you find yourself wanting to use binding you should normally consider whether you can achieve the same result using higher order functions instead. binding is useful in some contexts but it is not in general a good way to pass parameters around - function composition is usually better in the long run. The reason for this is that binding creates an implicit context that is required for the execution of your function and this can be difficult to test/debug.

这篇关于clojure 和 ^:dynamic的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆