lazily calling functions for infinite sequence clojure

81 Views Asked by At

I am very new to clojure and want to make obvious programs.

(def fib (conj fib (apply + (take-last 2 fib)))) Doesn't work because of no lazy evaluation, nor indication of the start being [0 1]

(def fib ( (fn [array] (recur (conj array (apply + (take-last 2 array))))) [0 1])) And here recur is seemingly not lazily evaluated.

Highly related to ( a recursive Fibonacci function in Clojure ) however they use loops and counts or multi-arity. I was wondering if this problem was possible without this.

(def fib (lazy-cat [0 1] (map + fib (rest fib)))) is a very nice solution but somehow hides the operation of fx = fx-1 + fx-2, rather a shift and add all elements. Elegant but not so obvious to the original fibonacci definition.

Is there a way to avoid using map.

2

There are 2 best solutions below

2
Harold On BEST ANSWER

obvious is going to be highly subjective, but perhaps this can give you some ideas:

user> (def fib ((fn f [a b] (lazy-seq (cons a (f b (+ a b))))) 0 1))
#'user/fib
user> (take 10 fib)
(0 1 1 2 3 5 8 13 21 34)
0
Rulle On

There are many ways to skin a cat but using lazy-seq to implement the Fibonacci sequence is arguably the prettiest. But the iterate function is also not that bad.

Here are three implementations of the Fibonacci sequence that all produce lazy sequences with increasing degree of ugliness.

(defn fib
  ([] (fib 0 1))
  ([a b]
   (lazy-seq
    (cons a (fib b (+ a b))))))

(take 20 (fib))
;; => (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

(defn fib2 []
  (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))

(take 20 (fib2))
;; => (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

(defn fib3 []
  (iterator-seq
   (let [state (atom [0 1])]
     (reify java.util.Iterator
       (hasNext [_] true)
       (next [_] (ffirst (swap-vals!
                          state
                          (fn [[a b]]
                            [b (+ a b)]))))))))

(take 20 (fib3))
;; => (0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181)

But I wouldn't recommend the last one that creates a Java iterator and wraps it with iterator-seq. But I nevertheless included it to show the strong connection between Clojure lazy sequences and Java iterators.