Clojure惯用法在惰性集合中查找重复值(不包括检查中的某些值)

时间:2013-09-08 21:29:21

标签: clojure

在惰性集合中查找重复值的惯用方法是什么,从检查中排除某些值?

像:

(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 ())))

3 个答案:

答案 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)))))))

对于包含nilfalse

的集合,此解决方案也是安全的
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