我有一个整数序列,我想将它们分成增加的段,我希望尽可能少的段。所以我想要
(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函数可以完成这个,或者是否有更惯用的方法来做到这一点。
答案 0 :(得分: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
。
答案 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
更具可读性。