每次出现元素后拆分矢量

时间:2016-07-05 20:29:05

标签: clojure

这应该很容易,但我发现它比预期更困难。

给定[0 1 2 0 1 2 0 1],在每次出现2之后拆分序列。

结果应与[[0 1 2] [0 1 2] [0 1]]类似。

split函数仅在第一个实例中拆分。我的想象力也限于如何使用partition函数来实现这一目标。

3 个答案:

答案 0 :(得分:2)

以前的解决方案都可以(虽然@magos解决方案在某些情况下有缺陷),但如果将此函数用作实用程序(我猜这是相当普遍的),我会使用经典的迭代方法:

(defn group-loop [delim coll]
  (loop [res [] curr [] coll (seq coll)]
    (if coll
      (let [group (conj curr (first coll))]
        (if (= delim (first coll))
          (recur (conj res group) [] (next coll))
          (recur res group (next coll))))
      (if (seq curr)
        (conj res curr)
        res))))

在repl中:

user> (map (partial group-loop 2)
           [[]
            nil
            [1 2 3 1 2 3]
            [1 2 3 1 2 3 2]
            [2 1 2 3 1 2 3]
            [1 3 4 1 3 4]])

;;([] [] 
;; [[1 2] [3 1 2] [3]] 
;; [[1 2] [3 1 2] [3 2]] 
;; [[2] [1 2] [3 1 2] [3]] 
;; [[1 3 4 1 3 4]])

虽然它看起来有点过于冗长,但它仍然有一些相当重要的优点:首先它有点经典(我找到一个专业而不是骗局),第二:它很快(根据我的基准约3)比reduce变体快一倍,比partition变种快6到10倍

你也可以通过一些小的调整使它变得更加狡猾,像clojure的序列操作函数那样返回懒惰的集合:

(defn group-lazy [delim coll]
  (loop [curr [] coll coll]
    (if (seq coll)
      (let [curr (conj curr (first coll))]
        (if (= delim (first coll))
          (cons curr (lazy-seq (group-lazy delim (rest coll))))
          (recur curr (next coll))))
      (when (seq curr) [curr]))))

user> (map (partial group-lazy 2)
           [[]
            nil
            [1 2 3 1 2 3]
            [1 2 3 1 2 3 2]
            [2 1 2 3 1 2 3]
            [1 3 4 1 3 4]])

;;(nil nil 
;; ([1 2] [3 1 2] [3]) 
;; ([1 2] [3 1 2] [3 2]) 
;; ([2] [1 2] [3 1 2] [3]) 
;; [[1 3 4 1 3 4]])

答案 1 :(得分:1)

这是通过组合两个partition变体的一种方式。首先使用partition-by除以2的实例,然后使用partition-all将这些分区中的两个和两个分开,并使用concat将它们连接在一起。

(->> [0 1 2 0 1 2 0 1]
     (partition-by (partial = 2))               ;;((0 1) (2) (0 1) (2) (0 1))
     (partition-all 2)                          ;;(((0 1) (2)) ((0 1) (2)) ((0 1)))
     (mapv (comp vec (partial reduce concat)))) ;;[[0 1 2] [0 1 2] [0 1]]

虽然请注意,如果输入从2开始,则返回的分区也将以2开始,而不是像这里那样结束。

答案 2 :(得分:0)

在这里,按照所有输入的要求工作:

reduce

虽然Magos有一个优雅的解决方案,但遗憾的是,他提到的并不完整。因此,上述内容应该使用2来完成工作。

我们看一下最近添加的元素。如果是(conj %1 [%2]),我们会创建一个新的子向量(partition)。否则,我们将它添加到最后一个子矢量。真的很简单。像splitfullscreenPlayPauseButton.style.backgroundImage = "url('../images/pause.png')"; 这样的现有函数非常适合在可能的情况下重用,但有时最好的解决方案是自定义函数,在这种情况下它实际上非常干净。