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

3

我有一串整数,我想把它们分成递增的片段,并且希望尽可能少的片段。因此,我希望有

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

我已按照以下方式实现了segmentize:
(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个回答

4
你可以使用partition-by来构建这个
(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


2

这是另一种懒惰的实现方式。基本上,找出“lte”函数返回true的连续数对的数量(使用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

这是org.flatland/useful中一些序列处理函数的特殊情况,具体来说是flatland.useful.seq/partition-between

(partition-between (partial apply >) xs)

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

0

这是我的segmentize版本(我称之为split-when):

(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]]

@Alex 是的,它可能需要改进。在我看来,它不能与列表一起使用。我想找到一个简单的解决方案。 - edbond

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))))))

计算每个段落的代码可能可以使用循环/递归来减少冗余,但我通常发现reduce更易读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接