HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

Project Euler Problem 2 in Clojure

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
problemprojectclojureeuler

Problem

I am in the process of learning Clojure. I am fairly new to functional programming and would like to know if my code smells or if there are any performance implications with my approach.

; Returns the the given sequence with the given item appended to it.
(defn snoc [xs x] (concat xs [x]))

; Returns the Fibonacci sequence up to the highest number less than max.
(defn fib [max] 
  (loop [a 1, b 1, acc [1]] 
    (if (> b max) 
      acc
      (recur b (+ a b) (snoc acc b)))))

; Project Euler Problem 2: Attempt A
(defn pe2a []
  (reduce +
    (filter even? (fib 4000000))))


For reference:


Each new term in the Fibonacci
sequence is generated by adding the
previous two terms. By starting with 1
and 2, the first 10 terms will be:


1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...


By considering the terms in the
Fibonacci sequence whose values do not
exceed four million, find the sum of
the even-valued terms.

Solution

(defn snoc [xs x] (concat xs [x]))


There is a reason snoc is not defined by default in clojure: Since appending at the end of a singly linked list takes O(n) time, this is actually quite expensive. When building up non-lazy lists tail-recursively in a functional language, you often build the list the wrong way around (using cons instead of snoc) and then reverse it at the end to avoid that cost.

However in this case there is actually a nicer way: by using a lazy sequence rather than a strict list, we can avoid the need for loop/recur and save the cost of building up the list. We can also separate the logic of creating the Fibonacci numbers from the logic which decides how many numbers we want by first creating a lazy sequence containing all Fibonacci numbers and then using take-while to take those less than the given maximum. This will lead to the following code:

;; A lazy sequence containing all fibonacci numbers
(def fibs
  (letfn
    [(fibsrec [a b]
      (lazy-seq (cons a (fibsrec b (+ a b)))))]
    (fibsrec 1 1)))

;; A function which returns all fibonacci numbers which are less than max
(defn fibs-until [max]
  (take-while #(<= % max) fibs))

Code Snippets

(defn snoc [xs x] (concat xs [x]))
;; A lazy sequence containing all fibonacci numbers
(def fibs
  (letfn
    [(fibsrec [a b]
      (lazy-seq (cons a (fibsrec b (+ a b)))))]
    (fibsrec 1 1)))

;; A function which returns all fibonacci numbers which are less than max
(defn fibs-until [max]
  (take-while #(<= % max) fibs))

Context

StackExchange Code Review Q#300, answer score: 14

Revisions (0)

No revisions yet.