2012-12-18 35 views
4

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

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

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

这是可能的,如果是这样的话,需要在上面的扩展协议定义中去(代替“??????”)吗?

回答

7

最简单的解决方案可能是用反射以编程方式抓取类。

(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进一步了解数组类。

+0

谢谢!它有点丑,但Class/forName解决方案似乎工作得很好。 – mikera

+5

我发现这只适用于扩展协议定义中的第一个类。如果不是,Clojure 1.5.1会给出一个java.lang.UnsupportedOperationException异常,并且抱怨第n个字符或符号类型不支持。 – hadronzoo

1

正如hadronzoo所述,Class/forName解决方案只有在解决方案的第一个定义时才起作用,该解决方案将解决方案限制为每个协议只有一种数组类型(或者需要制定多个解协议定义)。这可以使用宏来处理多种阵列类型,以避免读者不能解析阵列符号的问题:

(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))