向量中的单个重复项

时间:2019-01-02 20:57:47

标签: clojure

给出一个从1到10的整数列表,大小为5,如何检查列表中是否只有2个相同的整数?

例如

(check '(2 2 4 5 7)) 

是的,而

(check '(2 1 4 4 4)) 

(check '(1 2 3 4 5))

不产生

6 个答案:

答案 0 :(得分:4)

这是一种使用频率对出现次数进行计数并进行过滤以对仅出现两次的值进行计数的解决方案:

(defn only-one-pair? [coll]
  (->> coll
       frequencies                ; map with counts of each value in coll
       (filter #(= (second %) 2)) ; Keep values that have 2 occurrences
       count                      ; number of unique values with only 2 occurrences
       (= 1)))                    ; true if only one unique val in coll with 2 occurrences

哪个给:

user=> (only-one-pair? '(2 1 4 4 4))
false
user=> (only-one-pair? '(2 2 4 5 7))
true
user=> (only-one-pair? '(1 2 3 4 5))
false

该功能的中间步骤可让您了解其工作原理:

user=> (->> '(2 2 4 5 7) frequencies)
{2 2, 4 1, 5 1, 7 1}
user=> (->> '(2 2 4 5 7) frequencies (filter #(= (second %) 2)))
([2 2])
user=> (->> '(2 2 4 5 7) frequencies (filter #(= (second %) 2)) count)
1

根据建议,该函数可以使用更具描述性的名称,并且最佳做法是为谓词函数指定??在Clojure的结尾处。那么也许只有一对?比只检查要好。

答案 1 :(得分:3)

Christian Gonzalez's answer很优雅,如果您确定要使用较小的输入,则非常好。但是,它很急切:即使原则上可以早些告诉结果为假,它也会强制整个输入列表。如果列表很大,或者它是一个懒惰的列表(其元素的计算成本很高),就会出现问题-在(list* 1 1 1 (range 1e9))上尝试一下!因此,我在下面介绍了一种替代方法,该方法一旦发现第二个重复项就会短路:

(defn exactly-one-duplicate? [coll]
  (loop [seen #{}
         xs (seq coll)
         seen-dupe false]
    (if-not xs
      seen-dupe
      (let [x (first xs)]
        (if (contains? seen x)
          (and (not seen-dupe)
               (recur seen (next xs) true))
          (recur (conj seen x) (next xs) seen-dupe))))))

自然地,它比无忧无虑的方法要麻烦得多,但是我无法看到一种无需手工完成所有操作即可获得这种短路行为的方法。我希望看到通过结合更高级别的功能可以达到相同结果的改进。

答案 2 :(得分:1)

为回答艾伦·马洛伊(Alan Malloy)的请求,这是一种组合解决方案:

(defn check [coll]
  (let [accums (reductions conj #{} coll)]
    (->> (map contains? accums coll)
         (filter identity)
         (= (list true)))))

  • 创建累积集的惰性序列;
  • 针对每个相应的新元素进行测试;
  • 用于true情况的过滤器-那些已经存在元素的过滤器;
  • 测试其中是否确切一个。

这很懒,但是确实重复了扫描给定集合的工作。我在艾伦·马洛伊(Alan Malloy)的示例中进行了尝试:

=> (check (list* 1 1 1 (range 1e9)))
false

这立即返回。扩大范围没有区别:

=> (check (list* 1 1 1 (range 1e20)))
false

...也立即返回。


编辑以接受Alan Malloy建议的简化,为了避免Clojure 1.10.0中似乎存在错误,我不得不对其进行修改。

答案 3 :(得分:0)

您可以这样做

(defn check [my-list]
  (not (empty? (filter (fn[[k v]] (= v 2)) (frequencies my-list)))))

(check '(2 4 5 7))
(check '(2 2 4 5 7))

答案 4 :(得分:0)

(letfn [(check [xs] (->> xs distinct count (= (dec (count xs)))))]
    (clojure.test/are [input output]
        (= (check input) output)
        [1 2 3 4 5] false
        [1 2 1 4 5] true
        [1 2 1 2 1] false))

但是我喜欢一个较短的(但仅限于5个项目列表):

(check [xs] (->> xs distinct count (= 4)))

答案 5 :(得分:0)

类似于使用频率的其他人-只需应用两次

(-> coll
    frequencies
    vals
    frequencies
    (get 2)
    (= 1))

正例:

(def coll '(2 2 4 5 7))

frequencies=> {2 2, 4 1, 5 1, 7 1}
vals=> (2 1 1 1)
frequencies=> {2 1, 1 3}
(get (frequencies #) 2)=> 1

否定情况:

(def coll '(2 1 4 4 4))

frequencies=> {2 1, 1 1, 4 3}
vals=> (1 1 3)
frequencies=> {1 2, 3 1}
(get (frequencies #) 2)=> nil