clojure:根据值的序列划分序列
clojure: partition a seq based on a seq of values
我想根据值的序列对序列进行分区
(partition-by-seq [3 5] [1 2 3 4 5 6])
((1 2 3)(4 5)(6))
第一个输入是一系列分割点。
第二个输入是我想要分区的序列。
因此,第一个列表将按值 3 (1 2 3) 进行分区,第二个分区将是 (4 5),其中 5 是下一个拆分点。
另一个例子:
(partition-by-seq [3] [2 3 4 5])
result: ((2 3)(4 5))
(partition-by-seq [2 5] [2 3 5 6])
result: ((2)(3 5)(6))
给定:第一个序列(分割点)始终是第二个输入序列的子集。
这是我想出的解决方案。
(def a [1 2 3 4 5 6])
(def p [2 4 5])
(defn partition-by-seq [s input]
(loop [i 0
t input
v (transient [])]
(if (< i (count s))
(let [x (split-with #(<= % (nth s i)) t)]
(recur (inc i) (first (rest x)) (conj! v (first x))))
(do
(conj! v t)
(filter #(not= (count %) 0) (persistent! v))))))
(partition-by-seq p a)
要划分的序列是 splittee
并且分裂点的元素(又名 splitter
)标记划分的最后一个元素。
根据您的示例:
分裂者:[1 2 3 4 5 6]
分离器:[3 5]
结果:((1 2 3)(4 5)(6))
因为分割结果总是递增整数序列,递增整数序列x
可以定义为start <= x < end
,拆分器元素可以转化为end
序列根据定义。
所以,从 [3 5]
开始,我们想找到以 4
和 6
.
结尾的子序列
然后加上start
,splitter
可以转化为[start end]
的序列。 splittee的start
和end
也用到了
所以,拆分器 [3 5]
则变为:
[[1 4] [4 6] [6 7]]
拆分转换可以这样完成
(->> (concat [(first splittee)]
(mapcat (juxt inc inc) splitter)
[(inc (last splittee))])
(partition 2)
转换后的 splitter
和期望的结果之间有很好的对称性。
[[1 4] [4 6] [6 7]]
((1 2 3) (4 5) (6))
那么问题就变成了如何提取 splittee
内的子序列,这些子序列在变换后的拆分器
内由 [start end]
排列
clojure 具有 subseq
函数,可用于根据 start
和 end
条件在有序序列中查找子序列。我可以为 transformed-splitter
的每个元素映射 splittee 的子序列
(map (fn [[x y]]
(subseq (apply sorted-set splittee) <= x < y))
transformed-splitter)
结合以上步骤,我的答案是:
(defn partition-by-seq
[splitter splittee]
(->> (concat [(first splittee)]
(mapcat (juxt inc inc) splitter)
[(inc (last splittee))])
(partition 2)
(map (fn [[x y]]
(subseq (apply sorted-set splittee) <= x < y)))))
我想到了这个懒惰且相当 (IMO) 直截了当的解决方案。
(defn part-seq [splitters coll]
(lazy-seq
(when-let [s (seq coll)]
(if-let [split-point (first splitters)]
; build seq until first splitter
(let [run (cons (first s) (take-while #(<= % split-point) (next s)))]
; build the lazy seq of partitions recursively
(cons run
(part-seq (rest splitters) (drop (count run) s))))
; just return one partition if there is no splitter
(list coll)))))
如果分割点都在序列中:
(part-seq [3 5 8] [0 1 2 3 4 5 6 7 8 9])
;;=> ((0 1 2 3) (4 5) (6 7 8) (9))
如果有分割点不在序列中
(part-seq [3 5 8] [0 1 2 4 5 6 8 9])
;;=> ((0 1 2) (4 5) (6 8) (9))
带有一些用于拆分器的无限序列和要拆分的序列的示例。
(take 5 (part-seq (iterate (partial + 3) 5) (range)))
;;=> ((0 1 2 3 4 5) (6 7 8) (9 10 11) (12 13 14) (15 16 17))
我想根据值的序列对序列进行分区
(partition-by-seq [3 5] [1 2 3 4 5 6])
((1 2 3)(4 5)(6))
第一个输入是一系列分割点。 第二个输入是我想要分区的序列。 因此,第一个列表将按值 3 (1 2 3) 进行分区,第二个分区将是 (4 5),其中 5 是下一个拆分点。
另一个例子:
(partition-by-seq [3] [2 3 4 5])
result: ((2 3)(4 5))
(partition-by-seq [2 5] [2 3 5 6])
result: ((2)(3 5)(6))
给定:第一个序列(分割点)始终是第二个输入序列的子集。
这是我想出的解决方案。
(def a [1 2 3 4 5 6])
(def p [2 4 5])
(defn partition-by-seq [s input]
(loop [i 0
t input
v (transient [])]
(if (< i (count s))
(let [x (split-with #(<= % (nth s i)) t)]
(recur (inc i) (first (rest x)) (conj! v (first x))))
(do
(conj! v t)
(filter #(not= (count %) 0) (persistent! v))))))
(partition-by-seq p a)
要划分的序列是 splittee
并且分裂点的元素(又名 splitter
)标记划分的最后一个元素。
根据您的示例:
分裂者:[1 2 3 4 5 6]
分离器:[3 5]
结果:((1 2 3)(4 5)(6))
因为分割结果总是递增整数序列,递增整数序列x
可以定义为start <= x < end
,拆分器元素可以转化为end
序列根据定义。
所以,从 [3 5]
开始,我们想找到以 4
和 6
.
然后加上start
,splitter
可以转化为[start end]
的序列。 splittee的start
和end
也用到了
所以,拆分器 [3 5]
则变为:
[[1 4] [4 6] [6 7]]
拆分转换可以这样完成
(->> (concat [(first splittee)]
(mapcat (juxt inc inc) splitter)
[(inc (last splittee))])
(partition 2)
转换后的 splitter
和期望的结果之间有很好的对称性。
[[1 4] [4 6] [6 7]]
((1 2 3) (4 5) (6))
那么问题就变成了如何提取 splittee
内的子序列,这些子序列在变换后的拆分器
[start end]
排列
clojure 具有 subseq
函数,可用于根据 start
和 end
条件在有序序列中查找子序列。我可以为 transformed-splitter
(map (fn [[x y]]
(subseq (apply sorted-set splittee) <= x < y))
transformed-splitter)
结合以上步骤,我的答案是:
(defn partition-by-seq
[splitter splittee]
(->> (concat [(first splittee)]
(mapcat (juxt inc inc) splitter)
[(inc (last splittee))])
(partition 2)
(map (fn [[x y]]
(subseq (apply sorted-set splittee) <= x < y)))))
我想到了这个懒惰且相当 (IMO) 直截了当的解决方案。
(defn part-seq [splitters coll]
(lazy-seq
(when-let [s (seq coll)]
(if-let [split-point (first splitters)]
; build seq until first splitter
(let [run (cons (first s) (take-while #(<= % split-point) (next s)))]
; build the lazy seq of partitions recursively
(cons run
(part-seq (rest splitters) (drop (count run) s))))
; just return one partition if there is no splitter
(list coll)))))
如果分割点都在序列中:
(part-seq [3 5 8] [0 1 2 3 4 5 6 7 8 9])
;;=> ((0 1 2 3) (4 5) (6 7 8) (9))
如果有分割点不在序列中
(part-seq [3 5 8] [0 1 2 4 5 6 8 9])
;;=> ((0 1 2) (4 5) (6 8) (9))
带有一些用于拆分器的无限序列和要拆分的序列的示例。
(take 5 (part-seq (iterate (partial + 3) 5) (range)))
;;=> ((0 1 2 3 4 5) (6 7 8) (9 10 11) (12 13 14) (15 16 17))