Counting with Clojure
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 true
:
Idea 0: The count
function
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 c1
and 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!