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

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

问题描述

我试图了解动态变量和绑定函数,所以尝试了这一点(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=> 

解决方案

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

^: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./p>

最后,您可能会发现一些样式点很有帮助:

  • 通常将defdefn放入函数中是一个坏主意,因为它们会影响封闭的名称空间.在函数中,应改用(let [foo bar] ...).
  • 当您发现自己想使用binding时,通常应考虑是否可以使用高阶函数来达到相同的结果. binding在某些情况下很有用,但通常不是传递参数的好方法-从长远来看,函数组合通常更好.原因是binding创建一个隐式上下文,该隐式上下文是执行函数所必需的,并且这可能很难测试/调试.

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

解决方案

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 is an instruction to the Clojure compiler that a symbol (as defined with def) is intended to be dynamically rebound (with binding).

Example:

(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

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:

  • 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天全站免登陆