1

I'm trying to learn Clojure, so I figured a good way to start would be to work through the project Euler challenges with it, and the first challenge is summing all of the number below 1000 that are divisible by 3 or 5.

My original code was:

(defn div3 [input-no] (zero? (mod input-no 3)))
(defn div5 [input-no] (zero? (mod input-no 5)))
(reduce + (filter (or div3 div5) (range 1 1000)))

But that didn't work, and it turned out that the filter would just return a list of numbers divisible by 3, and not those divisible by 5.

I changed my code to:

(defn div3or5 [input-no] (or (div3 input-no) (div5 input-no)))
(reduce + (filter div3or5 (range 1 1000)))

Which got the right result, but I don't know why my original code didn't work.

Could anyone shed some light on this?

Peter
  • 1,381
  • 2
  • 13
  • 24
  • Another way to learn Clojure is to find a project you'd do in another language -- for me it would have been implemented in C or r Python -- and write that in Clojure. For me, it was taking .csv input, forming and sending http requests from the .csv input data, retrieving the results, and, based on them, writing out a different .csv file. – octopusgrabbus Apr 27 '12 at 13:21
  • For Clojure http://www.4clojure.com/ (no pun intended) is good, the web interface makes it really easy! – huon Apr 27 '12 at 13:23
  • i'm curious if this was just a mistake (which is ok - no criticism intended) or whether there is some language where what you typed would have made sense. were you expecting `or` to combine predicates because you see that behaviour elsewhere? if so, where? thanks... – andrew cooke Apr 27 '12 at 13:45
  • I thought I could use or like: if( Function1(x) || Function2(y)) where Functions 1 and 2 both return booleans. – Peter Apr 27 '12 at 14:24

2 Answers2

4

The problem you are running into is that filter expects a predicate (a function taking an input and returning true or false) as its first argument. But while div3 and div5 are functions you can't simply combine them with or. You need to construct a new function that takes one argument and feeds this to both div3 and div5 and calls or and the results of both.

Fortunately this is easy to do in Clojure, try

(filter #(or (div3 %) (div5 %)) (range1 1000))

#() is shorthand for defining a function inline (also called a lambda) and you can get to the first argument with %1 to the second with %2 and so on. If there is only one argument then you can use % for %1 see this question.

You may also want to understand that #() is just syntactic sugar for the fn form which looks like this: (fn [arg1 arg2 ... & restArgs] (forms)). #() has some limitations (for example it can't be nested).

Community
  • 1
  • 1
Paul
  • 7,836
  • 2
  • 41
  • 48
3

If you just evaluate (or div3 div5) in the REPL you can see what is happening:

=> (or div3 div5)
#<user$div3 user$div3@73305c>

That is, or is evaluating to the function div3 (which filter is then using, giving the behaviour you describe).

The reason for this is or will return its first non-falsy argument (i.e. the first argument that isn't nil or false); in this case, the arguments are two function objects and a function object is not nil or false.

To put it another way, the or is happening on the functions themselves, not the results of the functions. As Paul said, you can use an anonymous function to make or act on the results rather than the functions themselves.

huon
  • 94,605
  • 21
  • 231
  • 225