Clojure partition by filter
在Scala中,partition方法将一个序列分为两个单独的序列-谓词为true的序列和为false的序列:
1 2 | scala> List(1, 5, 2, 4, 6, 3, 7, 9, 0, 8).partition(_ % 2 == 0) res1: (List[Int], List[Int]) = (List(2, 4, 6, 0, 8),List(1, 5, 3, 7, 9)) |
注意,Scala实现仅遍历该序列一次。
在Clojure中,
1 2 | user=> (partition-by #(= 0 (rem % 2)) [1, 5, 2, 4, 6, 3, 7, 9, 0, 8]) ((1 5) (2 4 6) (3 7 9) (0 8)) |
而
1 2 | user=> (split-with #(= 0 (rem % 2)) [1, 5, 2, 4, 6, 3, 7, 9, 0, 8]) [() (1 5 2 4 6 3 7 9 0 8)] |
是否有内置的Clojure函数与Scala
我相信您正在寻找的功能是clojure.core / group-by。它返回键映射到原始序列中的项目列表,分组功能为其返回该键。如果您使用产生真/假的谓词,则会得到您要查找的拆分。
1 2 | user=> (group-by even? [1, 5, 2, 4, 6, 3, 7, 9, 0, 8]) {false [1 5 3 7 9], true [2 4 6 0 8]} |
如果看一下实现,它将满足您的要求,即仅使用一次通过。另外,它使用引擎盖下的瞬变信号,因此它应该比到目前为止发布的其他解决方案更快。一个警告是,您应该确定分组功能正在生成的键。如果生成的是
关于
clojure.contrib.seq-utils的一部分:
1 2 3 4 | user> (use '[clojure.contrib.seq-utils :only [separate]]) nil user> (separate even? [1, 5, 2, 4, 6, 3, 7, 9, 0, 8]) [(2 4 6 0 8) (1 5 3 7 9)] |
请注意,Jürgen,Adrian和Mikera的答案都遍历了输入序列两次。
1 2 3 4 5 6 7 8 | (defn single-pass-separate [pred coll] (reduce (fn [[yes no] item] (if (pred item) [(conj yes item) no] [yes (conj no item)])) [[] []] coll)) |
单次通过只能是渴望。懒惰必须经过两遍,再加上微弱地抓住头部。
编辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | (defn lazy-single-pass-separate [pred coll] (let [coll (atom coll) yes (atom clojure.lang.PersistentQueue/EMPTY) no (atom clojure.lang.PersistentQueue/EMPTY) fill-queue (fn [q] (while (zero? (count @q)) (locking coll (when (zero? (count @q)) (when-let [s (seq @coll)] (let [fst (first s)] (if (pred fst) (swap! yes conj fst) (swap! no conj fst)) (swap! coll rest))))))) queue (fn queue [q] (lazy-seq (fill-queue q) (when (pos? (count @q)) (let [item (peek @q)] (swap! q pop) (cons item (queue q))))))] [(queue yes) (queue no)])) |
这很懒,您可以获得:
1 2 3 4 5 6 7 8 9 10 11 12 | user=> (let [[y n] (lazy-single-pass-separate even? (report-seq))] (def yes y) (def no n)) #'user/no user=> (first yes) ">0<" 0 user=> (second no) ">1<" ">2<" ">3<" 3 user=> (second yes) 2 |
综上所述,我会说"渴望"或"两次通过"。
也许请参见https://github.com/amalloy/clojure-useful/blob/master/src/useful.clj#L50-是否两次遍历序列取决于您"遍历序列"的含义。
编辑:现在我不在手机上,我想链接而不是粘贴是愚蠢的:
1 2 3 4 5 6 7 | (defn separate [pred coll] (let [coll (map (fn [x] [x (pred x)]) coll)] (vec (map #(map first (% second coll)) [filter remove])))) |
编写一些有用的技巧并不难:
1 2 3 4 5 6 7 8 9 | (defn partition-2 [pred coll] ((juxt (partial filter pred) (partial filter (complement pred))) coll)) (partition-2 even? (range 10)) => [(0 2 4 6 8) (1 3 5 7 9)] |