8

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

以及clojure 和 ^:dynamicClojure 动态绑定

我仍然不明白为什么需要,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))

没有宏,没有动态绑定......仍然很干净。

4

2 回答 2

15

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

然而,绑定作为一种将动态上下文传递给函数而无需显式传递参数的方式很有用。

当您编写深度嵌套的高阶函数并且不想为调用堆栈中的每个函数添加额外的参数以便将一些值传递给嵌入深处的低级函数时,它特别有用。

以您的示例为基础:

(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函数深埋在复杂的高阶函数中......

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

于 2012-10-17T12:27:49.833 回答
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"

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

于 2012-10-18T00:36:36.657 回答