I'm learning Clojure and to get a better handle on my progress I decided to start solving the Project Euler problems in the language (some of which I've already solved in C++ and Python). Problem 1 goes as follows:
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.
Here's my first run at the Clojure solution:
(defn main1
([n]
(reduce +
(filter
#(or
(= 0 (mod % 3))
(= 0 (mod % 5)))
(range n)))))
I then looked at my Python version of the code, which is as follows:
import sys
import operator
from functools import reduce
def main(n=1000):
"""
returns solution up to but excluding n
"""
genexp = (num for num in range(1, n) if ((num % 3 == 0) or (num % 5 == 0)))
total = reduce(operator.add, genexp)
return total
if __name__ == "__main__":
if len(sys.argv) > 1:
print(main(int(sys.argv[1])))
else:
print(main())
Ignoring the extra CLI-args stuff I've added in the Python version, the only major difference is that I used filter
in Clojure instead of a generator. I guess I could've used a filter
in Python too, but just for the sake of argument, I made my Clojure code more similar to the Python code:
(defn main2
([n]
(reduce + (for [i (range n)
:let [div3 (= 0 (mod i 3))
div5 (= 0 (mod i 5))]
:when (or div3 div5)]
i))))
That got me thinking - how can I benchmark these functions to compare them? For Python, it's easy enough:
$ time python python/p0001.py 10000000
23333331666668
real 0m2.693s
user 0m2.660s
sys 0m0.018s
$ time python python/p0001.py 100000000
2333333316666668
real 0m26.494s
user 0m26.381s
sys 0m0.050s
It goes up to n=100,000,000
in a reasonable amount of time (under 30 seconds). How can I run a similar test for my Clojure funcs? I imagine I'd have to compile the Clojure code first before running it. Would that even be a fair comparison, given the Python code isn't JIT-compiled here?
On another note, how idiomatic is my Clojure code (both versions)? And what are some good recommendations for styling the code? Is there a pep8-like style-guide? Or even something along the lines of a Clojure version of pep20: the "Zen of Python"?
(
around the argument vector([n] ...
(2)(= 0 ...
is often expressed as(zero? ...
(3) your indentation is mostly correct but search for the clojure style guide and you'll get some good info. (4) When expressing numbers with many zeroes, consider using scientific notation.100,000,000
in clojure is also1e8
. all in all nice job! I would second the recommendation for criterium. I use it almost daily. the 100,000,000 version shows a time of 1.84s here FYI. - Josh(let [n 100000000] (transduce (filter #(or (zero? (rem % 3)) (zero? (rem % 5)))) + 0 (range n)))
it uses transducers to avoid building up any collection. - ClojureMostlypmap
comes to mind - and it would be nigh impossible to write "naively similar" concurrent code in Python and Clojure, what with Python's lack of focus on concurrency. - alichaudryzero?
and scientific notation are helpful, and github.com/bbatsov/clojure-style-guide seems like the style guide I will soon be trying to grok. - alichaudry