将Clojure协议扩展为原始数组

时间:2012-12-18 01:08:38

标签: arrays clojure protocols primitive-types

我想扩展一个Clojure协议来处理Java原始数组。

(defprotocol PVectorisable
  (to-vector [a]))

(extend-protocol PVectorisable
  ??????
    (to-vector [coll]
      (Vectorz/create ^doubles coll))
  java.util.List
    ... other implementations......)

这是可能的,如果是这样,上面的扩展协议定义需要什么(代替“??????”)?

2 个答案:

答案 0 :(得分:9)

最简单的解决方案可能是以反射方式编写类。

(defprotocol do-a-thing
 (print-thing [thing]))

(extend-protocol do-a-thing
 (class (float-array 0))
  (print-thing [_]
   (println "it's a float array")))

Java的数组有些奇怪的名字。例如,float数组是[F。如果您尝试直接在REPL中使用它,它将会阻塞不匹配的[。但是,您仍然可以使用此名称,例如Class/forName

(defprotocol do-another-thing
 (print-another-thing [thing]))

(extend-protocol do-another-thing
 (Class/forName "[F")
  (print-another-thing [_]
   (println "it's still a float array")))

This article详细介绍了数组类。

答案 1 :(得分:1)

正如hadronzoo所指出的,Class / forName解决方案只有在它是defprotocol的第一个定义时才有效,它将解决方案限制为每个协议只有一个数组类型(或者必须进行多个defprotocol定义)。这可以使用宏来处理多个数组类型,以避免读者无法解析数组符号的问题:

(defprotocol ISample
  (sample [_]))

(defmacro extend-protocol-arrays []
  (let [ba (symbol "[B")
        ia (symbol "[I")
        oa (symbol "[Ljava.lang.Object;")]
  `(extend-protocol ISample
     ~ba
     (sample [this#] (println "Byte Array"))
     ~ia
     (sample [this#] (println "Int Array"))
     ~oa
     (sample [this#] (println "Object Array")))))

(extend-protocol-arrays)

(sample (byte-array 0))

(sample (int-array 0))

(sample (make-array Object 0))