9

エンコードするために、特定のオブジェクトのクラスをオンにしたいと考えています。

(defn encoded-msg-for [msg]
  (case (class msg)
    java.lang.Double   (encode-double msg)
    java.lang.String   (encode-str msg)
    java.lang.Long   (encode-int msg)
    java.lang.Boolean  (encode-bool msg)
    clojure.lang.PersistentArrayMap (encode-hash msg)
    clojure.lang.PersistentVector (encode-vec msg)
    nil "~"
  )
 )

を呼び出すと(encoded-msg-for {})、返されますNo matching clause: class clojure.lang.PersistentArrayMap

奇妙なのは、ケースをハッシュ マップ (クラスをキーとして、文字列を値として) に配置すると、完全にうまく機能することです。

また、(= (class {}) clojure.lang.PersistentArrayMap)真です。ここでどのような比較が行われているのでしょうか。また、オブジェクト自体のクラスまたは (より良い) 階層内の何かを切り替えるにはどうすればよいでしょうか?

4

3 に答える 3

14

クラス名をリテラルシンボルとして扱うと思いますcase-実際のクラスに解決しません:

>>> (case 'clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap 1 17)
1

>>> (case clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap 1 17)
17

これはかなり直感的ではありませんが、Clojure のcase. とにかく、慣用的な方法は、スイッチを入れる代わりにdefmultiandを使用することです:defmethodtype

(defmulti encoded-msg class)

(defmethod encoded-msg java.util.Map [x] 5)

(defmethod encoded-msg java.lang.Double [x] 7)

>>> (encoded-msg {})
5

>>> (encoded-msg 2.0)
7

ディスパッチャーは、isa?型の比較を適切に処理する述語を使用します。特に、Java 継承とうまく機能します。

defmultiを使用したくない場合は、テスト式を適切に評価するため、ユースケースでcondp置き換えることができます。case一方、一定時間の発送は提供していません。

于 2012-08-19T19:19:03.893 に答える
9

クラスでのみディスパッチする場合は、プロトコルが適切な解決策になる可能性があります。これにより、後で他の型の実装を提供できるようになります (または API のクライアント)。以下に例を示します。

(defprotocol Encodable
  (encode [this]))

(extend-protocol Encodable
  java.lang.String
  (encode [this] (println "encoding string"))
  clojure.lang.PersistentVector
  (encode [this] (println "encoding vector")))

よりきめ細かいディスパッチが必要な場合、または他のタイプへの拡張が不要であることがわかっている場合、このソリューションにはボイラープレートが多すぎる可能性があります。

于 2012-08-19T19:43:38.893 に答える