I've been keenly interested in Clojure for awhile now and have been slowly adding to my skill set. In order to continue my progress and, hopefully, help others that are new to the language, I thought it would be worthwhile to have a series of posts where I tackle the Project Euler problems in Clojure, comparing and contrasting various approaches where appropriate. I'll start out pretty introductory and will hopefully build up to more advanced concepts quickly. While I pursue this I'll be committing the solutions to a github repository.
This is the first post in this series and I'll tackle Problem 1. From the Project Euler site:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
So, the first effort will be the straightforward route. We need to generate the natural numbers between 1 and 1000 and filter those that are divisible by 3 and/or 5. Then, we'll sum all of the numbers that make it through the filter.
In Clojure, a series of values like this, a subset of the natural numbers, is represented by a sequence. A sequence is a view over a collection, not a concrete data structure in its own right. The sequence of natural numbers from 0 to 1000 is created using the range function:
user=> (range 1000)
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ...)
Now we need to pull out the numbers that are divisible by 3 and/or 5. In other words, we need to "filter" the sequence to create a new sequence (which is just a different view of the current sequence) that only contains the numbers we want:
user=> (filter #(or (zero? (rem % 3))
(zero? (rem % 5)))
(range 1000))
(0 3 5 6 9 10 12 15 18 20 21 24 25 27 30 33 35 36 39 40 ...)
The #(...) syntax is just sugar to stand in for (fn [] (...)) to ease the creation of anonymous functions, or lambdas. The first argument passed to the lambda can be accessed by % or %1 and, in general, the nth argument passed to the lambda can be accessed by %n. So, our lambda is just getting passed the next item from the sequence to filter and it will return true if that item is divisible by 3 or 5.
Now, all that's left is to sum these up, so we'll apply the + function to all of the items in our filtered sequence, which will keeping a running total until it runs out of items:
user=> (apply + (filter #(or (zero? (rem % 3))
(zero? (rem % 5)))
(range 1000)))
233168
Voilá!
Certainly there are other ways to attack this problem and perhaps I'll revisit it in the future if any are particularly interesting.
Questions, comments, suggestions and hints are all welcome in the comments!