break-on-gaps - just curious if there is a more idiomatic way to do this

60 views
Skip to first unread message

qhfgva

unread,
Sep 28, 2011, 2:39:33 PM9/28/11
to Clojure
I've been working on problems from "Programming Challenges" (Skiena)
to learn clojure. As part of a problem I developed the following
routine. I sort of scare myself how natural thinking in reduce is
getting, but I was wondering if there is a more clever/idiomatic way
to solve this problem.

(defn break-on-gaps [minutes]
(reduce (fn [acc x]
(if (empty? acc)
[[x]]
(if (= (inc (last (last acc))) x)
(conj (vec (butlast acc))
(conj (last acc) x))
(conj acc [x]))))
[]
minutes))

user=> (break-on-gaps [1 2 3 5 6 7 8 10 20 21])
[[1 2 3] [5 6 7 8] [10] [20 21]]

Nathan Sorenson

unread,
Sep 28, 2011, 4:00:07 PM9/28/11
to clo...@googlegroups.com
If you were feeling so inclined, you could structure this as a lazy sequence (like 'partition' does)

(defn lazy-break
  [coll]
  (letfn [(break-paired [pairs]
            (lazy-seq
             (when-let [s (seq pairs)]
               (let [p (doall (take-while (fn [[a b]] (= (inc a) b)) pairs))
                     cutpoint (count p)]
                 (cons (concat p [(nth pairs cutpoint)])
                       (break-paired (drop (inc cutpoint) pairs)))))))]
    (map #(map first %) (break-paired (map vector coll (rest coll))))))

user> (take 2 (lazy-break (concat (range 4) (range 3) (range))))
((0 1 2 3) (0 1 2))

Alan Malloy

unread,
Sep 28, 2011, 5:07:13 PM9/28/11
to Clojure
I wrote a generalized version of this called partition-between, which
you can see at https://github.com/flatland/useful/blob/develop/src/useful/seq.clj#L181
if you're interested. Using that as a primitive, your break-on-gaps
function is simple:

user> (partition-between (fn [[a b]] (not= a (dec b))) [1 2 3 5 6 7 8
10 20 21])
([1 2 3] [5 6 7 8] [10] [20 21])

qhfgva

unread,
Sep 29, 2011, 1:58:40 PM9/29/11
to Clojure
Thanks. That's helps me think about when/how to use lazy-seq

qhfgva

unread,
Sep 29, 2011, 1:59:39 PM9/29/11
to Clojure
Nice. Also this makes me think that my clojure intuitions are getting
better.

On Sep 28, 3:07 pm, Alan Malloy <a...@malloys.org> wrote:
> I wrote a generalized version of this called partition-between, which
> you can see athttps://github.com/flatland/useful/blob/develop/src/useful/seq.clj#L181
> if you're interested. Using that as a primitive, your break-on-gaps
> function is simple:
>
> user> (partition-between (fn [[a b]] (not= a (dec b))) [1 2 3 5 6 7 8
> 10 20 21])
> ([1 2 3] [5 6 7 8] [10] [20 21])
>

Thierry Pirot

unread,
Oct 3, 2011, 1:03:50 PM10/3/11
to clo...@googlegroups.com
qhfgva, 2011-09-28 20:39 +0200

> I was wondering if there is a more clever/idiomatic way
> to solve this problem.
>
> (defn break-on-gaps [minutes]
> (reduce (fn [acc x]
> (if (empty? acc)
> [[x]]
> (if (= (inc (last (last acc))) x)
> (conj (vec (butlast acc))
> (conj (last acc) x))
> (conj acc [x]))))
> []
> minutes))
>
> user=> (break-on-gaps [1 2 3 5 6 7 8 10 20 21])
> [[1 2 3] [5 6 7 8] [10] [20 21]]

Instead of folding from left with reduce
you might consider the pros and cons of folding from right
with

(defn foldr [f f0 s]
(if (empty? s)
f0
(f (first s) (foldr f f0 (rest s)))))

then

(defn break-on-gaps [minutes]
(foldr (fn [x acc]
(if (= (inc x) (ffirst acc))
(cons (cons x (first acc)) (rest acc))
(cons (cons x ()) acc)))
()
minutes))

--
Take it Easy Don't Hurry Be Happy

Thierry

�������o�o��������o�o��������o�o��������o�o��������o�o�������

Reply all
Reply to author
Forward
0 new messages