更好的序列重复卸妆

时间:2014-08-28 15:23:16

标签: clojure

我使用此功能删除了连续的副本,但我想知道是否有更好或更短的方式来表达它使用distinct或类似的东西。

(defn duplicates
  [s]
  (reduce 
    #(if-not (= (last %1) %2) 
      (conj %1 %2) %1) 
    [] s))

2 个答案:

答案 0 :(得分:6)

clojure-1.7.0-alpha1dedupe名称下具有此功能的正确版本。

您引用的那个返回其输入序列而没有连续的重复。 (几乎可以肯定),如果它们开始输入序列,它也会吞下所有连续的nil值。

#(if-not (= (last %1) %2)
    (conj %1 %2)
    %1)

要减少的lambda说:如果累加器的最后一个元素(%1)不等于下一个输入元素(%2),则将其添加到累加器,否则返回累加器。

由于(last [])的计算结果为nil,因此当累加器为空时,它永远不会添加nil值。我把这作为练习留给读者:

确保duplicates返回输入[nil true nil]的预期结果[nil nil true true nil]

注意:使用向量进行操作时,使用peek的效果明显优于last

编辑(自编辑问题以来):distinct仅返回输入序列的每个值一次。与set不同,它返回惰性序列。

编写duplicates / dedupe的更惯用的方式是A. Webb发布的评论(因为它也是懒惰的)。否则,修复lambda以正确使用空累加器作为其输入并使用peek而不是last将更加惯用。

不是固定lambda,而是在clojure-1.7.0-alpha1中使用dedupe传感器进行急切评估,例如: G:

(into [] (dedupe) [nil nil true true nil])
-> [nil true nil] 

或懒惰评估:

(dedupe [nil nil true true nil])
-> (nil true nil)

答案 1 :(得分:2)

如果与最后一个元素不同,则reduce中的匿名函数会将一个元素与一个元素组合在一起。你可以像这样重写它:

(defn maybe-conj [s e]
  (if (= (last s) e)
     s
     (conj s e)))

然后你可以将compress-sequence重写为:

(defn compress-sequence [s]
  (reduce maybe-conj [] s))

这将做的是遍历序列的每个元素,并且只有当它与当前在该向量中的最后一个不同时才将其附加到初始空向量。输出将是初始序列的向量,删除任何运行。例如:

(compress-sequence [1 1 1 1 1 2 3 4 5 5 5 5 6 6 6 5 6 6 7 8 9])

评估为

[1 2 3 4 5 6 5 6 7 8 9]