2

This is a factorial procedure from SICP that generates a recursive process.

(define (factorial n)
  (if (= n 1) 
      1 
      (* n (factorial (- n 1)))))

Now this is the same procedure, but generates an iterative process. Counter increments up to n and product multiplies itself by counter in each procedure call. When not in a block structure, fact-iter has a variable max-count which is actually n.

(define (factorial n)
  (define (iter product counter)
    (if (> counter n)
        product
        (iter (* counter product)
              (+ counter 1))))
  (iter 1 1))

I'm a little curious as to why we need counter, it doesn't really do anything but incrementing itself and using its value for testing the base case. as with the recursive process, can't we just do the same process while just adding an accumuator to make it iterative? For example:

(define (factorial n) 
(define (fact-iter product n)
  (if (= n 1)
      product
      (fact-iter (* n product)
                 (- n 1))))
  (fact-iter 1 n))

So, this is still an iterative process, and I think a more obvious procedure than the first example.

However, there must be a reason why the book prefered the first example. So what is the advantage of the first iterative example over the second procedure?

lightning_missile
  • 2,821
  • 5
  • 30
  • 58
  • Did you read http://stackoverflow.com/questions/17254240/sicp-recursive-process-vs-iterative-process-using-a-recursive-procedure-to-gene?rq=1 ? – reto Feb 10 '16 at 08:58
  • @reto I did. And I believe my second example is still iterative because I still pass on all information to the parameters needed to perform the procedure. – lightning_missile Feb 10 '16 at 09:04
  • 1
    The only difference between the two seems to be that the first one counts up, and the second counts down. It makes no difference which one you use. Btw. you should not use the same name for `n` in the inner function. That's needlesly confusing. – jkiiski Feb 10 '16 at 09:18
  • 1
    There is no significant difference between the two formulations, as @jkiiski noted, but you should change `(= n 1)` in `(= n 0)` in your function, otherwise `(factorial 0)` causes an endless loop. – Renzo Feb 10 '16 at 09:51
  • There's no advantage to either - the first views the factorial as `1 * 2 * ... * n` and the second as `n * (n - 1) * ... * 1`. The first is more "loop-like" and the second more "recursion-like", in my opinion. – molbdnilo Feb 10 '16 at 10:11
  • I suspect (i.e. hope) the authors chose the version they did to show that you can iterate in that direction and to make you (hopefully) think of "your" version and wonder if they were equivalent. (SICP can be quite subtle at times.) – molbdnilo Feb 10 '16 at 10:15
  • the reason is the separation of concerns, in the book's version: one is to maintain data needed for the next step calculation, the other is testing for when to stop. Your version conflates the two -- uses `n` for both (you make this possible by counting down instead of counting up, but is it always possible to morph a code in such a way?). It is also more fragile, prone to errors because of that. So methodologically, theirs is "cleaner", one might argue. --- I believe you have a typo and the 1st line of code in your 3rd snippet needs to be moved 5 lines down? -- yes, both are iterative. – Will Ness Feb 10 '16 at 23:04
  • The book;s version properly calculates (factorial 0) whereas your version does not. The other difference is the book's version isn't going to go into an infinite loop if you input (factorial 32/3). I mean the book's version won't get the right answer but it makes a good try. – WorBlux Feb 11 '16 at 22:20

1 Answers1

5

Your two iterative versions are the same except one counts up and compares with a free variable n while the other counts down and compares to a constant.

It won't make much difference in speed so I guess the one you think you should go with the one that is more clear in it's intention. Some people might prefer the steps going up.

Sometimes you may choose the order wisely though. If you were to make a list of the numbers instead you would have chosen the steps in the opposite order of your wanted resulting list to be able to keep it iterative:

(define (make-range to)
  (define (aux to acc)
    (if (> 0 to)
        acc
        (aux (- to 1) (cons to acc))))
  (aux to '()))

(define (make-reverse-range start)
  (define (aux n acc)
    (if (> n start)
        acc
        (aux (+ n 1) (cons n acc))))
  (aux 0 '()))

(make-range 10)          ; ==> (0 1 2 3 4 5 6 7 8 9 10)
(make-reverse-range 10)  ; ==> (10 9 8 7 6 5 4 3 2 1 0)
Sylwester
  • 47,942
  • 4
  • 47
  • 79