split a sequence by delimiter in clojure?
说我有一个Clojure序列,例如
1 | '(1 2 3 6 7 8) |
我想将其拆分,以便每当遇到一个可被3整除的元素时拆分列表,以便结果看起来像
1 | '((1 2) (3) (6 7 8)) |
(编辑:我真正需要的是
1 | [[1 2] [3] [6 7 8]] |
,但我也将使用序列版本:)
在Clojure中执行此操作的最佳方法是什么?
1 2 | (partition-by #(= (rem % 3) 0) '(1 2 3 6 7 8)) ; => ((1 2) (3 6) (7 8)) |
1 2 | (split-with #(not (= (rem % 3) 0)) '(1 2 3 6 7 8)) ; => [(1 2) (3 6 7 8)] |
像这样吗
1 2 3 4 5 6 7 8 9 | (defn partition-with [f coll] (lazy-seq (when-let [s (seq coll)] (let [run (cons (first s) (take-while (complement f) (next s)))] (cons run (partition-with f (seq (drop (count run) s)))))))) (partition-with #(= (rem % 3) 0) [1 2 3 6 7 8 9 12 13 15 16 17 18]) => ((1 2) (3) (6 7 8) (9) (12 13) (15 16 17) (18)) |
以及另一种基于还原的老派解决方案:
1 2 3 4 5 6 7 8 9 10 11 | user> (defn split-all [pred items] (when (seq items) (apply conj (reduce (fn [[acc curr] x] (if (pred x) [(conj acc curr) [x]] [acc (conj curr x)])) [[] []] items)))) #'user/split-all user> (split-all #(zero? (rem % 3)) '(1 2 3 6 7 8 10 11 12)) ;;=> [[1 2] [3] [6 7 8 10 11] [12]] |
这是一个有趣的问题。 我最近在图珀洛(Tupelo)库中添加了函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | (ns tst.clj.core (:use clojure.test tupelo.test) (:require [tupelo.core :as t] )) (t/refer-tupelo) (defn start-segment? [vals] (zero? (rem (first vals) 3))) (defn partition-using [pred vals-in] (loop [vals vals-in result []] (if (empty? vals) result (t/spy-let [ out-first (take 1 vals) [out-rest unprocessed] (split-using pred (spyx (next vals))) out-vals (glue out-first out-rest) new-result (append result out-vals)] (recur unprocessed new-result))))) |
给我们的输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | out-first => (1) (next vals) => (2 3 6 7 8) [out-rest unprocessed] => [[2] (3 6 7 8)] out-vals => [1 2] new-result => [[1 2]] out-first => (3) (next vals) => (6 7 8) [out-rest unprocessed] => [[] [6 7 8]] out-vals => [3] new-result => [[1 2] [3]] out-first => (6) (next vals) => (7 8) [out-rest unprocessed] => [[7 8] ()] out-vals => [6 7 8] new-result => [[1 2] [3] [6 7 8]] (partition-using start-segment? [1 2 3 6 7 8]) => [[1 2] [3] [6 7 8]] |
或更大的输入向量:
1 2 | (partition-using start-segment? [1 2 3 6 7 8 9 12 13 15 16 17 18 18 18 3 4 5]) => [[1 2] [3] [6 7 8] [9] [12 13] [15 16 17] [18] [18] [18] [3 4 5]] |
您也可以使用嵌套的
1 2 3 4 5 6 7 8 9 10 11 12 | (defn split-using "Splits a collection based on a predicate with a collection argument. Finds the first index N such that (pred (drop N coll)) is true. Returns a length-2 vector of [ (take N coll) (drop N coll) ]. If pred is never satisified, [ coll [] ] is returned." [pred coll] (loop [left [] right (vec coll)] (if (or (empty? right) ; don't call pred if no more data (pred right)) [left right] (recur (append left (first right)) (rest right))))) |
实际上,上述功能似乎将来会很有用。