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