我试图了解动态变量绑定功能,所以我想这(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=>
我得到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中的不变根值。
最后,一对夫妇的造型加分,你可能会觉得有所帮助:
- 它通常被认为是坏主意,把
def
和defn
因为它们影响封闭的命名空间功能之内。 在功能你应该使用(let [foo bar] ...)
来代替。 - 当你发现自己想要使用
binding
你通常应该考虑是否可以使用高阶函数,而不是达到同样的效果。 binding
是在某些情况下非常有用,但它不是一般的好办法是传递参数-函数组合通常从长远来看更好。 这样做的原因是, binding
创建需要你的函数的执行隐式上下文,这可能很难测试/调试。