Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure多方法,如何添加数据?_Clojure_Multimethod - Fatal编程技术网

Clojure多方法,如何添加数据?

Clojure多方法,如何添加数据?,clojure,multimethod,Clojure,Multimethod,首先,我是Clojure的新手,所以这可能是个愚蠢的问题 作为一个学习练习,我有一个简单的文本冒险多方法系统在工作。我现在想从使用关键字改为某种形式的“分类”,它可以保存与“sack”、“sword”等单个实例相关的数据 defrecord是这里的路吗 问题:似乎与此类似,但公认的答案是“不,可能使用接口” 答案真的是否定的吗?为了使用Clojure的多方法,我是否必须将所有数据表示都编写为Java类 谢谢 克里斯 工作代码: (derive ::unlit_root ::room) (deri

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

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

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