【问题标题】:Clojure Dynamic BindingClojure 动态绑定
【发布时间】:2012-09-29 03:41:33
【问题描述】:

我意识到以下是一个坏主意,原因有很多。我还意识到,鉴于我的 stackoverflow 代表为 23,因此很自然地假设我是学习编程的新手。但是,请幽默,并专注于“我们如何做到这一点”而不是“你为什么要这样做/你不想这样做”方面。

我想要什么:

(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 使 speak 函数映射到我正在调用的对象的某个方法。

在 Clojure 中是否有一种简洁的方法可以做到这一点?

谢谢!

【问题讨论】:

  • 我喜欢免责声明 :)

标签: clojure dynamic-scope


【解决方案1】:

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

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


首先制作一些动物,我在这个例子中只是使用地图,很多人会使用一些其他类型的对象:
(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 称为照应(如果您喜欢这样的花哨名称):

重要的部分是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 主体的值以及从该主体调用的任何内容。第二个示例只为宏的主体引入名称。

【讨论】:

  • 我喜欢这个解决方案。我可以看到自己在一个地方定义了与动物一起使用的所有功能,然后为每个动物在该文件中定义它。
  • speak 在这两个示例中都不需要是函数,不是吗?可能只是(def ^:dynamic speak "eh?")。无论如何,您所做的只是将其连接成一个字符串。
【解决方案2】:

如果您真的想让动物类型说话惯用,请使用 Clojure 协议:

(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!!"

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

(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 了解更多信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 1970-01-01
    • 2015-09-14
    • 2012-10-07
    相关资源
    最近更新 更多