2012-01-28 32 views
2

我正在編寫一個clojure函數來將各種數據類型格式化爲一個字符串。取決於數據類型的格式輸出字符串

我天真的解決方案:我沒有用過

(defn p [d] 
     (cond 
     (vector? d) (str "vector: " d) 
     (list? d) (str "list: " d))) 

#'user/p 
user> (p [1 2 3]) 
"vector: [1 2 3]" 
user> (p '(1 2 3)) 
"list: (1 2 3)" 

多方法之前。我這是一個很好的使用,或者是否有另一種方法來避免臭味使用cond?

回答

5

我會去定義格式協議,並將其延伸到你需要的類型,通過@rodnaph的建議:

(defprotocol Format 
    (fmt [this])) 

(extend-protocol Format 
    clojure.lang.IPersistentVector 
    (fmt [this] (str "vector:" this)) 
    clojure.lang.IPersistentList 
    (fmt [this] (str "list:" this))) 

不過,我不知道這將有更好的表現,多方法或協議擴展。

的多方法定義看起來是這樣的:

(defmulti fmt class) 

(defmethod fmt 
    clojure.lang.IPersistentVector [this] 
    (str "vector:" this)) 
(defmethod fmt 
    clojure.lang.IPersistentList [this] 
    (str "list:" this)) 

編輯:你可能要檢查this question about protocols vs multimethods,因爲有相當不錯的兩種解釋常見的使用情況。根據這些信息,最好在你的方案中使用一個協議。

+0

感謝您展示如何做到這一點。我喜歡這裏的multimethods的簡單性。我可能只需要根據它是seq或字符串還是其他任何地方來改變格式,所以我可能會避免對接口進行硬編碼。 – devth 2012-01-31 00:08:56

1

(我是小白,但)它看起來像一個協議將是最適合的:

http://clojure.org/protocols

然後你就可以定義不同的格式實現每個數據類型你想支持。

1

我相信你的問題是顯示一個相對於你真正需要完成的簡化案例。對於一般的解決方案,我同意協議是一個體面的方法。

你問過關於multimethods的問題,但遇到麻煩的是調度功能。 defmulti需要一個調度函數,這個函數將被調用到實函數的參數上。調度函數必須返回一個值,然後可以使用該值來選擇將調用哪個方法實現。

問題是,你發什麼?要集合類型區分,你最終的東西是這樣的:

(defmulti stringify class) 
(defmethod stringify clojure.lang.PersistentVector [v] ...) 
(defmethod stringify clojure.lang.PersistentArrayMap [m] ...) 
;;; More dispatching on concrete class names 

嘛,只要你看到具體clojure.lang類的名字出現在你的代碼,各種警鐘應該去關閉。這些方式太具體了......如果Clojure核心庫發生變化,它們將會中斷,但它們不能很好地與Java interop一起工作,它們不包括恰巧實現Seqable的用戶定義類型...總之,他們是抽象的細分。

任何時候你都會試圖派發類名,無論是來自Clojure,Java還是第三方庫,你都應該總是達到擴展類型。

+1

對於我自己的學習 - 是否有理由拒絕Clojure內置的特殊分層系統,並記錄在http://clojure.org/multimethods上? (你沒有提到它,並且似乎假設在具體類上派遣,似乎正在將Java讀回到Clojure中。) – 2012-01-30 15:41:21

+1

最初的問題是使用文字作爲數據結構。如果有方法在{}和[]之上構建臨時層次結構,我不知道它。 我在指出,使用多方法必然會吸引Clojure數據結構下的實現類(用Java編寫)。這就是爲什麼我不喜歡這種多方法。 – mtnygard 2012-01-31 18:18:45

相關問題