我有三个类似的函数,它们过滤了一个与键(列)匹配的映射集合,不区分大小写。
这是我想要干的原始代码:
;; gets a collection filtered by a column, exact match
(defn get-collection-by-equals [collection-name column-name column-value]
(filter #(= (string/lower-case (column-name %)) (string/lower-case column-value)) (cached-get-collection collection-name)))
;; gets a collection filtered by a column, with partial match, case-insensitive
(defn get-collection-by-like [collection-name column-name column-value]
(filter #(string/includes? (string/lower-case (column-name %)) (string/lower-case column-value)) (cached-get-collection collection-name)))
;; gets a collection filtered by a column, which starts with given value, case-insensitive
(defn get-collection-by-starts-with [collection-name column-name column-value]
(filter #(string/starts-with? (string/lower-case (column-name %)) (string/lower-case column-value)) (cached-get-collection collection-name)))
您可以看到代码有多相似,我只是在每种情况下使用不同的匹配策略,=
,includes?
和starts-with?
。
我的第一次尝试如下:
;; returns a function which does a case-insensitive match between given column and value for the given map
(defn matching-fn [match-fn column-name column-value]
(fn [map] (match-fn (string/lower-case (column-name map)) (string/lower-case column-value))))
;; gets a collection filtered by a column, exact match
(defn get-collection-by-equals [collection-name column-name column-value]
(filter #((matching-fn = column-name column-value) %) (cached-get-collection collection-name)))
;; gets a collection filtered by a column, with partial match, case-insensitive
(defn get-collection-by-like [collection-name column-name column-value]
(filter #((matching-fn string/includes? column-name column-value) %) (cached-get-collection collection-name)))
;; gets a collection filtered by a column, which starts with given value, case-insensitive
(defn get-collection-by-starts-with [collection-name column-name column-value]
(filter #((matching-fn string/starts-with? column-name column-value) %) (cached-get-collection collection-name)))
我不喜欢这个解决方案的可读性,我发现我只能传递匹配函数,而不是有一个返回函数的函数,我想出了这个:
;; gets a collection filtered by a column, using the given function, case-insensitive
(defn get-collection-by-filter [collection-name filter-fn column-name column-value]
(filter #(filter-fn (string/lower-case (column-name %)) (string/lower-case column-value)) (cached-get-collection collection-name)))
;; gets a collection filtered by a column, exact match, case-insensitive
(defn get-collection-by-equals [collection-name column-name column-value]
(get-collection-by collection-name = column-name column-value))
;; gets a collection filtered by a column, with partial match, case-insensitive
(defn get-collection-by-like [collection-name column-name column-value]
(get-collection-by collection-name string/includes? column-name column-value))
;; gets a collection filtered by a column, which starts with given value, case-insensitive
(defn get-collection-by-starts-with [collection-name column-name column-value]
(get-collection-by collection-name string/starts-with? column-name column-value))
这是惯用的Clojure,还有其他(更好的)解决方案吗?
使用宏似乎有点矫枉过正。
答案 0 :(得分:9)
与许多OO模式一样,在函数式语言中,整个模式归结为“使用带参数的函数”。除了变为新函数的小部分之外,提取除了作为该函数的参数而变化的部分。
(defn collection-comparator [cmp]
(fn [collection-name column-name column-value]
(let [lower-value (string/lower-case column-value)]
(filter #(cmp (string/lower-case (column-name %))
lower-value)
(cached-get-collection collection-name)))))
(def get-collection-by-equals (collection-comparator =))
(def get-collection-by-like (collection-comparator string/includes?))
(def get-collection-by-starts-with (collection-comparator string/starts-with?))
答案 1 :(得分:3)
我只是使用你的get-collection-by-filter
而不再包装它 - 为什么为参数可能采用的每个值创建一个新函数?
除此之外:只需将描述设为文档字符串,然后对其进行格式化:
(defn get-collection-by-filter
"gets a collection filtered by a column, using the given function,
case-insensitive"
[collection-name filter-fn column-name column-value]
(filter #(filter-fn (string/lower-case (column-name %))
(string/lower-case column-value))
(cached-get-collection collection-name)))