Clojure动态绑定
我知道以下是一个坏主意,原因很多。 我也意识到,鉴于我有一个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是一个宏,所有发生的“speak”函数调用都被映射到我要调用该块的对象。
特别是,我不想写:
(let-binding [speak (fn [] "woof")] ...)
(let-binding [speak (fn [] "meow")] ...)
相反,我希望with-animal将讲话功能映射到我打电话给的对象的某些方法。
有没有一种干净的方式在Clojure中做到这一点?
谢谢!
动态绑定存在的原因有很多,所以不用担心会被理解:-)在许多早期的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
现在只需引入一个符号的“高级版本”就
speak
使用let来说明范围,而不需要动态绑定。 这并不是说绑定在某种程度上是不好的,它更贴近你不写的愿望(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正文的值和从该正文中调用的任何内容。 第二个示例仅为宏的主体引入名称。
如果你真的想让动物类型通俗易懂,请使用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!!"
请注意,您可以使用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类的语法有点不同,请参阅协议文档以获取更多信息。
链接地址: http://www.djcxy.com/p/65757.html