在惰性集合中查找重复值的惯用方法是什么,从检查中排除某些值?
像:
(duplicates? '(1 2 3 4 1) '(1)) ;; 1 is excluded from the check
false
(duplicates? '(1 2 3 4 1) ()) ;; no exclusions
true
我的要求是代码应该能够处理无限列表。换句话说,如果通过查看第一个N
元素可以发出重复性,则代码不应该处理整个惰性集合。
我可怕的混乱尝试是:
(defn duplicates? [coll except]
(let [_duplicates? (fn [coll except accum]
(let [item (first coll)]
(if (nil? item)
false
(if (some #{item} except)
(recur (rest coll) except accum)
(if (some #{item} accum)
true
(recur (rest coll) except (conj accum item)))))))]
(_duplicates? coll except ())))
答案 0 :(得分:4)
重用现有功能比手动发明更好。这个解决方案按照要求进行了短路,乍一看显然是正确的,因为它只是建立在现有原语之上,这是对其他建议解决方案的一个巨大胜利:它们很长并且足够参与,必须进行检查和仔细检查对于错误(这些错误很容易做出,正如所提出的每个解决方案都经历了一两轮修复这一事实所示)。
(defn duplicates? [coll except]
(let [except (set except)
filtered (remove #(contains? except %) coll)]
(not= filtered (distinct filtered))))
这不适用于无限except
列表,因为它使用集合,但显然没有解决方案可以处理两个参数是无限的,所以这不是一个缺点,只是一些东西要注意。
答案 1 :(得分:3)
我会做一些像这样简单的事情:
(defn duplicates? [xs]
(not= (count (distinct xs)) (count xs)))
至于删除重复项,你可以提供一个可选的第二个参数,但这对我来说似乎不太惯用。相反,我只使用内置的remove
函数,例如:
user=> (duplicates? '(1 2 3 4 1))
true
user=> (duplicates? (remove #{1} '(1 2 3 4 1)))
false
在功能语言中,“idomatic”解决方案通常包括使用一些现有的更高级别的功能来创建新功能。在这种情况下,我们使用distinct
删除重复项,使用remove
(使用set作为过滤器函数)从检查中排除元素。
如果你真的想要一些懒惰的东西,这里有一个解决方案,当你找到一个副本时会退出。它基于distinct
:
(defn duplicates? [xs]
(loop [[x & xs :as coll] xs, seen #{}]
(if (empty? coll) false
(or (contains? seen x) (recur xs (conj seen x))))))
如果确实想要将“排除项目”设置为参数(而不仅仅是使用remove
),我会通过使函数多个来使其成为可选项-arity:
(defn duplicates?
([xs exclude?] (duplicates? (remove exclude? xs)))
([xs] (loop [[x & xs :as coll] xs, seen #{}]
(if (empty? coll) false
(or (contains? seen x) (recur xs (conj seen x)))))))
对于包含nil
和false
user=> (duplicates? '(1 2 3 4 1) #{1})
false
user=> (duplicates? '(1 2 3 4 1) #{})
true
user=> (duplicates? [true false false])
true
答案 2 :(得分:2)
您可以尝试使用集合而不是列表来处理异常和累加器。然后检查就像:累加器中的项目或例外?可能要快得多,同时还要保持懒惰。
(defn duplicates? [coll except]
(let [_duplicates?
(fn [coll except accum]
(if (seq coll)
(let [item (first coll)]
(if (contains? except item)
(recur (rest coll) except accum)
(if (contains? accum item)
true
(recur (rest coll) except (conj accum item)))))
false))]
(_duplicates? coll except #{})))
user=> (duplicates? '(1 2 3 4 1) #{1})
false
user=> (duplicates? '(1 2 3 4 1) #{})
true
注意:
user=> (duplicates? (repeat 1) #{})
true