Clojure 向量中的单个重复
给定一个大小为5的从1到10的整数列表,如何检查列表中是否只有2个相同的整数 比如说Clojure 向量中的单个重复,clojure,Clojure,给定一个大小为5的从1到10的整数列表,如何检查列表中是否只有2个相同的整数 比如说 (check '(2 2 4 5 7)) 是的,但是 (check '(2 1 4 4 4)) 或 不产生这里有一个解决方案,使用频率来计算出现次数,并使用过滤器来计算只出现两次的值的数量: (defn only-one-pair? [coll] (->> coll frequencies ; map with counts of each v
(check '(2 2 4 5 7))
是的,但是
(check '(2 1 4 4 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。也许只有一双?总比检查好。你可以这样做
(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))
但我喜欢较短但仅限于5项的列表:
(check [xs] (->> xs distinct count (= 4)))
是优雅的,如果你确信你是在一个小的输入上操作的话,那就很棒了。然而,它是急切的:它强制整个输入列表,即使原则上它可以更早地判断结果是错误的。如果列表非常大,或者如果它是一个懒惰的列表,其元素的计算成本很高,那么这就是一个问题-请在列表*1范围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))))))
当然,这比无忧无虑的方法要麻烦得多,但我想不出一种方法来实现这种短路行为,而不需要手工操作。我希望看到通过组合更高级别的功能来实现相同结果的改进。针对Alan Malloy的请求,这里有一个组合解决方案:
(defn check [coll]
(let [accums (reductions conj #{} coll)]
(->> (map contains? accums coll)
(filter identity)
(= (list true)))))
这个
创建累加集的惰性序列;
针对每个相应的新元素进行测试;
过滤真实情况——元素已经存在的情况;
测试是否恰好存在其中一个。
它是懒惰的,但确实重复了扫描给定集合的业务。我以艾伦·马洛伊为例进行了尝试:
=> (check (list* 1 1 1 (range 1e9)))
false
这会立即返回。扩大范围没有什么区别:
=> (check (list* 1 1 1 (range 1e20)))
false
。。。也会立即返回
编辑以接受Alan Malloy建议的简化,我必须对其进行修改,以避免Clojure 1.10.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
否定情况:
(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
你试过什么?您具体需要什么帮助?@Carcigenicate我无法理解这背后的逻辑,我真的无法理解像检查'1 2 3'这样的情况是什么?这也是错误的,只需要一对频率就可以帮助解决这个很好的解决方案,它也适用于其他收集类型。IMHO,最好给函数一个更好的、不太通用的名称,这也反映了它的谓词性质。像这样的东西只包含一对?或者是这样。我同意这一点。如果我没有弄错的话,你的解决方案将复制seen集合中可能巨大的集合,当结果为真时。在这种情况下,你可以用速度换取内存。还是我错了?@StefanKamphausen什么交易?其他解决方案所需的空间至少与此方案相同。如果你不记得到目前为止看到过哪些项目,你将如何找到重复的项目?这一个需要更少的空间,因为一旦它知道它可以停止,它就不会一直积累地图;它也不会为输入中的每个唯一项存储一个无意义的整数。我也忘了复制收藏。我试过了。啊,很好。一个小错误:您应该使用seq和next而不是first和second,否则您可能会在包含boolean或nil的集合上得到错误的答案。编辑:不,我明白了-您正在检查一个已知只包含trues的列表。在这种情况下,您可以将每个pred完全替换为=[true],不是吗?@amalloy try=[true]。它失去了懒惰。那肯定是一只虫子列出真实作品。这是一个非常有趣的结果。我惊讶地发现,=range[]立即返回false,但=[]range无限循环。显然,这种情况一直存在,我只是从来没有注意到。@amalloy如果第一次收集被计算在内,它是否会尝试计算第二次收集,无论是否被计算在内?这是可以避免的。你的票还是我的票?很复杂。问题出在c.l.APersistentVector/doEquiv中,它假设j.u.List的实例有一个快速大小方法,即使每个clojure集合都是一个列表,但并非所有集合都被计算在内。