2

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

作为一个学习练习,我有一个简单的文本冒险多方法系统工作。我现在想从使用关键字更改为某种形式的“classiness”,它可以保存与“sack”、“sword”等各个实例有关的数据。

defrecord去这里的路吗?

问题:我可以使用 Clojure 的派生来创建我的 defrecord 类类型的层次结构吗?似乎与此类似,但接受的答案是“不,也许使用接口”。

答案真的没有吗?为了使用 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)
4

3 回答 3

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
于 2012-10-17T05:56:33.760 回答
1

您想将 Java 的类型系统引入 Clojure。你这样做的方式(以你正在寻求的方式)是使用协议(另见http://clojure.org/protocols

但是,我建议您阅读以下博客文章:使用 Clojure 进行面向步枪的编程。也许考虑一下数据结构可能比使用类型足够好(并且更灵活)。

于 2012-10-16T18:43:09.843 回答
0

Clojure提供了解决此类问题的协议和多种方法。如果您想使用 defrecord,那么我建议您使用协议来代替。


具体问题在多方法页面中有说明:

“您也可以将类用作子类(但不能使用父类,将某物作为类的子类的唯一方法是通过 Java 继承)。”

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

您可以继续使用isa?层次结构

于 2012-10-16T18:42:30.437 回答