I’ve been reading Carin Meier’s Living Clojure and cannot recommend it highly enough for functional programming enthusiasts! The book is divided into two parts - a guided tour of the most important Clojure concepts, and a weekly training plan. After a quick read of the guided tour, I embarked on the training plan and have completed Week 1.
Many of the initial set of exercises are put up on 4Clojure, a fantastic website that hosts code exercises in a koan-style (i.e. ‘fill in the blanks’ style). One of the problems deals with writing a function to count the total number of elements in a sequence. Ofcourse, one could use
count - but the point here is to make use of functional programming concepts to achieve what the
count function would have accomplished.
In short, we need to fill in the blanks - and the expression should evaluate to
Idea 0: The
Why not use the super-lame, standard
count function as a starter?
Idea 1: One, two, three …
Map the input sequence into a (1 2 3 …). The last element would give the number of elements in the original sequence.
We could use the fact that
map can be used with multiple collections, and terminates when the shortest collection ends.
In other words,
map f c1 c2 applies function
f on the corresponding elements of
c2. The function’s output is accumulated into a lazy sequence. For example,
map + [1 2 3] [4 5 6] returns
[5 7 9]
Going to back to our problem, we first need to transform the input sequence to (1 2 3 …):
Now, we need to get the last element of the above sequence:
Finally, it looks like:
Idea 2: One, one, one …
Map the input sequence into (1 1 1 …) and sum it up.
Transforming the sequence into (1 1 1 …) is easy. We just need to use
repeat 1 instead of
range as one of the input collections to our map:
To sum up the resultant sequence, we can use the
reduce function. The
reduce function takes two arguments: the ongoing result (or the accumulator) and the element it is processing currently. For example
reduce + [1 2 3 4] returns
10. Putting it together, we have:
Going back to our ‘fill-in-the-blank answer’, we cannot just replace the blanks with
(reduce + (map (fn [one _] one) (repeat 1) - because we want to map before we even attempt to reduce. The solution is to create an anonymous function or a lambda. Let’s illustrate that with examples:
Armed with this information, we can come up with the final ‘fill-in-the-blank answer’:
Idea 3: Aesthetic improvement on Idea 2
Instead of using the
map f c1 c2 form, we can use the more fundamental
map f c form.
The idea is to constantly return 1 for every element of the input sequence. Luckily, we have the
constantly built-in function.
The koan answer would now look like:
Idea 4: Just count
Why not just use
reduce, and keep incrementing the accumulator? Arguably, this is the simplest among all the ideas presented in this article.
So simple that I’ll jump into the koan answer:
That concludes the finger-licking experience of counting with Clojure. There might be a gazillion better ways to count in Clojure, but the bottom-line is that Clojure rocks!