Clojure动态绑定

时间:2012-10-08 20:56:39

标签: clojure dynamic-scope

我意识到以下是一个坏主意,原因很多。我也意识到,鉴于我有一个23的stackoverflow代表,它的本质是假设我是一个新手学习编程。但是,请幽默我,并专注于“我们怎么做”,而不是“你为什么要这样做/你不想这样做”。

我想要的是什么:

(def dog (Dog. ...))
(def cat (Cat. ...))

(with-animal dog
  (println (str "Dog: " (speak) "\n")))
(with-animal cat
  (println (str "Cat: " (speak) "\n")))

输出:

Dog: woof
Cat: meow

基本上,我希望 with-animal 成为一个宏s.t.所有出现的“speak”函数调用都会映射到我正在调用块的对象。

特别是,我不想写:

(let-binding [speak (fn [] "woof")] ...)
(let-binding [speak (fn [] "meow")] ...)

相反,我希望with-animal能够将发言函数映射到我正在调用的对象的某个方法中。

在Clojure中有一个干净的方法吗?

谢谢!

2 个答案:

答案 0 :(得分:20)

动态绑定存在是有原因的,它有很多很好的用途,所以不用担心因为试图理解它而受到抨击:-)在许多旧的Clojure教程中存在一些混淆,这些教程早于需要添加^ :动态元数据到您希望动态重新绑定的变量。

第一个示例通过重新绑定现有名称来使用动态绑定。这消除了宏引入新符号的需要:

<小时/> 首先制作一些动物,我只是在这个例子中使用地图,很多人会使用其他类型的对象:

(def dog {:sound #(str "wooooof")})
(def cat {:sound #(str "mewwww")})

定义我们将重新绑定为动态的函数(允许重新绑定)

(defn :^dynamic speak [] (println "eh?"))

编写一个基本模板宏来绑定说话动物中的函数:

(defmacro with-animal [animal & body] 
    `(binding [speak (:sound ~animal)] 
       ~@body))

并测试它:

(with-animal dog  
  (println (str "Dog: " (speak) "\n")))
Dog: wooooof                                                   

<小时/> 现在是“高级版本”,它只是使用let将符号speak引入范围,而不需要动态绑定。这并不是说绑定在某种程度上是坏的,它更符合你不写的愿望(let-binding [speak (fn [] "meow")] ...)这种类型的maco被称为照应(如果你是这样的花哨的名字):

< / p>

重要的部分是~'符号之前的speak,它明确地将不合格的符号引入范围:

user> (defmacro with-animal [animal & body]
    `(let [~'speak (:sound ~animal)] 
        ~@body))
#'user/with-animal

user> (with-animal dog 
        (println (str "Dog: " (speak) "\n")))
Dog: wooooof 

nil

<小时/> 我希望这两个例子之间的对比可以回答你关于从一个对象到一个范围的绑定行为的问题。第一个示例绑定maco主体的值以及从该主体调用的任何内容。第二个示例仅为宏的主体引入名称。

答案 1 :(得分:0)

如果你真的想让动物类型以惯用方式交谈,请使用Clojure Protocols:

(defprotocol ISpeak
  (speak [animal] "make the type say it's thing"))

(deftype Dog []
  ISpeak
  (speak [this] "Woof!"))

(deftype Cat []
  ISpeak
  (speak [_] "Meow!!")) ;;you can "drop" the item if not used using _

(def a-dog (Dog.))
(speak a-dog)
;;=>"Woof!"

(def a-cat (Cat.))
(speak a-cat)
;;=>"Meow!!"

请注意,您可以使用发言方法扩展任何类型(类)。

(extend java.util.Random
  ISpeak
  {:speak (fn [_] "I'm throwing dices at you!")})

(speak (java.util.Random.))
;;=>"I'm throwing dices at you!"

Java类的语法略有不同,请参阅Protocols documentation以获取更多信息。