Reading blogs can be so thought-provoking.
I did experience it today after having read Paul Sanwald’s clojure basics: argument destructuring. It’s so short and easy to follow that I read it a couple of times. I seem to not like it much, as it left me wondering how to improve the code. What stroke me was the function symbol in
map. I didn’t like it (and I bet many newcomers to Clojure would concur).
I spent a few “mental” cycles thinking about a better way. Here’s what I came up with. There’s no anonymous function definition inside
map, and the code follows:
(reduce + (map multiply distribution))
Do you think it’s really better? Would you think of a better way? Let me know.
For novices, a bit of explanation on what these two lines do. In line 1,
partial defines a function that will call
(apply *) with the arguments given.
([f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
user=> (partial apply *)
# (hash) says the result of calling partial with arguments is a function.
def special form the result is in turn assigned to the
multiply symbol. From now on,
multiply points to the result of the
partial‘s call and resolves to a function.
The arguments to
multiply (and hence to the new partial function) will come from the distribution map/vector –
distribution – when used in
map (the 2nd line).
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments.
So, let’s pretend we work with the distribution (I use ratios to denote fractions – a neat feature of Clojure I have not found in the other languages I worked with or learnt):
map takes each element off a sequence and applies the function given returning another sequence of results. In our case, the function is
(partial apply *) which applies multiplication to every pair.
(1/2 2/3 3/4)
reduce kicks in.
([f coll] [f val coll])
f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. If coll has only 1 item, it
is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called.
It reduces the seq(uence of the
multiply results) with
+ to sum it up and gives the final result.
Easy, isn’t it?
When d(istribution) is of the Map type, make use of
(mapcat seq d).
user=> (reduce + (map multiply (mapcat seq d)))
I don’t however think a map would be the correct type for
distribution. Let me know how much off base I am. I’m glad to learn.