2

まず、私はClojureを初めて使用するので、これはばかげた質問になる可能性があります。

学習演習として、私は些細なテキストアドベンチャーマルチメソッドシステムを機能させています。ここで、キーワードの使用から、「sack」、「sword」などの個々のインスタンスに関連するデータを保持できる「classiness」の形式に変更したいと思います。

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 に答える