Clojure在惰性集合中查找重复值的惯用方法(从检查中排除某些值)

Clojure在惰性集合中查找重复值的惯用方法(从检查中排除某些值),clojure,Clojure,在惰性集合中查找重复值的惯用方法是什么,从检查中排除一些值 比如: 我的要求是代码应该能够处理无限列表。换句话说,如果可以通过查看第一个N元素来判断重复性,那么代码就不必处理整个惰性集合 我糟糕的尝试是: (defn duplicates? [coll except] (let [_duplicates? (fn [coll except accum] (let [item (first coll)]

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

比如:

我的要求是代码应该能够处理无限列表。换句话说,如果可以通过查看第一个
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 ())))

对于异常和累加器,可以尝试使用集合而不是列表。然后检查如下:项目是在累加器中还是在异常中?在保持懒惰的同时,速度可能要快得多

(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
注:


我会做一些简单的事情,比如:

(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
(设置为筛选函数)从检查中排除元素


如果你真的想要一些懒惰的东西,这里有一个解决方案,当你发现一个重复的时候,它会退出。它松散地基于
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
),我会将函数multiple 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

重用现有功能比手工发明要好得多。此解决方案按要求短路,乍一看显然是正确的,因为它是在现有原语的基础上构建的,与其他建议的解决方案相比,这是一个巨大的优势:它们很长,涉及面很广,必须对它们进行检查并仔细检查是否存在错误(而这些错误很容易犯,每一个提出的解决方案都经过了一轮或两轮修复)


这不适用于无限的
,除了
列表,因为它使用集合,但显然没有解决方案可以处理两个参数都是无限的,所以这不是一个真正的缺点,只是需要注意。

排序并查看是否有两个相同的连续数字排序在无限的集合中不会花费很长时间吗d通过查看第一个
N
元素,您可能可以找到重复的元素。我更新了我的帖子以明确这一点。
(迭代标识1)
==>
(重复1)
是的,我喜欢
if let
,而设置确实更有意义。@MarcusJuniusBrutus-注意,由于使用了
if let
(以及集合的使用方式),该输入失败,表示没有重复项:
[nil nil]
。与
[false]相同
。是的,但是
计数
区别
在一个无限列表上不会永远持续下去吗?另一方面,你可以通过不必查看整个列表来判断重复性与否。我更新了我的帖子,以明确这一点。@MarcusJuniusBrutus-在无限列表中检查重复性是没有意义的e集合。如果没有重复项,它将永远不会返回。另一方面,如果你知道它将返回,那么你已经知道它有重复项,那么检查的意义是什么?根据上下文,最终用户可能会这样做(例如)如果他认为搜索花费的时间太长,并且他确信重复项(如果存在的话)离集合的头部太远,则中止搜索。@MarcusJuniusBrutus-同样,这没有什么意义。如果他们只想查看前N个元素,那么他们可以调用
(重复项)(取N coll))
而不是执行混乱的中止。这样做效率低下的原因是它会强制生成整个惰性序列。例如,如果每个元素需要10秒才能生成,而您只需检查前2个就可以避免,这可能不是最佳解决方案。或者换句话说,我希望函数不会o只有在必须花费永远的情况下才会花费永远。(distinct?'(0 0))将求值为true。您需要(应用distinct…)您还需要逃逸空coll,否则(重复?(){})会中断。伙计,我讨厌
distinct?
。我再也不会重用现有函数了。
(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)))))))
user=> (duplicates? '(1 2 3 4 1) #{1})
false
user=> (duplicates? '(1 2 3 4 1) #{})
true
user=> (duplicates? [true false false])
true
(defn duplicates? [coll except]
  (let [except (set except)
        filtered (remove #(contains? except %) coll)]
    (not= filtered (distinct filtered))))