clojure:根据seq值对seq进行分区

时间:2015-03-17 14:56:45

标签: clojure split clojurescript partition partition-by

我想根据值的序列

对seq进行分区
(partition-by-seq [3 5] [1 2 3 4 5 6]) 
((1 2 3)(4 5)(6))

第一个输入是一系列分裂点。 第二个输入是我要分区的seq。 因此,第一个列表将被分区为值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))

给定:第一个seq(分裂点)始终是第二个输入序列的子集。

3 个答案:

答案 0 :(得分:1)

要分区的序列是splittee,分裂点的元素(又名。splitter)标记分区的最后一个元素。

来自你的例子:

分裂者:[1 2 3 4 5 6]

拆分器:[3 5]

结果:((1 2 3)(4 5)(6))

因为生成的分区始终是递增的整数序列,并且x的递增整数序列可以定义为start <= x < end,所以可以将拆分器元素转换为序列的end。定义。

所以,从[3 5]开始,我们希望找到以46结尾的子序列。

然后通过添加startsplitter可以转换为[start end]的序列。也使用了分离对象的startend

因此,分离器[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函数,可用于按startend条件查找有序序列内的子序列。我可以为变换分裂器

的每个元素映射spltee子项
(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)))))

答案 1 :(得分:1)

我想出了这个懒惰而且非常简单的解决方案。

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

答案 2 :(得分:0)

这是我提出的解决方案。

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