Clojure过滤器成分减少

时间:2015-01-10 06:49:07

标签: clojure functional-programming higher-order-functions function-composition

我有一个更高阶的谓词

(defn not-factor-of-x? [x]
  (fn [n]
    (cond 
      (= n x) true
      (zero? (rem n x)) false
      :else true)))

返回一个谓词,检查给定的参数n是否不是x的因子。 现在我想过滤一个数字列表,找出哪些不是说法的因素'(2 3)。一种方法是:

(filter (not-factor-of-x? 3) (filter (not-factor-of-x? 2) (range 2 100)))

但是人们只能输入这么多。为了动态地执行此操作,我尝试了函数组合:

(comp (partial filter (not-factor-of-x? 2)) (partial filter (not-factor-of-x? 3)))

它有效。所以我尝试减少过滤器,如下所示:

(defn compose-filters [fn1 fn2]
  (comp (partial filter fn1) (partial filter fn2)))
(def composed-filter (reduce compose-filters (map not-factor-of-x? '(2 3 5 7 11))))
(composed-filter (range 2 122))   ; returns (2 3 4 5 6 7 8 9 10 .......)

那么,为什么过滤器组合物没有按预期工作?

2 个答案:

答案 0 :(得分:2)

有很多方法可以组合函数和/或改进代码。这是一个:

(defn factor? [n x]
  (and (not= n x) (zero? (rem n x))))

(->> (range 2 100)
     (remove #(factor? % 2))
     (remove #(factor? % 3)))

;; the same as the above
(->> (range 2 100)
     (remove (fn [n] (some #(factor? n %) [2 3]))))

答案 1 :(得分:0)

要查看(reduce compose-filters ...的问题,让我们看一下实际做的事情。首先,它在前两个谓词上使用filter并组合它们。结果是从序列到序列的新函数。当过滤器需要谓词时,下一次迭代会在该函数上调用filter。每个序列都是一个真值,因此新的过滤器现在永远不会删除任何值,因为它使用的是一个始终返回真值的“谓词”。所以最后,只有最后一个过滤器实际上进行了任何过滤 - 在我的REPL中你的代码删除了数字22,33,44等等,因为11是它们中的一个因素。我认为你想在这里做的减少更像是

(reduce comp (map (comp (partial partial filter) not-factor-of-x?) '(2 3 5 7 11)))

请注意,因为我们只想为每个号码调用(partial filter)一次,您可以将其移动到mapreduce的映射步骤中。至于我如何做到这一点,考虑到你一起产生所有的谓词:

(map not-factor-of-x? '(2 3 5 7 11))

使用every-pred

在那一点组合谓词似乎更自然
(apply every-pred (map not-factor-of-x? '(2 3 5 7 11)))

并在该谓词上使用一个filter。它似乎更清楚地传达了意图(“我希望值满足这些预测值中的每一个”)并且与(partial filter ...)的组合不同,它避免为每个谓词创建一个中间序列。
(在Clojure 1.7+中,你也可以通过编写filter的传感器版本来避免这种情况。