On May 28, 3:01 pm, Meikel Brandmeyer <m...@kotka.de> wrote:
> Hi,
>
> Am 28.05.2009 um 20:11 schrieb Sean Devlin:
>
> > Without discussing a specific application, I think what you're looking
> > for can be achieved by normal macros and functions in Clojure.  I'll
> > try implement the collect method in Clojure, and hopefully that will
> > explain things.
>
> > Let's start by creating a collect function.
>
> > (defn collect
> >  [pred coll]
> >  (loop [remaining coll
> >         output []]
> >    (if (empty? remaining)
> >      output
> >      (recur (rest remaining)
> >        (conj output (pred (first remaining)))))))
>
> This can be written more concise with reduce:
>
> (defn collect
>    [f coll]
>    (reduce #(conj %1 (f %2)) (empty coll) coll))
>
> Whenever you do loop/recur and return one argument
> when the input is used up, you probably can turn it into
> a nice reduce.

1.  collect is the ruby version of map, not reduce.  An interesting
use of reduce, though.
2.  I was trying to show how one would explicitly write map.  I agree,
using standard clojure is much much much better.

>
>
>
> > And a quick test shows
>
> > (collect (fn[x](* 2 x)) [1 2 3])
> > =>[2 4 6]
>
> > Notice that the clojure collect takes a function, pred as an
> > argument.  The s-expression (pred (first remaining)) automatically
> > applies the right function do to the *pure genius* that is eval.  It's
> > pretty slick.
>
> > Here's my best attempt at writing an all-purpose collect-m macro.
> > It's ugly, and I don't like it.
>
> > (defmacro collect-m
> >  [coll & body]
> >  `(let [~'pred (fn [~'elemen...@body)]
> >     (loop [~'remaining ~coll
> >         ~'output []]
> >    (if (empty? ~'remaining)
> >      ~'output
> >      (recur (rest ~'remaining)
> >        (conj ~'output (~'pred (first ~'remaining))))))))
>
> > (collect-m [1 2 3] (* 2 element))
> > =>[2 4 6]
>
> Please don't write a macro like that! This captures
> the names you chose for your locals. Imagine a
> call like this (collect-m (do-something-with-another pred) ...),
> where the pred comes from outside the macro. This
> call will fail, because you captured pred in your macro
> expansion. Use auto-gensym via the # suffix.
>
> `(let [pred# ...])
>
> This will generate and new symbol like pred__AUTO_412
> or something similar, which is unlikely to be used by the
> surrounding code, which calls the macro.
>
> The parameter problem can be solved by providing an
> additional parameter, which is then used in the function
> argument vector.
>

This is exactly the problem I wanted to highlight, thanks for
providing a better way to write that macro.

>
>
> > Notice that the term element is fixed as a parameter name.  I assume
> > that there is ONLY on input in the function.  The macro is ugly to
> > read.  Maybe someone else can do better.
>
> > Revisiting the function version, notice:
>
> > (collect (fn[x](* 2 x)) [1 2 3])
>
> > Feels very similar to
>
> > [1, 2, 3].collect(){|x| 2*x}
>
> > You have much more control of the function.
>
> > The main point I'm getting at is that I don't think the blockfn macro
> > approach is the way to go.  Either write a function that take
> > functions, or use a traditional macro.
>
> I find this defblockfn actually quite interesting. A lot of macros
> boil down to wrap some body in a thunk and pass it to a driver
> function, which does the work, which can be defered to runtime.
> This approach has several advantages:
>
> - smaller code size since the macro expands to a simply function call
> - changing the logic in the driver function does not require  
> recompilation
>    of the macro users.
> - the driver function is easier to write than the macro
>
> So maybe 95% of my macros are paired with a function.
>

This is exactly why I think creating defblockfn is a bad idea.  We
already have a perfectly good macro facility in clojure.  Let's use
what we've got.

> YMMV of course...
>
> Sincerely
> Meikel
>
>  smime.p7s
> 5KViewDownload
--~--~---------~--~----~------------~-------~--~----~
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
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