Equations and Recursion


Computing powers

Suppose that you want to define a function power(x, n) that yields xn, where n is a positive integer. For example, power(3,2) = 32 = 9 and power(2,4) = 24 = 16.

Thinking in terms of cases, there is certainly one easy case: x1 = x. So, just to get started, let's write that case.

  case power(x,n) = x    when n == 1
But what about larger values of n. Will it work to have a separate case for each n?
  case power(x,n) = x*x         when n == 2
  case power(x,n) = x*x*x       when n == 3
  case power(x,n) = x*x*x*x     when n == 4
  case power(x,n) = x*x*x*x*x   when n == 5
  ...
The obvious problem is that there will be too many cases. We are striving for short and simple programs, not long ones that go on and on. But powers have some regularity to them. Just think about some facts:
  x2 = x * x1
  x3 = x * x2
  x4 = x * x3
  ...
Those facts are so regular that they can be expressed as one general fact.
  xn = x * xn-1
as long as n > 1. Suppose that we add that as a second case to our definition of power. But remember that power(x,n) is supposed to equal xn, so replace xn by power(x,n), and replace xn-1 by power(x, n-1).
  case power(x, n) = x                  when n == 1
  case power(x, n) = x * power(x, n-1)  when n > 1
But that is a strange definition! It appears to be circular, since it seems to assume that you already know how to compute the power function before you say how to compute the power function. But, in reality, it is not circular, which is easy to see by trying to do a computation. Let's compute power(5,3).
  power(5,3)
    = 5 * power(5, 3-1)       (using the second case for power, 
                               since 3 == 1 is false)
    = 5 * power(5, 2)
    = 5 * (5 * power(5, 2-1)) (using the second case for power, 
                               since 2 == 1 is false)
    = 5 * (5 * power(5, 1))
    = 5 * (5 * 5)             (using the first case for power,
                               since 1 == 1 is true)
    = 5 * 25
    = 125

The reason that our equations for power are not really circular is that we define fifth powers in terms of fourth powers, fourth powers in terms of third powers, third powers in terms of second powers, etc. There is no circularity in that.

Using a function in its own definition is called recursion.


Case study: computing factorials

The value n! is defined to be the result of multiplying together the first n positive integers. For example, 4! = 1*2*3*4 = 24 and 5! = 1*2*3*4*5 = 120.

Let's define a function factorial(n) that produces n!. Notice that

    1! = 1
    2! = (1)*2          = (1!)*2
    3! = (1*2)*3        = (2!)*3
    4! = (1*2*3)*4      = (3!)*4
    5! = (1*2*3*4)*5    = (4!)*5
    6! = (1*2*3*4*5)*6  = (5!)*6
You can compute larger factorials from smaller ones. In general, it is easy to see that n! = ((n-1)!)*n as long as n > 1.

Our definition of factorial uses cases. The first case tells how to computer 1!, and the second cases tells how to compute all of the others.

  case factorial(n) = 1                   when n == 1
  case factorial(n) = factorial(n-1) * n
Let's use the usual substitution approach to compute factorial(2).
  factorial(2)
    = factorial(2-1) * 2       (by the second case for factorial,
                                since 2 == 1 is false)
    = factorial(1) * 2
    = 1 * 2                    (by the first case for factorial,
                                since 1 == 1 is true)
    = 2
When doing a substitution for factorial(2), you look at the cases and decide what they tell you when n = 2. Since 2 ≠ 1, the second case applies, and you see that factorial(2) = factorial(2-1) * 2. When doing a substitution for factorial(1), you look again at the cases and ask what they say in the case where n is 1.

Computation of factorial(4) is a little longer, but it works.

  factoral(4)
    = factorial(4-1) * 4               (by the second case for factorial,
                                        since 4 == 1 is false)
    = factorial(3) * 4
    = (factorial(3-1) * 3) * 4         (by the second case for factorial,
                                        since 3 == 1 is false)
    = (factorial(2) * 3) * 4
    = ((factorial(2-1) * 2) * 3) * 4   (by the second case for factorial,
                                        since 2 == 1 is false)
    = ((factorial(1) * 2) * 3) * 4
    = (((1) * 2) * 3) * 4              (by the first case for factorial,
                                        since 1 == 1 is true)
    = ((2) * 3) * 4
    = (6) * 4
    = 24


Problems

  1. [solve] Suppose that f(n) is defined as follows.

      case f(n) = n                  when n == 1
      case f(n) = 2*f(n-1)
    
    Do a step-by-step evaluation of f(3).

  2. [solve] The following function sum(n) yields 1 + 2 + ... + n, the sum of the first n positive integers. For example, sum(3) = 1 + 2 + 3 = 6 and sum(5) = 1 + 2 + 3 + 4 + 5 = 15.

      case sum(n) = 1              when n == 1
      case sum(n) = sum(n-1) + n
    
    Do a step-by-step evaluation of sum(4).

  3. [solve] Suppose that s(n) is defined as follows.

      case s(n) = 0                  when n == 0
      case s(n) = s(n-1) + 2*n + 1
    
    Do a step-by-step evaluation of s(3).


Summary

The equations that define a function can use the same function that you are defining. Doing that is called using recursion.

Recursion allows you to write simple and elegant definitions of a wide variety of functions.

Recursion always relies on using more than one case.


Review

Each equation that defines a function is a separate context.

Each case in a definition by cases is also a separate context.


Review questions

  1. [solve] Not all functions require recursion.

    Write a definition of function min(x,y), which produces the smaller of x and y.

  2. [solve] You can compute the largest of three numbers x, y and z by computing the larger of x and y, and then to compute the larger of that number and z. Write a definition of largest(x,y,z) that only uses one defining equation, but that uses function max to find the larger of two given numbers.