2013-03-03 49 views
3

鑑於多方法如下 -如何從多方法中提取共享邏輯?

(defmulti group-data :group-by) 

(defmethod group-data :day 
    [kv] 
    (->> kv :data (group-by 
       (fn [kv] 
        (let [date (:time kv)] 
        (str 
         (month date) "-" (day date) "-" (year date))))))) 

(defmethod group-data :month 
    [kv] 
    (->> kv :data (group-by 
       (fn [kv] 
        (let [date (:time kv)] 
        (str 
         (month date) "-" (year date))))))) 

(defmethod group-data :year 
    [kv] 
    (->> kv :data (group-by 
       (fn [kv] 
        (let [date (:time kv)] 
        (year date)))))) 

在所有三個多方法,唯一的區別是字符串函數。我如何創建一個更高階的函數,在那裏它只需要不同的s表達式來創建字符串?

還有什麼更好的想法,以減少重複的代碼?

回答

3

最簡單的答案是定義包含所有共享邏輯的功能,這需要在用於填充,所述改變所述「洞」的另一功能:

(defn helper [kv time-fn] 
    (->> kv :data (group-by 
       (fn [kv] 
        (let [date (:time kv)] 
        (timefn data)))))) 

(defmulti group-data :group-by) 

(defmethod group-data :day 
    [kv] 
    (helper kv (fn [date] 
       (str (month date) "-" (day date) "-" (year date))))) 

(defmethod group-data :month 
    [kv] 
    (helper kv 
      (fn [date] 
      (str (month date) "-" (year date))))) 

(defmethod group-data :year 
    [kv] 
    (helper kv year)) 
+0

感謝@amalloy,這是一個整潔的解決方案。 – murtaza52 2013-03-04 14:17:30

2

@amalloy打我束,但我們解決方案的結構有點不同,所以我也會加我的解決方案。

與往常一樣,當存在重複的邏輯時,是時候考慮將它封裝在一個函數中。在我們的例子中,不是直接返回值,multimethod可以返回用於group-by的算法。然後,group-data可以寫成一個常規函數,它使用multimethod來確定它是如何分組的

(defmulti group-by-algorithm :group-by) 

(defn group-data 
    [kv] 
    (->> kv :data (group-by (group-by-algorithm kv)))) 

(defmethod group-by-algorithm :day 
    [_] 
    (fn [{date :time}] ; destructuring kv to save us a `let` 
    (str 
     (month date) "-" (day date) "-" (year date)))) 

(defmethod group-by-algorithm :month 
    [_] 
    (fn [{date :time}] 
    (str 
     (month date) "-" (year date)))) 

(defmethod group-by-algorithm :year 
[_] 
(fn [{date :time}] 
    (year date))) 

該解決方案可讓算法返回任何內容並且通常適用。然而,假設每個算法都會返回一些基於日期的由破折號分隔的字符串,我們可以通過引入一個函數來減少樣板,該函數接受生成值的函數並通過依次調用它們來生成破折號分隔的字符串。

(defmulti fns-to-group-by :group-by) 

(defn group-by-algorithm 
    [group-by-fns] 
    (fn [{date :time}] 
    (->> 
     (map #(%1 date) group-by-fns) ; Call each function on the date 
     (interpose "-") ; Separate by dashes 
     (apply str)))) ; And mush them into a string 

(defn group-data 
    [kv] 
    (->> kv :data (group-by (group-by-algorithm (fns-to-group-by kv))))) 

(defmethod fns-to-group-by :day 
    [_] 
    [month day year]) 

(defmethod fns-to-group-by :month 
    [_] 
    [month year]) 

(defmethod fns-to-group-by :year 
[_] 
[year]) 
+0

這真的很酷。你正在把複雜性放在它應該是的。 – murtaza52 2013-03-04 14:19:08

4

有時候,你並不需要多方法:

(def dmap {:day #(str (month %) "-" (day %) "-" (year %)) 
      :month #(str (month %) "-" (year %)) 
      :year #(year %)}) 

(defn group-data [kv] 
    (->> kv :data (group-by 
       (fn [kv] 
        (let [date (:time kv)] 
        ((dmap (:group-by kv)) date)))))) 
+0

嘿,這真的很酷!使用地圖存儲功能,然後根據需要檢索它們。所以我的下一個問題是,我應該在什麼時候選擇一種多方法來處理上述問題? – murtaza52 2013-03-04 14:11:13

1

3個答案已經和沒有提到的宏:) ..我知道有明顯的理由喜歡高階函數,但宏應該至少一個答案,所以這裏是:

(defmacro group-by-template [date-symbol expression coll] 
    `(group-by 
     (fn [kv#] 
     (let [~date-symbol (:time kv#)] 
      ~expression)) ~coll)) 

(defmulti group-data :group-by) 

(defmethod group-data :day 
    [kv] 
    (->> kv :data (group-by-template date (str (month date) "-" (day date) "-" (year date))))) 

(defmethod group-data :month 
    [kv] 
    (->> kv :data (group-by-template date (str (month date) "-" (year date))))) 

(defmethod group-data :year 
    [kv] 
    (->> kv :data (group-by-template date (year date)))) 
+1

宏比這個問題的必要性更強大,但至少如果你要使用宏,你應該嘗試減少浪費的輸入量:https://gist.github.com/amalloy/5084918 – amalloy 2013-03-04 19:48:02

+0

@amalloy:尼斯..我不知道關於宏。 – Ankur 2013-03-05 04:11:42