Trying to rewrite a loop as map/reduce

150 views
Skip to first unread message

samppi

unread,
Dec 15, 2009, 2:00:28 PM12/15/09
to Clojure
I'm trying to rewrite a loop to use higher-level functions instead.
For pure functions f1, f2, f3, f4, f5, f6?, and f7, and a Clojure
object a0, how can one rewrite the following loop to use map, reduce,
etc.?

(loop [a a0]
(let [b (f1 a)
c (f2 b)
d (f3 c)
e (f4 d)
g (f5 c)
h (-> e f2 f5)]
(if (or (f6? b) (<= g h))
e
(recur (f7 d b)))))

Sean Devlin

unread,
Dec 15, 2009, 2:06:50 PM12/15/09
to Clojure
Could you re-write this w/ comp, and repost?

Zach Tellman

unread,
Dec 15, 2009, 2:33:10 PM12/15/09
to Clojure
At first glance I don't see a clean to make this completely higher-
order, but here's a shorter (albeit a little messy) version:

(loop [a a0]
(let [[a b c d e] (reduce #(conj %1 (%2 (last %1))) [a] [f1 f2 f3
f4])
g (f5 c)
h (-> e f2 f5)]
(if (or (f6? b) (<= g h))
e
(recur (f7 d b)))))


Laurent PETIT

unread,
Dec 15, 2009, 4:05:34 PM12/15/09
to clo...@googlegroups.com
Hello,

it seems to me that your example is unnecessary complicated.
Let's refactor it a bit before trying to obtain your goal.

First,

your example can be, for the purpose of your goal, simplified as :

(loop [a a0]
  (if (predicate-fn a)
    (return-fn a)
    (recur (recur-fn a))))

So now, what can we do with this ?

you keep applying function recur-fn to a, starting with a = a0.
This is a job for iterate : (iterate recur-fn a0) will create this lazy sequence starting with a0, and where each new value is made from (recur-fn a)

Then you want to stop when predicate-fn returns true.
This is a job for remove, for example : (first (remove (comp not predicate-fn) (iterate recur-fn a0)))

The final step is to apply return-fn to the result:
(return-fn
  (first (remove
                    (comp not predicate-fn)
                    (iterate recur-fn a0)))

As for return-fn, predicate-fn and recur-fn :

(def b f1)
(def c (comp f2 b))
(def d (comp f3 c))
(def e (comp f4 d))
(def g (comp f5 c))
(def h (comp f5 f2))

(def return-fn e)
(def predicate-fn #(if (or (f6? (b %)) (<= (g %) (h %))))
(def recur-fn #(f7 (d a) (b a)))

Is it something like that you expected ?


2009/12/15 samppi <rbys...@gmail.com>

--
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

Sean Devlin

unread,
Dec 15, 2009, 4:20:33 PM12/15/09
to Clojure
On Dec 15, 4:05 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> Hello,
>
> it seems to me that your example is unnecessary complicated.
> Let's refactor it a bit before trying to obtain your goal.
>
> First,
>
> your example can be, for the purpose of your goal, simplified as :
>
> (loop [a a0]
>   (if (predicate-fn a)
>     (return-fn a)
>     (recur (recur-fn a))))
>
> So now, what can we do with this ?
>
> you keep applying function recur-fn to a, starting with a = a0.
> This is a job for iterate : (iterate recur-fn a0) will create this lazy
> sequence starting with a0, and where each new value is made from (recur-fn
> a)
>
> Then you want to stop when predicate-fn returns true.
> This is a job for remove, for example : (first (remove (comp not
> predicate-fn) (iterate recur-fn a0)))
>
> The final step is to apply return-fn to the result:
> (return-fn
>   (first (remove
>                     (comp not predicate-fn)
>                     (iterate recur-fn a0)))
>

Shouldn't this be:

(return-fn
(first (filter predicate-fn (iterate recur-fn a0)))


> As for return-fn, predicate-fn and recur-fn :
>
> (def b f1)
> (def c (comp f2 b))
> (def d (comp f3 c))
> (def e (comp f4 d))
> (def g (comp f5 c))
> (def h (comp f5 f2))
>
> (def return-fn e)
> (def predicate-fn #(if (or (f6? (b %)) (<= (g %) (h %))))
> (def recur-fn #(f7 (d a) (b a)))
>
> Is it something like that you expected ?
>
> 2009/12/15 samppi <rbysam...@gmail.com>
>
> > I'm trying to rewrite a loop to use higher-level functions instead.
> > For pure functions f1, f2, f3, f4, f5, f6?, and f7, and a Clojure
> > object a0, how can one rewrite the following loop to use map, reduce,
> > etc.?
>
> >  (loop [a a0]
> >    (let [b (f1 a)
> >          c (f2 b)
> >          d (f3 c)
> >          e (f4 d)
> >          g (f5 c)
> >          h (-> e f2 f5)]
> >      (if (or (f6? b) (<= g h))
> >        e
> >        (recur (f7 d b)))))
>
> > --
> > 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<clojure%2Bunsu...@googlegroups.com>

Laurent PETIT

unread,
Dec 15, 2009, 4:32:25 PM12/15/09
to clo...@googlegroups.com
Of course you're right. I couldn't remember filter, was somehow "stuck" with some which does not do the job of course, and playing with the doc did not help since my version of clojure still has the bug on filter's lack of documentation :-)

2009/12/15 Sean Devlin <francoi...@gmail.com>

samppi

unread,
Dec 15, 2009, 9:14:25 PM12/15/09
to Clojure
Wonderful. I'm still getting used to juggling functions like this,
rather than doing standard loops. But it's so much cleaner.

Thanks again, everyone; your explanations showed me not only how to
solve my problem, but to organize my logic better too.

On Dec 15, 2:32 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> Of course you're right. I couldn't remember filter, was somehow "stuck" with
> some which does not do the job of course, and playing with the doc did not
> help since my version of clojure still has the bug on filter's lack of
> documentation :-)
>
> 2009/12/15 Sean Devlin <francoisdev...@gmail.com>
> > <clojure%2Bunsu...@googlegroups.com<clojure%252Bunsubscribe@googlegroup s.com>

Meikel Brandmeyer

unread,
Dec 15, 2009, 3:07:26 PM12/15/09
to clo...@googlegroups.com
Hi,

Am 15.12.2009 um 20:06 schrieb Sean Devlin:

> Could you re-write this w/ comp, and repost?

Because you need all intermediate results comp is totally useless here. (Unless you want to recompute everything several times, of course. But that might be prohobitive due to performance reasons...)
I don't think, that higher-order functions make this clearer... Since you are only interested in the last step of the computation, this is calling for reduce. But reduce acts on a0. Not some inner condition for looping. Another approach might be define a lazy seq of the intermediate computation steps and calling last on it. I'm not sure, that this clarifies things. I would stick with loop/recur here.

Sincerely
Meikel

DTH

unread,
Dec 15, 2009, 4:10:50 PM12/15/09
to Clojure
On Dec 15, 7:33 pm, Zach Tellman <ztell...@gmail.com> wrote:
> At first glance I don't see a clean to make this completely higher-
> order, but here's a shorter (albeit a little messy) version:
>
> (loop [a a0]
>     (let [[a b c d e] (reduce #(conj %1 (%2 (last %1))) [a] [f1 f2 f3
> f4])
>           g (f5 c)
>           h (-> e f2 f5)]
>       (if (or (f6? b) (<= g h))
>         e
>         (recur (f7 d b)))))
>

While I, too, could not spot an obvious "completely" higher order
route, you can use `clojure.contrib.seq-utils/reductions` [1] to
cleanup the `reduce` in the above:

(require '[clojure.contrib.seq-utils :as s-u])

(let [f1-4 [f1 f2 f3 f4]]
(loop [a a0]
(let [[a b c d e] (s-u/reductions #(%2 %1) a f1-4)
g (f5 c)
h (-> e f2 f5)]
(if (or (f6? b) (<= g h))
e
(recur (f7 d b))))))

-DTH

[1] http://richhickey.github.com/clojure-contrib/seq-utils-api.html#clojure.contrib.seq-utils/reductions

DTH

unread,
Dec 15, 2009, 4:28:43 PM12/15/09
to Clojure
On Dec 15, 9:05 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
>
> The final step is to apply return-fn to the result:
> (return-fn
>   (first (remove
>                     (comp not predicate-fn)
>                     (iterate recur-fn a0)))
>

Damn, well played sir; that's much cleaner. If I might offer one
small tweak:

(return-fn (some predicate-fn (iterate recur-fn a0)))

would seem equivalent, though I doubt I'd have got there without your
stepwise guide to change the way I was thinking about it.

Cheers,

-DTH

Laurent PETIT

unread,
Dec 16, 2009, 3:13:04 AM12/16/09
to clo...@googlegroups.com
Hello,

2009/12/15 DTH <dth...@gmail.com>


No, some does not work here. Dean gave the final word, I think, by reminding us of filter.

some does not work because it will return the result of applying predicate-fn to the item, but we want the item "intact" in order to apply return-fn to it.

But as said  Meikel, and as I was implicitly implying in my final words by just saying "is it something like that you expected", the loop version is not so bad with performance considerations in mind.

Cheers,

--
Laurent
 

Cheers,

-DTH

Timothy Pratley

unread,
Dec 16, 2009, 7:19:03 AM12/16/09
to Clojure
On Dec 16, 6:00 am, samppi <rbysam...@gmail.com> wrote:
> I'm trying to rewrite a loop to use higher-level functions instead.

;; Here is my 'novelty' answer... just for fun! [not a serious
answer] ;;

(defn bounce
[start pred iter]
(trampoline
(fn f [x]
(if (pred x)
x
(fn []
(f (iter x)))))
start))

(bounce 1 (partial = 5) inc)
=> 5


Meikel Brandmeyer

unread,
Dec 16, 2009, 2:09:06 AM12/16/09
to Clojure
Hi,

On Dec 15, 10:28 pm, DTH <dth...@gmail.com> wrote:

> Damn, well played sir; that's much cleaner.

Someone, please enlighten me!

Why is this clearer?

(defn foo
[a]
(let [b f1
c (comp f2 b)
d (comp f3 c)
e (comp f4 d)
g (comp f5 c)
h (comp f5 f2 e)]
(->> (iterate #(f7 (d %) (b %)) a)
(filter #(or (f6? (b %)) (<= (g %) (h %))))
first
e)))

It is more verbose than the loop. It generates 7 additional classes.
Per iteration step it calls b 5 times and c 3 times. Depending on b
and c maybe memoize should be considered, too. Why the first of the
resulting sequence, not the second? (<- The point here is: In which
way is defining a seq of uninteresting values to obtain a single one
cleaner than a loop which just returns that desired value? Maybe this
is really a fixpoint iteration?)

Sincerely
Meikel

DTH

unread,
Dec 16, 2009, 9:51:09 AM12/16/09
to Clojure
On Dec 16, 8:13 am, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> 2009/12/15 DTH <dth...@gmail.com>
> >
> > (return-fn (some predicate-fn (iterate recur-fn a0)))
>
> > would seem equivalent, though I doubt I'd have got there without your
> > stepwise guide to change the way I was thinking about it.
>
> No, some does not work here. Dean gave the final word, I think, by reminding
> us of filter.
>
> some does not work because it will return the result of applying
> predicate-fn to the item, but we want the item "intact" in order to apply
> return-fn to it.
>

Gah, of course; my toy implementation of f6? happened to return the
argument when logical true, which made it appear to work; I completely
forgot that `some` itself was not handling this. I read both Sean's
response and your note on some while both my original messages were in
moderation, and couldn't face sending another "badger, got that one
wrong..." email on the heels of the first two...

Cheers,

-Dave

Laurent PETIT

unread,
Dec 16, 2009, 10:30:21 AM12/16/09
to clo...@googlegroups.com
Hey, the exercise was to rewrite it with higher order functions, not to make it clearer !

2009/12/16 Meikel Brandmeyer <m...@kotka.de>

Meikel Brandmeyer

unread,
Dec 16, 2009, 11:26:36 AM12/16/09
to Clojure
Hi,

On Dec 16, 4:30 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:

> Hey, the exercise was to rewrite it with higher order functions, not to make
> it clearer !

Well. It was claimed it is cleaner... Just asking...

Sincerely
Meikel

Meikel Brandmeyer

unread,
Dec 17, 2009, 6:25:09 PM12/17/09
to clo...@googlegroups.com
Hi,

Am 16.12.2009 um 17:26 schrieb Meikel Brandmeyer:

> Well. It was claimed it is cleaner... Just asking...

I wrote down my thoughts in a small blog post: http://tr.im/HW5P

Sincerely
Meikel

Joseph Smith

unread,
Dec 17, 2009, 10:18:32 PM12/17/09
to clo...@googlegroups.com
What are you using to generate the pretty rainbow perens on your website?

---
Joseph Smith
j...@uwcreations.com
(402)601-5443

Meikel Brandmeyer

unread,
Dec 18, 2009, 12:29:30 AM12/18/09
to clo...@googlegroups.com
Hi,

Am 18.12.2009 um 04:18 schrieb Joseph Smith:

> What are you using to generate the pretty rainbow perens on your website?

I use Vim to do the highlighting. VimClojure does the rainbow parens.

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages