首先,我刚接触Clojure,所以这可能是一个愚蠢的问题。
作为学习练习,我使用了一个简单的文本冒险多方法系统。我现在想从使用关键字改为某种形式的“类”,它可以保存与“sack”、“some”等的单个实例相关的数据。
defrecord是去这里的路吗?
问题:Can I use Clojure's derive to create a hierarchy of my defrecord class types?似乎与此类似,但公认的答案是‘不,也许使用接口’。
答案真的是否定的吗?为了使用Clojure的多方法,我必须把所有的数据表示都写成Java类吗?
谢谢,
克里斯。
工作代码:
(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)发布于 2012-10-17 13:56:34
正如@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发布于 2012-10-17 02:43:10
您希望将Java的类型系统引入Clojure。实现这一点的方法(以您所寻求的方式)是使用Protocols (另请参阅http://clojure.org/protocols)
然而,我建议你阅读下面的博客文章:Rifle-Oriented Programming with Clojure。考虑一下,数据结构可能比使用类型更好(也更灵活)。
发布于 2012-10-17 02:42:30
Clojure提供了protocols和multimethods来解决这类问题。如果你想使用defrecord,那么我建议你改用协议。
具体问题在多方法页面进行了说明:
您还可以将类用作子类(但不是父类,使某些类成为子类的唯一方法是通过Java继承)。
(derive java.util.Map ::collection)
(derive java.util.Collection ::collection)您可以通过继续使用isa?层次结构
https://stackoverflow.com/questions/12919384
复制相似问题