2014-01-21 95 views
1

以下是一些值。每个都是一系列的升序(或其他分组)值。在Clojure中分区分区?

(def input-vals [[[1 :a] [1 :b] [2 :c] [3 :d] [3 :e]] 
      [[1 :f] [2 :g] [2 :h] [2 :i] [3 :j] [3 :k]] 
      [[1 :l] [3 :m]]]) 

我可以按价值分割它们。

=> (map (partial partition-by first) input-vals) 
    ((([1 :a] [1 :b]) ([2 :c]) ([3 :d] [3 :e])) (([1 :f]) ([2 :g] [2 :h] [2 :i]) ([3 :j] [3 :k])) (([1 :l]) ([3 :m]))) 

但是,我得到了3个分区序列。我想要一个单独的分区组序列。

我想要做的是返回单个惰性序列(可能)是连接的各个分区的惰性序列。例如我想产生这样:

((([1 :a] [1 :b] [1 :f] [1 :l]) ([2 :c] [2 :g] [2 :h] [2 :i]) ([3 :d] [3 :e] [3 :j] [3 :k] [3 :m]))) 

请注意,并非所有的值出现在所有序列(有第三矢量没有2)。

这当然是我的问题的简化。真实的数据是来自非常大的文件的一组惰性流,所以什么都不能实现。但我认为上述问题的解决方案是解决我的问题。

随意编辑标题,我不太确定如何表达它。

+0

您是否知道您改变了多少问题? :p – Chiron

+0

我改变了内容,但没有改变我正在寻找的功能的应用。 – Joe

+0

感谢您的耐心人。我试图通过使用简单的值来尽可能简化问题。 (也造成了repl中的拼写错误)。我试图实现的目标没有改变,但凯龙的答案使用了身份,这意味着我必须证明用于分区的投影的价值(在本例中为“第一”)具有共同的价值,但价值他们自己('[1:a]')是相互独特的。 – Joe

回答

2

试试这个恐怖:

(defn partition-many-by [f comp-f s] 
    (let [sorted-s (sort-by first comp-f s) 
     first-list (first (drop-while (complement seq) sorted-s)) 
     match-val (f (first first-list)) 
     remains (filter #(not (empty? %)) 
         (map #(drop-while (fn [ss] (= match-val (f ss))) %) 
          sorted-s))] 
    (when match-val 
     (cons 
     (apply concat 
      (map #(take-while (fn [ss] (= match-val (f ss))) %) 
       sorted-s)) 
     (lazy-seq (partition-many-by f comp-f remains)))))) 

它也可能会被提高,以除去双重值检查(take-while和drop-while)。

用法示例:

(partition-many-by identity [[1 1 1 1 2 2 3 3 3 3] [1 1 2 2 2 2 3] [3]]) 

=> ((1 1 1 1 1 1) (2 2 2 2 2 2) (3 3 3 3 3 3)) 
+0

非常感谢Karl Jonathan Ward。 – Joe

+0

这不是_quite_ work,例如'(partition-many-by-identity [[0 2 4 6 8 10] [0 3 6 9 12] [0 5 10 15]])=>((0 0 0)(2)(4)(6) (8)(10)(3)(6)(9)(12)(5)(10)(15)),而是应该将两个6和两个10合在一起。 –

+0

对。但我想这取决于确切的需要 - 分组更重要还是隔离?我喜欢你的想法,下面是一个lazy-merge-by,但是这确实需要分区元素是可订购的以及可分隔的。例如那么情况如何: –

1

我不知道我是否以下,但你可以faltten结果顺序是这样的:

(flatten (partition-by identity (first input-vals))) 

clojure.core /扁平
([X])
接受任何顺序事物(列表,向量,
等)的嵌套组合,并将其内容作为单个平坦序列返回。
(flatten nil)返回一个空序列。

你可以使用实现吗?函数来测试一个序列是否懒惰。

+0

这几乎是我想要的。我会澄清我的问题(我认为你的答案仍然适用)。但它是否懒惰? – Joe

+0

@Joe我编辑了我的帖子 – Chiron

+0

平展会消除输入的所有内部结构,只留下一个平坦的数字和关键字序列。 – noisesmith

1
user> (def desired-result '((([1 :a] [1 :b] [1 :f] [1 :l]) 
          ([2 :c] [2 :g] [2 :h] [2 :i]) 
          ([3 :d] [3 :e] [3 :j] [3 :k] [3 :m])))) 
#'user/desired-result 

user> (def input-vals [[[1 :a] [1 :b] [2 :c] [3 :d] [3 :e]] 
         [[1 :f] [2 :g] [2 :h] [2 :i] [3 :j] [3 :k]] 
         [[1 :l] [3 :m]]]) 
#'user/input-vals 

user> (= desired-result (vector (vals (group-by first (apply concat input-vals))))) 
true 

我改变了输入丘壑稍微纠正什么,我认为是一个拼写错误,如果它是不是一个错误,我可以更新我的代码,以适应结构比较松散。

使用->>(线程最后)宏,我们可以有相当的代码更可读的形式:

user> (= desired-result 
     (->> input-vals 
      (apply concat) 
      (group-by first) 
      vals 
      vector)) 
true 
+0

谢谢,看起来很有希望。我会试一试。 – Joe

+0

备注:你不能指望所有的项目都会被正确分组,并且也会达到懒惰。如果你认真思考,这两个愿望是矛盾的(除非你对未来投入的结构具有一些先验知识,并围绕该结构构建你的代码)。 – noisesmith

+0

除非我误解了你,'partition-by'确实不是吗?正如我在顶部所说的,输入是在它们的流中排序的。 – Joe

2

让我们把这个有趣和使用无限长度的序列为我们输入

(def twos (iterate #(+ 2 %) 0)) 
(def threes (iterate #(+ 3 %) 0)) 
(def fives (iterate #(+ 5 %) 0)) 

我们需要懒洋洋地合并。我们要求一个比较器,以便我们也可以应用于其他数据类型。

(defn lazy-merge-by 
([compfn xs ys] 
    (lazy-seq 
    (cond 
     (empty? xs) ys 
     (empty? ys) xs 
     :else (if (compfn (first xs) (first ys)) 
       (cons (first xs) (lazy-merge-by compfn (rest xs) ys)) 
       (cons (first ys) (lazy-merge-by compfn xs (rest ys))))))) 
    ([compfn xs ys & more] 
    (apply lazy-merge-by compfn (lazy-merge-by compfn xs ys) more))) 

测试

(take 15 (lazy-merge-by < twos threes fives)) 
;=> (0 0 0 2 3 4 5 6 6 8 9 10 10 12 12) 

我们可以(懒惰)由值分区如果需要

(take 10 (partition-by identity (lazy-merge-by < twos threes fives))) 
;=> ((0 0 0) (2) (3) (4) (5) (6 6) (8) (9) (10 10) (12 12)) 

现在,回到样品输入

(partition-by first (apply lazy-merge-by #(<= (first %) (first %2)) input-vals)) 
;=> (([1 :a] [1 :b] [1 :f] [1 :l]) ([2 :c] [2 :g] [2 :h] [2 :i]) ([3 :d] [3 :e] [3 :j] [3 :k] [3 :m])) 

根据需要少一个多余的一组外部括号。

+0

非常感谢,我会尝试。 – Joe

0
(partition-by first (sort-by first (mapcat identity input-vals))) 
+0

谢谢,但我不认为'排序'是懒惰的。 – Joe

+0

yeh我想我不明白这个问题 - 如果你想懒惰地分组,你怎么能确定你已经看到了给定组的所有项目,如果他们中的一些可能在(你的顺序的其余部分我还没有意识到? – Hendekagon

+0

因为我知道输入文件中的项目是按顺序分组的,在这种情况下,它们是按照日期顺序递增的,而且是按日期分区的 – Joe