Clojure多方法,如何添加数据?
首先,我是Clojure的新手,所以这可能是个愚蠢的问题 作为一个学习练习,我有一个简单的文本冒险多方法系统在工作。我现在想从使用关键字改为某种形式的“分类”,它可以保存与“sack”、“sword”等单个实例相关的数据Clojure多方法,如何添加数据?,clojure,multimethod,Clojure,Multimethod,首先,我是Clojure的新手,所以这可能是个愚蠢的问题 作为一个学习练习,我有一个简单的文本冒险多方法系统在工作。我现在想从使用关键字改为某种形式的“分类”,它可以保存与“sack”、“sword”等单个实例相关的数据 defrecord是这里的路吗 问题:似乎与此类似,但公认的答案是“不,可能使用接口” 答案真的是否定的吗?为了使用Clojure的多方法,我是否必须将所有数据表示都编写为Java类 谢谢 克里斯 工作代码: (derive ::unlit_root ::room) (deri
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")
下面是我到目前为止尝试过的,但在第一次导出时出现错误:
ClassCastException java.lang.Class不能强制转换为clojure.lang.Named clojure.core/namespace(core.clj:1496)
Clojure为解决此类问题提供了和。如果您想使用defrecord,那么我建议您改为使用协议
具体问题在“多种方法”页面中解释: 您也可以将类用作子类(但不能用作父类,使某些内容成为类的子类的唯一方法是通过Java继承)
您可以继续使用
isa?
层次结构,将Java的类型系统带入Clojure。你这样做的方式(以你所寻求的方式)是
但是,我建议您阅读以下博文:。也许考虑到数据结构可能比使用类型足够好(并且更灵活)。 < P>不愉快的答案,如@亚瑟和Noaz所提到的,是等级不能用类来描述。这给我们留下了什么样的多重方法 最好的答案可能是在简单的映射中包含一个
:键入键,并根据该值进行调度。您失去了协议提供的自动生成构造函数之类的功能,但它是一个非常简单的解决方案,并且提供了很大的灵活性
(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
谢谢,我喜欢第二个解决方案。在我看来,这就是使用LISP的全部意义所在。您已经创建了一个宏(def adventure record)来简化语法(而不是手动将每条记录添加到类关键字映射中)。是否有一种解决方案涉及gen interface
来生成层次结构,并使每条记录实现一个类似命名的接口?对不起,朋友。我看到了您所指的路径,但我从来没有走过它。如果我有时间,我会看看能不能凑合些什么。
(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