假设我们有一个整数列表:1, 2, 5, 13, 6, 5, 7
我希望找到重复的第一个数字并返回两个索引的向量。在我的示例中,[2, 5]
为5。到目前为止我所做的是loop
,但我可以更优雅,更短的方式吗?
(defn get-cycle
[xs]
(loop [[x & xs_rest] xs, indices {}, i 0]
(if (nil? x)
[0 i] ; Sequence is over before we found a duplicate.
(if-let [x_index (indices x)]
[x_index i]
(recur xs_rest (assoc indices x i) (inc i))))))
无需返回数字本身,因为我可以通过索引获取它,其次,它可能并不总是存在。
答案 0 :(得分:4)
使用列表处理的选项,但不是更简洁:
(defn get-cycle [xs]
(first (filter #(number? (first %))
(reductions
(fn [[m i] x] (if-let [xat (m x)] [xat i] [(assoc m x i) (inc i)]))
[(hash-map) 0] xs))))
答案 1 :(得分:3)
这是一个使用reduced在您找到第一个副本时停止使用序列的版本:
(defn first-duplicate [coll]
(reduce (fn [acc [idx x]]
(if-let [v (get acc x)]
(reduced (conj v idx))
(assoc acc x [idx])))
{} (map-indexed #(vector % %2) coll)))
答案 2 :(得分:1)
我知道你只要求第一个。这是一个完全惰性的实现,每步分配开销很少
(defn dups
[coll]
(letfn [(loop-fn [idx [elem & rest] cached]
(if elem
(if-let [last-idx (cached elem)]
(cons [last-idx idx]
(lazy-seq (loop-fn (inc idx) rest (dissoc cached elem))))
(lazy-seq (loop-fn (inc idx) rest (assoc cached elem idx))))))]
(loop-fn 0 coll {})))
(first (dups v))
=> [2 5]
编辑:以下是一些标准基准:
接受的答案:7.819269μs
此答案(first (dups [12 5 13 6 5 7]))
:6.176290μs
Beschastnys:5.841101μs
第一次复制:5.025445μs
答案 3 :(得分:0)
您的代码的意图似乎与您在评论中的描述不同,所以我对自己的理解并不完全自信。也就是说,loop
/ recur
绝对是解决问题的有效方法。
以下是我提出的建议:
(defn get-cycle [xs]
(loop [xs xs index 0]
(when-let [[x & more] (seq xs)]
(when-let [[y] (seq more)]
(if (= x y)
{x [index (inc index)]}
(recur more (inc index)))))))
这会将重复项目的地图返回到找到项目的两个索引的向量。
(get-cycle [1 1 2 1 2 4 2 1 4 5 6 7])
;=> {1 [0 1]}
(get-cycle [1 2 1 2 4 2 1 4 5 6 7 7])
;=> {7 [10 11]}
(get-cycle [1 2 1 2 4 2 1 4 5 6 7 8])
;=> nil
这是使用序列函数的替代解决方案。我喜欢这种方式更好,但它是否更短或更优雅可能是主观的。
(defn pairwise [coll]
(map vector coll (rest coll)))
(defn find-first [pred xs]
(first (filter pred xs)))
(defn get-cycle [xs]
(find-first #(apply = (val (first %)))
(map-indexed hash-map (pairwise xs))))
啊,明白了。这是你的想法吗?
(defn get-cycle [xs]
(loop [xs (map-indexed vector xs)]
(when-let [[[i n] & more] (seq xs)]
(if-let [[j _] (find-first #(= n (second %)) more)]
{n [i j]}
(recur more)))))
我从之前基于序列的解决方案中重新使用了find-first
。
答案 4 :(得分:0)
实际上,loop
是一个不错的选择,除非您想要找到所有重复项。像reduce
之类的东西会导致输入序列的完全扫描,即使它没有必要。
这是我的get-cycle
版本:
(defn get-cycle [coll]
(loop [i 0 seen {} coll coll]
(when-let [[x & xs] (seq coll)]
(if-let [j (seen x)]
[j i]
(recur (inc i) (assoc seen x i) xs)))))
与get-cycle
的唯一区别在于,如果没有重复项,我的版本会返回nil
。