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 表达式来创建字符串?

还有减少重复代码的更好想法吗?

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

@amalloy 击败了我,但我们解决方案的结构有点不同,所以我也会添加我的。

与往常一样,当有重复的逻辑时,是时候考虑将其包装在一个函数中了。在我们的例子中,multimethod 可以返回使用的算法,而不是直接返回值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 个答案并且没有提到宏 :) .. 我知道有明显的理由更喜欢高阶函数,但宏至少应该得到一个答案,所以这里是:

(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 回答