我有一个更高阶的谓词
(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 .......)
那么,为什么过滤器组合物没有按预期工作?
答案 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))
在那一点组合谓词似乎更自然
(apply every-pred (map not-factor-of-x? '(2 3 5 7 11)))
并在该谓词上使用一个filter
。它似乎更清楚地传达了意图(“我希望值满足这些预测值中的每一个”)并且与(partial filter ...)
的组合不同,它避免为每个谓词创建一个中间序列。
(在Clojure 1.7+中,你也可以通过编写filter
的传感器版本来避免这种情况。