2

While working on the Clojure Koans, I had to calculate the factorial of a number iterativly, I did find the solution, but I have a question about the difference between 2 solutions, one that works and one that doens't, although I don't understand why:

The one that works:

(defn factorial [n]
   (loop [n n
         acc 1]
     (if (zero? n)
      acc
      (recur (dec n) (* n acc )))
 )

The one that desn't:

(defn factorial [n]
   (loop [n n
         acc 1]
     (if (zero? n)
      1
      (recur (dec n) (* n acc )))
 )

Note that the only difference is the returned value of the If block if the condition is met.

Jeremy
  • 22,188
  • 4
  • 68
  • 81
Deleteman
  • 8,500
  • 6
  • 25
  • 39

2 Answers2

5

The second factorial function always returns 1. The code is built to use an accumulator variable (acc), and the first code block gets it right by returning this accumulator variable.

A factorial function can be written to return 1, though, if an accumulator variable is not used. Since this method does not utilize loop / recur, it can cause a stack overflow easily: try (fact 5000).

(defn factorial [x]
  (if (<= x 1)
      1
      (* x (factorial (- x 1)))))

(source)

Community
  • 1
  • 1
Jon Gauthier
  • 25,202
  • 6
  • 63
  • 69
  • Thanks for clearing that up for me :) I was aware of the recursive solution and it's limitations, but thanks for the heads up. – Deleteman May 28 '12 at 15:27
1

it's hard to work out what you think should be happening for the question to make sense. i think maybe you think loop is doing more than it does? your code is almost equivalent to:

(defn factorial 
  ([n] (factorial n 1)
  ([n acc] 
    (if (zero? n)
      acc
      (recur (dec n) (* n acc)))))

which is a stack-safe version of

(defn factorial 
  ([n] (factorial n 1)
  ([n acc] 
    (if (zero? n)
      acc
      (factorial (dec n) (* n acc)))))

so the acc (or 1) is the final value returned from the function.

all that loop does is give a different target for recur, which is useful if you have some code between the start of the function and the point where you want to repeat. it's basically a label for a goto.

andrew cooke
  • 45,717
  • 10
  • 93
  • 143