Clojure的和^:动态(clojure and ^:dynamic)

2019-06-26 15:42发布

我试图了解动态变量绑定功能,所以我想这(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

困惑,我想这一定程度上简单的代码:

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

是什么的两段代码之间的区别? 为什么第二个例子中工作,但先不?

提示:我只是意识到了以下工作(仍然不完全理解为什么):

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=> 

Answer 1:

我得到3结果(如你所期望的)当我运行Clojure中1.4第一个例子....你有一个新的REPL试过吗?

^:dynamic是一个符号(如所定义的Clojure的编译器的指令def )旨在是动态地反弹(与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中的不变根值。

最后,一对夫妇的造型加分,你可能会觉得有所帮助:

  • 它通常被认为是坏主意,把defdefn因为它们影响封闭的命名空间功能之内。 在功能你应该使用(let [foo bar] ...)来代替。
  • 当你发现自己想要使用binding你通常应该考虑是否可以使用高阶函数,而不是达到同样的效果。 binding是在某些情况下非常有用,但它不是一般的好办法是传递参数-函数组合通常从长远来看更好。 这样做的原因是, binding创建需要你的函数的执行隐式上下文,这可能很难测试/调试。


文章来源: clojure and ^:dynamic