Recursion with integers

Like lists, natural numbers have a recursive structure of which we can take advantage when we write direct-recursion procedures. A natural number is either (a) zero, or (b) the successor of a smaller natural number, which we can obtain by subtracting 1.

Standard Scheme provides the predicate zero? to distinguish between the (a) and (b) cases, so we can again use an if-expression to ensure that only the expression for the appropriate case is evaluated. So we can write a procedure that applies to any natural number if we know (a) what value it should return when the argument is 0 and (b) how to convert the value that the procedure would return for the next smaller natural number into the appropriate return value for a given non-zero natural number.

For instance, let's consider a procedure that computes the termial of any natural number number, that is, the result of adding together all of the natural numbers up to and including number. Here are some illustrative sample calls:

> (termial 7)   ; = 7 + 6 + 5 + 4 + 3 + 2 + 1 + 0
28
> (termial 1)   ; = 1 + 0
1
> (termial 0)
0

And here's how I'd define termial:


;;; termial: compute the sum of natural numbers not greater
;;; than a given natural number 

;;; John David Stone
;;; Department of Mathematics and Computer Science
;;; Grinnell College
;;; stone@cs.grinnell.edu

;;; created February 3, 2000
;;; last revised August 8, 2001

;;; Given:
;;;   NUMBER, an integer.

;;; Result:
;;;   SUM, an integer.

;;; Precondition:
;;;   NUMBER is exact and non-negative (in other words,
;;;   a natural number).

;;; Postcondition:
;;;   SUM is the sum of the natural numbers not greater
;;;   than NUMBER.

(define termial
  (lambda (number)
    (if (zero? number)
        0
        (+ number (termial (- number 1))))))

Whereas in a list recursion, we called the cdr procedure to reduce the length of the list in making the recursive call, the operation that we apply in recursion with natural numbers reduces the natural-number argument in the recursive call by 1. Here's a summary of what actually happens during the evaluation of a call to the termial procedure -- say, (termial 5):

    (termial 5)
--> (+ 5 (termial 4))
--> (+ 5 (+ 4 (termial 3)))
--> (+ 5 (+ 4 (+ 3 (termial 2))))
--> (+ 5 (+ 4 (+ 3 (+ 2 (termial 1)))))
--> (+ 5 (+ 4 (+ 3 (+ 2 (+ 1 (termial 0))))))
--> (+ 5 (+ 4 (+ 3 (+ 2 (+ 1 0)))))
--> (+ 5 (+ 4 (+ 3 (+ 2 1))))
--> (+ 5 (+ 4 (+ 3 3)))
--> (+ 5 (+ 4 6))
--> (+ 5 10)
--> 15

The restriction that termial takes only natural numbers as arguments is an important one. If we gave it a negative number or a non-integer, we'd have a runaway recursion, because we cannot get to 0 by subtracting 1 repeatedly from a negative number or from a non-integer, and so the base case would never be reached. If we gave the termial procedure an approximation rather than an exact number, we might or might not be able to reach zero, depending on how accurate the approximation is and how much of that accuracy is preserved by the subtraction procedure.

The important part of getting recursion to work is making sure that the base case is inevitably reached by performing the simplification operation enough times. For instance, we can use direct recursion on exact positive integers with 1, rather than 0, as the base case, provided that we always start from an exact integer greater than or equal to 1.

As an illustration of this, consider the factorial procedure, which finds the product of the positive integers up to and including its argument. (The computation would be pointless if we included 0 as one of the factors, since 0 times anything is 0.) Again, we can begin by writing out some sample calls:

> (factorial 7)  ; = 7 x 6 x 5 x 4 x 3 x 2 x 1
5040
> (factorial 2)  ; = 2 x 1
2
> (factorial 1)
1

Here is the definition:


;;; factorial: compute the product of positive integers not
;;; greater than a given positive integer

;;; John David Stone
;;; Department of Mathematics and Computer Science
;;; Grinnell College
;;; stone@cs.grinnell.edu

;;; created February 3, 2000
;;; last revised August 8, 2001

;;; Given:
;;;   NUMBER, an integer.

;;; Result:
;;;   PRODUCT, an integer.

;;; Precondition:
;;;   NUMBER is positive and exact.

;;; Postcondition:
;;;   PRODUCT is the product of the positive integers not
;;;   greater than NUMBER.

(define factorial
  (lambda (number)
    (if (= number 1)
        1
        (* number (factorial (- number 1))))))

Similarly, we can use direct recursion to approach the base case from below by repeated additions of 1, if we know that our starting point is less than or equal to that base case and that the difference between them is an exact integer. As an example, let's develop a procedure that takes two exact integers, lower and upper, as arguments, and returns a list of the exact integers from lower to upper, inclusive, in ascending order, thus:

> (count-from 1 10)
(1 2 3 4 5 6 7 8 9 10)
> (count-from -3 +4)
(-3 -2 -1 0 1 2 3 4)
> (count-from 116 117)
(116 117)
> (count-from -38 -38)
(-38)

Here is the definition:


;;; count-from: given two natural numbers, construct
;;; a list of the natural numbers from the first to
;;; the second, inclusive, in ascending order

;;; John David Stone
;;; Department of Mathematics and Computer Science
;;; Grinnell College
;;; stone@cs.grinnell.edu

;;; created February 3, 2000
;;; last revised August 8, 2001

;;; Given:
;;;   LOWER and UPPER, both integers.

;;; Result:
;;;   LS, a list.

;;; Preconditions:
;;;   (1) LOWER and UPPER are exact.
;;;   (2) LOWER is less than or equal to UPPER.

;;; Postconditions:
;;;   (1) The length of LS is UPPER - LOWER + 1.
;;;   (2) For every natural number k less than the length of
;;;       LS, the element in position k of LS is LOWER + k.

(define count-from
  (lambda (lower upper)
    (if (= lower upper)
        (list upper)
        (cons lower (count-from (+ lower 1) upper)))))

This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~gum/courses/151/readings/recursion-with-integers.xhtml

created February 3, 2000
last revised September 12, 2002

John David Stone (stone@cs.grinnell.edu)