Search code examples
clojurefibonacci

How to reassign a variable another value in clojure


I am doing this Clojure program for fibonacci series.

(def fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println '(a b c))
      )(swap! n inc)))
(fibonacci)

I am getting this error:

CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:1:1) 
CompilerException java.lang.RuntimeException: Unable to resolve symbol: fibonacci in this context, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:13:1) 

I can't figure out how to reassign the variables a, b and c . And also please suggest any correction needed in the program.


Solution

  • First, let's get your code working. The first def, to define a function, should be defn:

    (defn fibonacci []
      (def a 0)
      (def b 0)
      (def c 1)
      (def n (atom 0))
      (while (<= @n 10)
        (do
          (def c (+ a b))
          (def a b)
          (def b c)
          (println '(a b c))
          )(swap! n inc)))
    

    It compiles. Let's try it:

    > (fibonacci)
    
    (a b c)
    (a b c)
    ...
    (a b c)
    => nil
    

    Not what you intended. The trouble is the quote in front of (a b c), which returns the elements of the list unevaluated. We have to take the quote off, but we don't want a to be treated as an operator. So put in list:

    (defn fibonacci []
      (def a 0)
      (def b 0)
      (def c 1)
      (def n (atom 0))
      (while (<= @n 10)
        (do
          (def c (+ a b))
          (def a b)
          (def b c)
          (println (list a b c))
          )(swap! n inc)))
    

    Let's run it:

    > (fibonacci)
    
    (0 0 0)
    (0 0 0)
    ...
    (0 0 0)
    => nil
    

    Well, we got numbers, though not the ones we want. The problem is that the first (def c (+ a b)) wipes out the only non-zero value. We ought to start with b at 1, and just use c temporarily:

    (defn fibonacci []
      (def a 0)
      (def b 1)
      (def n (atom 0))
      (while (<= @n 10)
        (do
          (def c (+ a b))
          (def a b)
          (def b c)
          (println (list a b c))
          )(swap! n inc)))
    

    And now ...

    > (fibonacci)
    
    (1 1 1)
    (1 2 2)
    (2 3 3)
    (3 5 5)
    (5 8 8)
    (8 13 13)
    (13 21 21)
    (21 34 34)
    (34 55 55)
    (55 89 89)
    (89 144 144)
    => nil
    

    Hurrah!

    But we can see that b and c are the same, and just predict the next a.

    There is a bigger problem. Using mutable variables is not the Clojure way. Nor is it idiomatic for a function to print what it computes: better develop the result as a sequence, which we can do what we like with.

    We can define the whole Fibonacci sequence thus:

    (defn fibonacci []
      (map second
           (iterate
             (fn [[a b]] [b (+ a b)])
             [0 1])))
    

    And just develop as much as we want:

       (take 10 (fibonacci))
    
    => (1 1 2 3 5 8 13 21 34 55)