Clojure take-while with logical and

969 views Asked by At

I am learning Clojure and trying to solve Project's Euler (http://projecteuler.net/) problems using this language. Second problem asks to find the sum of the even-valued terms in Fibonacci sequence whose values do not exceed four million.

I've tried several approaches and would find next one most accurate if I could find where it's broken. Now it returns 0. I am pretty sure there is a problem with take-while condition but can't figure it out.

(reduce + 
  (take-while (and even? (partial < 4000000))  
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))))
2

There are 2 answers

2
Arthur Ulfeldt On BEST ANSWER
user> ((partial < 4000000) 1) 
false 

Partial puts the static arguments first and the free ones at the end, so it's building the opposite of what you want. It is essentially producing #(< 4000000 %) instead of #(< % 4000000) as you intended, So just change the > to <:

user> (reduce +
        (take-while (and even? (partial > 4000000))
                         (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))))
9227464

or perhaps it would be more clear to use the anonymous function form directly:

user> (reduce +
              (take-while (and even? #(< % 4000000))
                          (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))))
9227464 

Now that we have covered a bit about partial, let's break down a working solution. I'll use the thread-last macro ->> to show each step separately.

user> (->> (iterate (fn [[a b]] [b (+ a b)]) [0 1]) ;; start with the fibs
           (map first)                              ;; keep only the answer   
           (take-while #(< % 4000000))              ;; stop when they get too big
           (filter even?)                           ;; take only the even? ones
           (reduce +))                              ;; sum it all together.
4613732

From this we can see that we don't actually want to compose the predicates evan? and less-than-4000000 on a take-while because this would stop as soon as either condition was true leaving only the number zero. Rather we want to use one of the predicates as a limit and the other as a filter.

4
Michał Marczyk On

To compose multiple predicates in this way, you can use every-pred:

(every-pred even? (partial > 4000000))

The return value of this expression is a function that takes an argument and returns true if it is both even and greater than 4000000, false otherwise.