There's a partition-by in clojure.contrib.seq-utils, but I don't
see how to use it to build what you want. Here's what I came up
with:
(defn partition-when [f s]
(->> (iterate rest s)
(take-while seq)
(filter (comp f first))
(map (fn [[x & xs]] (cons x (take-while (complement f) xs))))))
user=> (partition-when odd? [1 2 3 4 6 8 9 11 13 14 15])
((1 2) (3 4 6 8) (9) (11) (13 14) (15))
I imagine someone can come up with something more elegant. Also,
this drops everything at the front of the seq until the predicate
matches, which may or may not be when you want:
user=> (partition-when even? [1 2 3 4 6 8 9 11 13 14 15])
((2 3) (4) (6) (8 9 11 13) (14 15))
--Chouser
In the meantime, I came up with the following, which seems to work.
I'm sure it can be improved.
(defn NOT [pred] (fn [x] (not (pred x))))
...
Which leads me to another question, are there standard functions
sitting around somewhere already to do boolean combinations of
predicates (conjoin, disjoin, negate perhaps?)? Like my NOT above.
I imagine someone can come up with something more elegant.
(defn partition-when [f s]
(loop [p [[]], [x & xs :as s] s]
(cond (empty? s) p
(f x) (recur (conj p [x]) xs)
:else (recur (conj (pop p) (conj (peek p) x)) xs))))
Maybe a bit simpler ...or maybe not. Not lazy at all. Behaves
a little differently than my previous:
user=> (partition-when odd? [1 2 3 4 6 8 9 11 13 14 15])
[[] [1 2] [3 4 6 8] [9] [11] [13 14] [15]]
user=> (partition-when even? [1 2 3 4 6 8 9 11 13 14 15])
[[1] [2 3] [4] [6] [8 9 11 13] [14 15]]
Note that the first vector never starts with an item that matches
the predicate -- it's a little bin in which all non-matching
items are dumped first.
--Chouser
(defn partition-when [pred coll]
(when (seq coll)
(lazy-seq
(let [[x xs] (split-at (or (some (fn [[i elt]]
(and (pos? i)
(pred elt)
i))
(indexed coll))
(count coll))
coll)]
(cons x (partition-when pred xs))))))
(partition-when odd? [])
=> nil
(partition-when odd? [1 2 3 4 6 8 9 11 13 14 15])
=> ((1 2) (3 4 6 8) (9) (11) (13 14) (15))
(partition-when even? [1 2 3 4 6 8 9 11 13 14 15])
=> ((1) (2 3) (4) (6) (8 9 11 13) (14 15))
;; Laziness
(take 10 (my-partition odd? (iterate inc 1)))
=> ((1 2) (3 4) (5 6) (7 8) (9 10) (11 12) (13 14) (15 16) (17 18) (19 20))
Cheers,
Mark
Chouser <cho...@gmail.com> writes:
--
Mark Triggs
<mark.h...@gmail.com>
Like Mark's but using split-with instead of split-at:
(defn partition-when [pred coll]
(lazy-seq
(when-let [[x & xs] (seq coll)]
(let [[xs ys] (split-with (complement pred) xs)]
(cons (cons x xs) (partition-when pred ys))))))
> (partition-when odd? [])
> => nil
>
> (partition-when odd? [1 2 3 4 6 8 9 11 13 14 15])
> => ((1 2) (3 4 6 8) (9) (11) (13 14) (15))
>
> (partition-when even? [1 2 3 4 6 8 9 11 13 14 15])
> => ((1) (2 3) (4) (6) (8 9 11 13) (14 15))
>
> ;; Laziness
> (take 10 (partition-when odd? (iterate inc 1)))
> Like Mark's but using split-with instead of split-at:
>
> (defn partition-when [pred coll]
> (lazy-seq
> (when-let [[x & xs] (seq coll)]
> (let [[xs ys] (split-with (complement pred) xs)]
> (cons (cons x xs) (partition-when pred ys))))))
Lovely! Much better.
--
Mark Triggs
<mark.h...@gmail.com>
Just realised this is almost the same as Warren's -- I had missed
reading Warren's before posting. Thought it might be helpful if I
explain the differences:
* lazy-seq on the outside of the conditional. This means the seq is
fully lazy, so if you never call first/next on it, nothing is ever run.
* As suggested by others complement instead of NOT.
* (when (seq coll) ...) instead of (if (empty? coll) () ...). We can
simplify things like this as (lazy-seq nil) => () and (seq ()) => nil.
* Using destructuring instead of (first coll) (next coll). Makes it a
bit shorter and is quite useful if you're going to use the result of
(first coll) or (next coll) multiple times, but in this case it doesn't
really matter.