如何在Clojure中将序列分区为增加的子序列?

时间:2014-08-04 14:36:43

标签: algorithm clojure

我有一个整数序列,我想将它们分成增加的段,我希望尽可能少的段。所以我想要

(segmentize [1 2 3 4 3 8 9 1 7] <=)
;=> [[1 2 3 4][3 8 9][1 7]]

我已按如下方式实施了分段:

(defn segmentize [col lte]
  (loop [col col s [] res []]
    (cond (empty? col) (conj res s)
          (empty? s) (recur (rest col) (conj s (first col)) res)
          (lte (last s) (first col)) (recur (rest col) (conj s (first col)) res)
          :else (recur col [] (conj res s)))))

但是我想知道是否已经有一些方便的clojure函数可以完成这个,或者是否有更惯用的方法来做到这一点。

5 个答案:

答案 0 :(得分:4)

您可以使用partition-by

构建this
(defn segmentize [cmp coll]
  (let [switch (reductions = true (map cmp coll (rest coll)))]
    (map (partial map first) (partition-by second (map list coll switch)))))

(segmentize <= [1 2 3 4 3 8 9 1 7])
;=> ((1 2 3 4) (3 8 9) (1 7))

如果您真的需要向量而不是延迟序列,则最后一行的前两个map可能会更改为mapv

答案 1 :(得分:2)

另一个懒惰的实现。基本上找出有多少连续的数字对于&#34; lte&#34; function(take-while + segment)然后用该数字拆分原始集合。重复提醒集合:

(defn segmentize
 [coll lte]
 (lazy-seq
  (when-let [s (seq coll)]
    (let [pairs-in-segment (take-while (fn [[a b]] (lte a b)) (partition 2 1 s))
          [segment reminder] (split-at (inc (count pairs-in-segment)) s)]
      (cons segment
            (segmentize reminder lte))))))

答案 2 :(得分:2)

这是org.flatland/useful中某些序列处理函数的特例,特别是flatland.useful.seq/partition-between

(partition-between (partial apply >) xs)

如果你需要一个没有外部依赖性的从头开始实现,我更喜欢dAni的答案。

答案 3 :(得分:0)

这是我的segmentize版本(我在分裂时调用):

(defn split-when [f s]
  (reduce (fn [acc [a b]]
            (if (f b a)
              (conj acc [b])
              (update-in acc [(dec (count acc))] conj b)))
          [[(first s)]]
          (partition 2 1 s)))

(split-when < [1 2 3 4 3 8 9 1 7])

;; [[1 2 3 4] [3 8 9] [1 7]]

答案 4 :(得分:0)

因为每个人都喜欢懒惰的序列:

(defn segmentize [coll cmp]
  (if-let [c (seq coll)]
    (lazy-seq 
     (let [[seg rem] (reduce (fn [[head tail] x]
                               (if (cmp (last head) x)
                                 [(conj head x) (next tail)]
                                 (reduced [head tail])))
                             [(vec (take 1 c)) (drop 1 c)]
                             (drop 1 c))]
       (cons seg (segmentize rem cmp))))))

使用loop / recur计算每个段的代码可能会稍微冗长一些,但我倾向于在大多数时候发现reduce更具可读性。