具有重复项的两个列表中的公共元素

时间:2013-08-09 17:09:53

标签: list clojure set-intersection

我想在可能存在重复的情况下找到两个[列表,向量,序列]中的公共元素。

(common-elements [1] [1]) ; [1]
(common-elements [1 2] [1 1]) ; [1]
(common-elements [1 1] [1 1 1]) ; [1 1]

以下是我目前的情况:

(defn remove-first [elem coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (let [f (first s), r (rest s)]
        (if (= elem f) 
          r 
          (cons f (remove-first elem r)))))))

(defn common-elements [coll1 coll2]
  (lazy-seq
    (when (and (seq coll1) (seq coll2))
      (let [f (first coll1)]
        (if (some #{f} coll2)
          (cons f (common-elements (rest coll1)
                                   (remove-first f coll2)))
          (common-elements (rest coll1) coll2)))))

我对4clojure的体验告诉我,我很少编写最惯用或最简洁的代码,因此我有兴趣了解是否有更好的方法。

3 个答案:

答案 0 :(得分:3)

这是我的实施。它使用map和set来保存中间数据,因此不像你的版本那样懒,但我认为它更具可读性并且具有更好的整体性能特征(你的版本具有二次时间复杂度来实现common-elements的结果)。

(require '[clojure.set :as set])
(defn common-elements [& colls]
  (let [freqs (map frequencies colls)]
    (mapcat (fn [e] (repeat (apply min (map #(% e) freqs)) e))
            (apply set/intersection (map (comp set keys) freqs)))))

答案 1 :(得分:2)

不是最有效,但相当简洁:

(defn common [& ss] 
  (let [fs (map frequencies ss), ks (map set ss)]
    (select-keys (apply merge-with min fs) 
                 (reduce clojure.set/intersection ks))))

返回值和计数的地图

(common [1] [1]) ;=> {1 1}
(common [1 2] [1 1]) ;=> {1 1}
(common [1 1] [1 1 1]) ;=> {1 2}

另一种方法,根据我自己的问题Idiomatic/Efficient Clojure way to intersect two a priori sorted vectors?修改,

(defn common [x y] 
  (loop [x (sort x) y (sort y) acc []] 
    (if (and x y)
      (let [x1 (first x) 
            y1 (first y)] 
      (cond 
        ( < x1 y1) (recur (next x) y acc) 
        ( > x1 y1) (recur x (next y) acc) 
        :else (recur (next x) (next y) (conj acc x1))))
    acc)))

返回原始问题中的向量

(common [1 1 1 2 2 3] [1 1 2 5]) ;=> [1 1 2]

当然,如果您知道输入已排序,则可以省略sort,如果您还知道它们是向量,则可以使用amalloy提供的优化来回答引用的问题。

答案 2 :(得分:1)

(defn get-commons [l1 l2]
  (let [f1 (frequencies l1) 
        f2 (frequencies l2)
        common (clojure.set/intersection (set l1) (set l2))
        love (merge-with (fn [val1 val2] (min val1 val2)) f1 f2)] 
    (mapcat #(repeat (love %) %) common)))

该功能类似于之前答案中给出的功能。我仍然会为可能偶然发现这个问题的Clojure初学者提交这个答案。我相信该函数使得函数评估结果所需​​的步骤更加清晰。