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

3 つのマルチメソッドすべてで、唯一の違いは文字列関数です。文字列を作成するために異なる s 式を取るだけの高階関数を作成するにはどうすればよいですか?

また、繰り返されるコードを減らすためのより良いアイデアはありますか?

4

4 に答える 4

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))))))
于 2013-03-04T01:10:39.103 に答える
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))
于 2013-03-03T20:39:55.130 に答える
2

@amaloy は私を打ち負かしましたが、私たちのソリューションの構造は少し異なるので、私も追加します。

いつものように、ロジックが重複している場合は、それを関数でラップすることを考えるときです。この場合、値を直接返すのではなく、マルチメソッドは に使用されたアルゴリズムを返すことができgroup-byます。次に、マルチメソッドを使用してグループ化する方法group-dataを決定する通常の関数として記述できます。

(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])
于 2013-03-03T20:59:21.570 に答える
1

すでに3つの回答があり、マクロについては言及されていません:) ..高階関数を好む明らかな理由があることはわかっていますが、マクロには少なくとも1つの回答が必要です。

(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))))
于 2013-03-04T04:51:55.673 に答える