partition-when

185 views
Skip to first unread message

Frank

unread,
Mar 3, 2015, 2:46:29 PM3/3/15
to clo...@googlegroups.com
Hi all,

for some tests I need a function which starts a new partition each time a predicate returns true, for instance:

(partition-when
  (fn [s] (.startsWith s ">>"))
  [">> 1" "2" "3" ">> 4" "5" "6"])
:=> [[">> 1" "2" "3"] [">> 4" "5" "6"]]

Since I haven't found a built-in function, I copied, pasted, and modified the core function partition-by:

(defn partition-when
  [f coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [fst (first s)
           run (cons fst (take-while #(not (f %)) (next s)))]
       (cons run (partition-when f (seq (drop (count run) s))))))))

Is there a better (more idiomatic) way to achieve the same result?

Thank you in advance.

Frank

Ivan L

unread,
Mar 4, 2015, 5:19:43 PM3/4/15
to clo...@googlegroups.com
I went though almost the exact same exercise and my code is almost identical to yours.  I called it partition-every and I use it a lot.  A more determined individual might submit this for inclusion into core!

Martin Harrigan

unread,
Mar 5, 2015, 10:47:40 AM3/5/15
to clo...@googlegroups.com
Hi Frank,

I use a similar function that combines partition-by and partition-all:

(defn partition-when
  [f coll]
  (map (partial apply concat) (partition-all 2 (partition-by f coll))))

Martin.


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andy-

unread,
Mar 5, 2015, 12:11:05 PM3/5/15
to clo...@googlegroups.com
Tried myself and my first transducer for fun:

(defn partition-when
  [f]
  (fn [rf]
    (let [a (java.util.ArrayList.)
          fval (volatile! false)]
      (fn
        ([] (rf))
        ([result]
           (let [result (if (.isEmpty a)
                          result
                          (let [v (vec (.toArray a))]
                            ;;clear first!
                            (.clear a)
                            (unreduced (rf result v))))]
             (rf result)))
        ([result input]
            (if-not (and (f input)  @fval)
               (do
                 (vreset! fval true)
                 (.add a input)
                 result)
               (let [v (vec (.toArray a))]
                 (.clear a)
                 (let [ret (rf result v)]
                   (when-not (reduced? ret)
                     (.add a input))
                   ret))))))))


(into [] (partition-when
          #(.startsWith % ">>"))
          [">> 1" ">> 2" "22" ">> 3"])

Based on partition-by (on master branch). Would be interesting how fast it is compared to the other implementations.

Any comments appreciated.

Cheers


On Tuesday, March 3, 2015 at 2:46:29 PM UTC-5, Frank wrote:

Steve Miner

unread,
Mar 5, 2015, 12:48:56 PM3/5/15
to clo...@googlegroups.com
For the transducer version, I don’t think you need to keep track of the previous value (fval). The decision to start a new partition group depends only on the current item. Here’s my version. (Warning: virtually untested.)  I assume that the first item goes into the first partition no matter what so you don’t even need to call f.  Maybe you should for side-effects?

(defn partition-when
   "Applies f to each value in coll, starting a new partition each time f returns a
   true value.  Returns a lazy seq of partitions.  Returns a stateful
   transducer when no collection is provided."
  {:added "1.X"
   :static true}
  ([f]
   (fn [rf]
     (let [a (java.util.ArrayList.)]
       (fn
         ([] (rf))
         ([result]
          (let [result (if (.isEmpty a)
                         result
                         (let [v (vec (.toArray a))]
                           ;;clear first!
                           (.clear a)
                           (unreduced (rf result v))))]
            (rf result)))
         ([result input]
            (if (.isEmpty a)
              (do (.add a input)
                  result)
              (if (f input)
                (let [v (vec (.toArray a))]
                  (.clear a)
                  (let [ret (rf result v)]
                    (when-not (reduced? ret)
                      (.add a input))
                    ret))
                (do
                  (.add a input)
                  result))))))))

  ([f coll]
  (lazy-seq
   (when-let [s (seq coll)]
     (let [fst (first s)
           run (cons fst (take-while #(not (f %)) (next s)))]
       (cons run (partition-when f (seq (drop (count run) s)))))))))


— Steve
Reply all
Reply to author
Forward
0 new messages