Clojure multimethods,如何添加数据?

时间:2012-10-16 16:35:09

标签: clojure multimethod

首先,我是Clojure的新手,所以这可能是个愚蠢的问题。

作为一项学习练习,我有一个简单的文本冒险多方法系统工作。我现在想要从使用关键字改为某种形式的“经典”,它可以保存与“解雇”,“剑”等个别实例相关的数据。

defrecord是这样的方式吗?

问题:Can I use Clojure's derive to create a hierarchy of my defrecord class types?似乎与此类似,但接受的答案是“不,也许使用接口”。

答案真的没有吗?我是否必须将所有数据表示作为Java类编写才能使用Clojure的多方法?

谢谢,

克里斯。

工作代码:

(derive ::unlit_root ::room)
(derive ::room ::thing)
(derive ::item ::thing)
(derive ::sword ::item)
(derive ::container ::thing)
(derive ::sack ::container)
(derive ::sack ::item)
(derive ::wardrobe ::furniture)
(derive ::furniture ::thing)
(derive ::wardrobe ::furniture)

(defmulti put (fn [x y z] [x y z]))
(defmethod put [::room ::thing ::thing] [x y z] "you can only put items into containers")
(defmethod put [::room ::sword ::sack] [x y z] "the sword cuts the sack")
(defmethod put [::room ::item ::container] [x y z] "ordinary success")
(defmethod put [::unlit_room ::thing ::thing] [x y z] "it's too dark, you are eaten by a grue")
(defmethod put [::room ::sack ::wardrobe] [x y z] "you win")
(defmethod put [::room ::item ::sack] [x y z] "you put it in the sack")
(defmethod put [::room ::furniture ::thing] [x y z] "it's too big to move")

下面是我到目前为止所尝试的内容,但我在第一个derive时收到错误:

ClassCastException java.lang.Class cannot be cast to clojure.lang.Named clojure.core/namespace (core.clj:1496)

(defrecord Item [name])
(defrecord Weapon [name, damage])
(defrecord Furniture [name])
(defrecord Container [name])
(defrecord Bag [name])
(derive Weapon Item)
(derive Container Item)
(derive Bag Container)
(derive Furniture Container)

(def sword (Weapon. "sword" 10))
(def apple (Item. "apple"))
(def cupboard (Furniture. "cupboard"))
(def bag (Bag. "bag"))


(defmulti putin (fn [src dst] [src dst]))
(defmethod putin [Item Container] [src dst] :success_0)

3 个答案:

答案 0 :(得分:4)

正如@Arthur和@noahz所提到的那样,不愉快的答案是层次结构不能用类来描述。那会给我们留下多方法?

最佳答案可能是在简单地图中包含:type键并调度该值。你丢失了协议提供的自动生成的构造函数,但它是一个非常简单的解决方案,它提供了很大的灵活性。

(def sword {:type ::weapon, :name "sword", :damage 10})
(def apple {:type ::item, :name "apple"})
(def cupboard {:type ::furniture, :name "cupboard"})
(def bag {:type ::bag, :name "bag"})

(derive ::weapon ::item)
(derive ::container ::item)
(derive ::bag ::container)
(derive ::furniture ::container)

; dispatch on [type-of-src type-of-dst]
(defmulti putin (fn [src dst] [(src :type) (dst :type)]))
(defmethod putin [::item ::container] [src dst] :success_0)

(println (putin sword bag)) ; :success_0

另一种是过度复杂化的替代方法是创建关键字类的映射,并在调度时使用它来查找层次结构中的关键字。再说一遍,我会强调你可能会找到更好的东西,但选择就在那里。

; used to look up the keywords associated with classes
(def class-keyword-map (atom {}))

; get the keyword associated with an instance's class
(defn class-keyword
  [instance]
  (@class-keyword-map (class instance)))

; this macro defines a record as normal
; however, after defining the record,
; it associates the record's type with
; a keyword generated by the record name
(defmacro def-adventure-record
  [clazz & body]
  `(do
     ; create the record as normal
     (defrecord ~clazz ~@body)
     ; and add the type to the keyword lookup
     (swap!
       class-keyword-map
       assoc ~clazz (keyword (str *ns*) (str '~clazz)))))

(def-adventure-record Item [name])
(def-adventure-record Weapon [name, damage])
(def-adventure-record Furniture [name])
(def-adventure-record Container [name])
(def-adventure-record Bag [name])

; we still need to use keywords,
; but at this point, they've been
; generated for us by the macro above
(derive ::Weapon ::Item)
(derive ::Container ::Item)
(derive ::Bag ::Container)
(derive ::Furniture ::Container)

(def sword (Weapon. "sword" 10))
(def apple (Item. "apple"))
(def cupboard (Furniture. "cupboard"))
(def bag (Bag. "bag"))

; this dispatch is done on the class's keywords
(defmulti putin (fn [src dst] [(class-keyword src) (class-keyword dst)]))

; again, keywords describe the multimethod
(defmethod putin [::Item ::Container] [src dst] :success_0)

(println (putin sword bag)) ; :success_0

答案 1 :(得分:1)

您希望将Java的类型系统引入Clojure。你这样做的方式(以你正在寻找的方式)是Protocols(另见http://clojure.org/protocols

但是,我建议您阅读以下博文:Rifle-Oriented Programming with Clojure。或许考虑数据结构可能比使用类型足够好(并且更灵活)。

答案 2 :(得分:0)

Clojure提供protocolsmultimethods来解决此类问题。如果你想使用defrecord,那么我会推荐goind与协议。


具体问题在多方法页面中解释:

“你也可以使用一个类作为子类(但不是父类,只能通过Java继承来创建一个类的子类)。”

(derive java.util.Map ::collection)
(derive java.util.Collection ::collection)

您可以继续使用{/ 1}}层次结构

相关问题