clojure的动态变量和绑定的实际目的是什么?

时间:2012-10-17 10:42:49

标签: clojure dynamic-scope

我查看了参考资料:http://clojure.org/vars#Vars%20and%20the%20Global%20Environmenthttp://clojuredocs.org/clojure_core/clojure.core/binding

以及clojure and ^:dynamicClojure Dynamic Binding

我仍然不明白为什么需要binding,因为我写的每个程序都没有它们,我可以找到以常规方式编写示例的方法 - 我发现更多可以理解的。有没有使用这个的项目/编程范例的例子?

例如......在动物说话例子中,你可以得到类似的效果:

(def dog {:name "Dog" :sound "Woof"})
(def cat {:name "Cat" :sound "Meow"})

(defn speak [animal]
   (str (:name animal) " says " (:sound animal))

(println (speak dog))
(println (speak cat))

没有宏,没有动态绑定......还是很干净。

2 个答案:

答案 0 :(得分:11)

严格不需要它们:正如你正确地观察到的那样,你可以在没有binding的情况下做任何你喜欢的事情,事实上如果binding不存在那么您可以使用宏和Java的ThreadLocal来相对轻松地重新实现它。

然而,绑定可用作将动态上下文传递给函数的方法,而无需显式传递参数。

当您编写深层嵌套的高阶函数并且不希望为调用堆栈中的每个函数添加额外参数以便将某些值传递给深入嵌入的较低级函数时,它尤其有用。

以你的榜样为基础:

(def ^:dynamic *loud-noises* false)

(defn speak [animal]
     (str (:name animal) " says " 
          (let [sound (:sound animal)]
            (if *loud-noises* (.toUpperCase sound) sound))))

(speak dog)
=> "Dog says Woof"

(binding [*loud-noises* true]
  (speak dog))
=> "Dog says WOOF"

注意我不需要在speak函数中添加额外的参数来获得不同的行为。在这种情况下,添加一个额外的参数将是微不足道的,但想象一下speak函数是否深埋在复杂的高阶函数中......

尽管如此,我认为总体上最好的建议是避免动态绑定,除非你真的需要它。如果可以的话,通常最好添加显式参数:直接参数可以更容易地测试和推理函数。

答案 1 :(得分:1)

关注mikera上面的例子..我可以看到为什么你会用一种表现力较弱的语言来做,但因为clojure是如此富有表现力,我宁愿重写它...... loud-noise函数可以改变稍微,再次通过添加额外的参数来实现相同的效果......

(defn speak [animal & opts]
  (let [sound (:sound animal)
        sound (if (some #(= % :louder) opts)
                 (.toUpperCase sound) sound)]
    (str (:name animal) " says " sound)))


> (speak dog)
;;=> "Dog says Woof"
> (speak dog :louder)
;;=> "Dog says WOOF"

如果您无法更改原始代码,绑定只是一种破解快速而肮脏的解决方案的方法吗?