Clojure 如何比较向量中连续元素的值进行过滤?

Clojure 如何比较向量中连续元素的值进行过滤?,clojure,Clojure,我需要过滤一个给定的向量,这样输出只包含那些不是直接邻居的重复元素 Example : I/P -> [1 1 3 2 2 4 5 5] O/P -> [3 4] 这几乎是一个蛮力解决方案,所以希望我们能看到一些更令人满意的答案,但为了让球滚起来 (defn lonely? "Return the ith element of v if it doesn't have a matching neighbor" [i v] (when (cond

我需要过滤一个给定的向量,这样输出只包含那些不是直接邻居的重复元素

Example : I/P -> [1 1 3 2 2 4 5 5]
          O/P -> [3 4]

这几乎是一个蛮力解决方案,所以希望我们能看到一些更令人满意的答案,但为了让球滚起来

(defn lonely? 
  "Return the ith element of v if it doesn't have a matching neighbor"
  [i v]
  (when (cond
          (zero? i) (not= (v 0) (v 1))
          (= i (- (count v) 1)) (not= (v i) (v (- i 1)))
          :else (and (not= (v i) (v (- i 1)))
                     (not= (v i) (v (+ i 1)))))
    (v i)))

> (def v [1 1 3 2 2 4 5 5])

> (keep #(lonely? % v) (range (count v)))
(3 4)
请注意,此解决方案还包括两个以上相邻值相同的情况,例如:

(def data-3 [1 1 3 2 2 2 4 5 5 2 5 5])
;[3 4 2]
所有的艰苦工作都在功能减速机中进行

我总是将
reduce
的第一个参数标记为“累加器”,第二个参数标记为“新值”

在这种情况下,累加器必须有两部分:正在创建的向量和最后看到的数字。(请注意,如果您只希望成对出现重复项,则不需要看到最后一个数字。)

因此,
[vp]
是减速器的“累加器”部分-
v
是您正在创建的向量,
p
是前面看到的值。
n
参数是“新值”

对这4个条件的解释如下:

  • 如果
    v
    为空,只需使用新值创建一个新向量并记录 新的价值观。这是必要的,因为窥视一个空向量 (下一个条件)将导致异常
  • 如果
    v
    的最后一个值与新值相同,请将其删除并记录 新的价值观
  • 如果新值(
    n
    )与上一个值(
    p
    )相同,则忽略 信息技术这种情况使我们能够消除重复的值 好几次
  • 如果这些条件都不是真的,则可以附加值:)

  • 另一种方法是:

    (defn remove-consecutive-duplicates [inp]
        (let [intm (into [] (partition-by identity inp))]
                 (reduce (fn [acc n]
                    (if (= 1 (count n))
                      (conj acc n)
                      acc ))
                  [] intm)))
    

    请注意,重复数据消除不会提供所需的输出。

    这与@amalloy的答案完全相同,但它使用
    传感器
    而不是线程宏(
    ->

    它应该更高效,至少在大型集合上是如此

    partitionbyidentity
    将coll划分为相同元素的子列表。
    remove next
    会删除
    next
    不是nil的所有子列表(即它们有多个元素)。最后一个
    map first
    获取每个子列表的第一个元素,从而将列表列表展平为元素列表


    只需分别运行每个步骤,看看它是如何工作的。

    基本上是从头开始构建的,您可以通过分解来完成大部分工作:

    (defn no-dupes [coll]
      (when-let [[x & [y & ys :as xs]] (seq coll)]
        (if xs
          (if (= x y)
            (recur (drop-while #(= x %) ys))
            (lazy-seq (cons x (no-dupes xs))))
          (list x))))
    
    话虽如此,我在这几行中犯了两个错误,但显然是正确的


    这比你的速度快吗


    对。它的速度大约是3倍:根据Criterium的数据,大约500纳秒,而这个简短的例子大约是1.5微秒

    使用传感器,如@amitr的解决方案,但IMHO,稍微清洁一点:

    (def isolate
      (comp
       (partition-by identity)
       (remove next)
       cat))
    

    然后,它可以与
    序列一起使用
    进入
    ,您希望的任何传感器接收功能。

    对于
    [1 1 3 2 4 5 2 5]
    ,输出应该是什么?(最后一个
    2
    是否因为序列前面有重复的
    2
    而被丢弃?还是因为没有另一个
    2
    作为邻居而保留?)输出应为[3 4 2]否。它很容易解释,并且使用的函数很容易查找。如果堆栈溢出不需要这样的答案,欢迎他们将其删除。或者将
    展平
    ,而不是
    (映射优先)
    @fl00r否,这将是非常错误的。尝试输入
    [[12][12]]
    @amalloy,但是
    (下一步删除)
    已经删除了所有非单一序列,不是吗?@fl00r是的,我们有一个单一序列列表。但是,该单例列表中的项本身可能是一个列表,您不应该将其展平。不过,我的示例输入是错误的:它应该没有重复项<代码>(按标识[[12]]划分)
    ,例如,生成
    ([12]))
    remove next
    不会更改它,但是我们想要的结果是
    ([12])
    (即,输入保持不变)<代码>首先映射
    会产生这种效果,但是使用
    展平
    会产生
    (12)
    ,这是一种过度的展平。
    重复数据消除
    ,而不是
    重复数据消除
    (defn isolate [coll]
      (transduce
       (comp
       (partition-by identity)
       (remove next)
       (map first))
      conj coll))
    
    (defn no-dupes [coll]
      (when-let [[x & [y & ys :as xs]] (seq coll)]
        (if xs
          (if (= x y)
            (recur (drop-while #(= x %) ys))
            (lazy-seq (cons x (no-dupes xs))))
          (list x))))
    
    (def isolate
      (comp
       (partition-by identity)
       (remove next)
       cat))