One of the familiar patterns for recursive procedures is numerical recursion, with zero (or sometimes one) as the base case:
(define power
(lambda (base exponent)
(if (zero? exponent)
1
(* base (power base (- exponent 1))))))
Another is structural recursion on lists, with an empty list (or sometimes a one-element list) as the base case:
(define sum-of-list
(lambda (ls)
(if (null? ls)
0
(+ (car ls) (sum-of-list (cdr ls))))))
In both of these patterns, it is possible to forecast the number of recursive calls that will be made before the base case is reached: In a numerical recursion, the value of the parameter that is decremented toward zero indicates the number of recursive calls remaining, and in a structural recursion the number of recursive calls is the same as the length of the list.
Sometimes, however, the recursion is indefinite, in the sense the number of recursive calls needed to establish the condition that terminates the recursion is not apparent. For instance, here is a recursive procedure that finds and returns the least prime divisor of any integer greater than or equal to 2:
(define least-prime-divisor
(lambda (num)
(if (or (not (integer? num))
(< num 2))
(error 'least-prime divisor
"The argument must be an integer greater than or equal to 2"))
(if (even? num)
2
(least-prime-divisor-kernel num 3))))
(define least-prime-divisor-kernel
(lambda (num trial-divisor)
(if (zero? (remainder num trial-divisor))
trial-divisor
(least-prime-divisor-kernel num (+ trial-divisor 2)))))
The husk procedure checks the precondition and, if it is met, also checks
whether num is even; since 2 is the least prime number, it
should be returned whenever num is divisible by 2, that is,
whenever num is even. In all other cases, the kernel
procedure is called, with 3 as the first trial divisor; the kernel
procedure continues to check odd trial divisors (3, 5, 7, and so on) in
ascending order until it comes up with one that evenly divides
num. This must happen eventually; num is
divisible at least by itself, so that when trial-divisor
becomes equal to num the procedure will terminate. But if
num is large it may be difficult for the programmer to predict
how many times the kernel procedure will invoke itself before terminating.
Use trace-define to find out how many recursive calls are
needed when num is 1007, and how many are needed when
num is 1009.
There are cases in which it is easy to write an indefinite recursion, but difficult to prove that it will always terminate. The following procedure illustrates the problem: Given any positive integer, it constructs and returns a sequence of integers in which each even number n is followed by n/2 and each odd number n, except 1, is followed by 3n + 1:
(define Collatz
(lambda (num)
(cond ((= num 1) '(1))
((even? num) (cons num (Collatz (quotient num 2))))
(else (cons num (Collatz (+ (* 3 num) 1)))))))
Call this procedure to construct the sequence that begins with the number 29, and confirm that every element in the sequence is generated according to the rule described above. Then call the procedure to construct the sequence that begins with the number 27.
The interesting point about the Collatz procedure is that in
spite of the fact that it is easily defined and contains no difficult
mathematical functions, no one knows whether it always terminates. Perhaps
there is a value for num that begins a sequence that never
reaches 1 and hence never ends.
In real-world programming, we generally try to avoid procedures that are not known to terminate.
Consider a leaf-transformation procedure, like
incremeent-every-leaf from the lab on
trees. Can we prove that all of the recursive calls to that procedure
will eventually terminate? If so, how?
Catch up on any of the exercises from previous labs that you haven't previously done. If you're all caught up, try exercises 4.8 - 4.11 on pages 114 and 115 of the textbook.
This document is available on the World Wide Web as
http://www.math.grin.edu/courses/Scheme/spring-1998/indefinite-recursion.html
created February 22, 1998
last revised June 21, 1998