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. 无论如何,惯用的方法是使用defmultianddefmethod而不是打开type

(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 回答
4

如果您正在寻找实现此目的的替代方法,请查看condp

(condp = (type {})
  clojure.lang.PersistentArrayMap :one
  clojure.lang.PersistentVector :many
  :unknown) ;; => :one
于 2017-10-03T00:53:31.987 回答