2010-05-31 74 views
10

我可能錯過了關於協議的全部觀點,但我的問題是,可以使用協議來規定如何迭代自定義數據結構或println如何打印對象?使用Clojure協議實現自定義數據結構

假設具有兩個向量地圖,

{:a [] :b []} 

當稱作第一上它,我想從採取:一個矢量,但是,該結構當連詞我想連詞爲:b。我可以使用協議來實現這種類型的行爲嗎?

+0

還沒有,因爲幾乎沒有低級別的fn(除了reduce)已經被「協議化」了,但是你可以使用defrecord或者deftype來定義一個你想要的行爲。 – cgrand 2010-05-31 15:18:12

+0

是的,但是我必須實現作用於它的函數,創建多餘的函數名稱,例如作爲我的數據結構的第一個函數頭,否? – 2010-05-31 15:42:35

+0

如果您使用deftype,您可以爲Clojure使用的各種接口(如clojure.lang.ISeq)提供您想要的實現。 – Brian 2010-05-31 17:16:18

回答

13

有些東西仍然作爲Clojure中的Java接口實現;那些,我會說有些人可能會永遠保持這種方式,以緩解與來自其他JVM語言的Clojure代碼的合作。

幸運的是,在使用deftype定義類型時,您可以讓新類型實現您需要的任何Java接口(Brian在上面的評論中提到)以及任何方法java.lang.Object。以符合你的描述的一個例子可能是這樣的:

(deftype Foo [a b] 
    clojure.lang.IPersistentCollection 
    (seq [self] (if (seq a) self nil)) 
    (cons [self o] (Foo. a (conj b o))) 
    (empty [self] (Foo. [] [])) 
    (equiv 
    [self o] 
    (if (instance? Foo o) 
    (and (= a (.a o)) 
      (= b (.b o))) 
    false)) 
    clojure.lang.ISeq 
    (first [self] (first a)) 
    (next [self] (next a)) 
    (more [self] (rest a)) 
    Object 
    (toString [self] (str "Foo of a: " a ", b: " b))) 

,你可以在REPL用它做什麼的樣本:

user> (.toString (conj (conj (Foo. [] []) 1) 2)) 
"Foo of a: [], b: [1 2]" 
user> (.toString (conj (conj (Foo. [:a :b] [0]) 1) 2)) 
"Foo of a: [:a :b], b: [0 1 2]" 
user> (first (conj (conj (Foo. [:a :b] [0]) 1) 2)) 
:a 
user> (Foo. [1 2 3] [:a :b :c]) 
(1 2 3) 

注意,REPL打印它作爲一個序列;我相信這是因爲內聯實施了clojure.lang.ISeq。您可以跳過它,並使用自定義toString代替seq方法,其中一個返回(seq a)作爲打印表示法。雖然,str總是使用toString

如果您需要pr系列功能(包括println等)的自定義行爲,則必須考慮爲您的類型實施自定義print-methodprint-method是在clojure.core中定義的多方法;例如在Clojure的源代碼中查看core_print.clj

0

我正在玩自定義集合,並希望自定義輸出到REPL,所以我最終根據Michal的最後一點的建議。我已經包含了代碼片段,關於如何做到這一點,因爲我發現通過源代碼篩選花了我一段時間,因爲我沒有在其他地方找到這個解釋。

(defmethod print-method your.custom.collection.goes.Here [c, ^java.io.Writer w] 
    (.write w (str "here is my custom output: " c))) 

這是很方便的,例如,在seq總是用括號打印您的自定義矢量(與米哈爾的例子)的情況下,你想方括號像普通Clojure的載體:

(defmethod print-method your.custom.Vector [v, ^java.io.Writer w] 
    (.write w (str (into [] v)))) 

這也意味着現在可以實現seq的方式實際返回一個數據類型序列,而不僅僅是實現REPL輸出。