Thanks, this was very helpful.  I am pretty new at Clojure (and
functional programming) - I'm just tinkering with it a bit in my spare
time these days, with an eye towards integrating it into a larger
project once I'm a little more comfortable with it.

Just to follow up on a few things - by SICP, do you mean the book
"Structure and Interpretation of Computer Programs"?  That's the most
likely match I could find.  I'm not familiar with the book, though, so
I might have another reading project.

So, map et. al let me tandem-iterate multiple sequences?  I had an
early implementation that did basically that using the 'for' list
comprehension, but I couldn't figure out how to add an element to my
result for some steps in the iteration and not others.  The early
implementation got around this because I was actually writing a method
that would count matching elements, but couldn't return them.  It
looked something like this:

(defn count-matching-elements [coll1 coll2]
  (reduce +
    (for [e1 coll1 e2 coll2]
      (if (= e1 e2) 1 0))))

I wanted instead to re-implement this solution as:

(defn count-matching-elements [coll1 coll2] (count (matching-elements
coll1 coll2)))

This was partially for my own debugging purposes, so I could see what
things were being counted.


On Oct 14, 12:23 am, John Harrop <jharrop...@gmail.com> wrote:
> On Wed, Oct 14, 2009 at 2:06 AM, Mark Tomko <mjt0...@gmail.com> wrote:
>
> > This is basically a problem of parallel iteration.  I've devised 2
> > solutions, one is recursive (alas, not tail-recursive) and the other
> > appears to be a more idiomatic Clojure solution.  Can someone suggest
> > a more efficient or cleaner solution?
>
> Meikel provided a quickie high-level-function solution, but I thought I
> might take a look at this anyway:
>
> Here's the "literal" solution:
>
>
>
> > (defn matching-elements [coll1 coll2]
> >  (if (or (empty? coll1) (empty? coll2)) ()
> >    (let [e1 (first coll1) e2 (first coll2)]
> >      (if (= e1 e2) (cons e1 (matching-elements (rest coll1) (rest
> > coll2)))
> >        (matching-elements (rest coll1) (rest coll2))))))
>
> First of all, the second matching-elements call is in tail position, so you
> can improve this to:
>
> (defn matching-elements [coll1 coll2]
>   (if (or (empty? coll1) (empty? coll2))
>     ()
>     (let [e1 (first coll1) e2 (first coll2)]
>       (if (= e1 e2)
>         (cons e1 (matching-elements (rest coll1) (rest coll2)))
>         (recur (rest coll1) (rest coll2))))))
>
> But it can be made fully tail-recursive:
>
> (defn- matching-elements* [coll1 coll2 res]
>   (if (or (empty? coll1) (empty? coll2))
>     res
>     (let [e1 (first coll1) e2 (first coll2)]
>       (if (= e1 e2)
>         (recur (rest coll1) (rest coll2) (cons e1 res))
>         (recur (rest coll1) (rest coll2) res)))))
>
> (defn matching-elements [coll1 coll2]
>   (matching-elements* coll1 coll2 nil))
>
> This handy trick is described fairly early in SICP and can generally be
> applied almost anywhere where you're "almost" tail recursive, because you do
> something to massage the return value of the recursive call on the way out
> but don't make multiple recursive calls on a single path through the
> function.
>
> I believe that the cons prevents the use of recur to make this tail
>
> > recursive.
>
> Only on the first recursive call, and only if the above trick isn't
> employed.
>
> > After a little digging and reading through a thread on this group
> > about parallel iteration, I devised the following alternative
> > solution, which makes use of lazy sequences:
>
> > (defn matching-elements2 [coll1 coll2]
> >  (map first (filter #(let [[e1 e2] %] (= e1 e2)) (partition 2
> > (interleave coll1 coll2)))))
>
> That's clever, though it's eclipsed by Meikel's. Apparently you didn't
> realize that map and friends can tandem-iterate more than one sequence at a
> time. Understandable enough if you're fairly new to Clojure.
>
> > Can anyone suggest an improved implementation of either
> > implementation?
>
> Meikel improved the second; I improved the first, above, not because it's
> better than Meikel's but because I think it might be enlightening to many
> readers here interested in tail recursion and, more generally, in design and
> optimization of recursive functions.
>
> HTH, HAND, and all that.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to