Search code examples
clojurepredicates

Clojure take-while with logical and


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]))))

Solution

  • 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.