"'Computer Science' is not about Computers!

It is not really about computers in the same way that Physics is not really about particle accelerators, and Biology is not really about test tubes."
- Gerald J. Sussman

"Computer Science is no more about computers than astronomy is about telescopes."
- Edsger W. Dijkstra

the brown-dragon blog

Recursive Processes vs Recursive Procedures (characterizing Tail Recursion)

2009-08-22

What is a recursive process?

Most programmers with a few years under their belt understand recursive procedures. A recursive procedure is simply a procedure that calls itself. Understanding this is somewhat of a "rite of passage" for many programmers.

In this post we will take the discussion slightly further from recursive procedures and discuss recursive processes.

Computer Science is not so much the study of procedures as the study of processes. Procedures are used to generate processes but it is the process in the end that we are interested in.

An interesting question to ask, therefore, is - Do recursive procedures always lead to recursive processes?

At first glance this may seem like a silly question but bear with me a little more. Let's discuss this by looking at two implementations of a simple recursive function - the Factorial function: F(0) = 1; F(n) = n * F(n)

Implementation 1

implementation-1

 (define (f1 n)                     |     int f1 (int n)
     (if (= n 0)                    |     {
         1                          |         if (n == 0) return 1;
         (* n (factorial (- n 1)))))|         return n * f1 (n - 1);
                                    |     }
        (SCHEME/LISP)               |            (C/C++)


Implementation 2

implementation-2

 (define (f2 n)                     | int f2i (int n, int result, int count)
     (define (f2i result count)     | {
         (if (> count n)            |    if (count > n) return result;
             result                 |    return f2i (n, result * count, ++count);
             (f2i (* result count)  | }
                  (+ count 1))))    | int f2 (int n)
     (f2i 1 1))                     | {
                                    |    return f2i (n, 1, 1);
                                    | }
         (SCHEME/LISP)              |            (C/C++)

Now note that both implementations are recursive and both compute the Factorial function perfectly. Now let us take a look at the processes each of them generate:

Process 1

Recursive Process

Process 2

Iterative Process

The first process grows and shrinks which is typical of a recursive process. The second, however, does not! All it's information is contained in the current value of it's variables. Such a process is, in fact, iterative in nature.

We can now answer the question posed earlier - "Do recursive procedures always lead to recursive processes?" Surprisingly the answer is - No!

We have seen a recursive procedure that actually describes an iterative process. Such recursive processes are known as tail recursive because the recursive call is normally the last call in the recursive procedure and can be replaced with a simple goto.

In fact, compilers like Scheme recognize tail recursion and replace the call with a simple jmp instruction. Many mainstream languages (like C/C++) however, keep the call instruction (note in the diagram above, that the C/C++ version continues to behave recursively returning the same value across each call). This is one reason many programmers are unaware of the distinction.

Another way to look at our result is that iteration is a special form of recursion! If you take another look at the second process you can see f2i actually resembles a for loop.

What this means is that, for, while, and the do, loops are simply syntactic sugar in languages that are properly tail-recursive.

Why is this important? Well, it could mean that, while formulating theories in the new and exciting field of Computer Science, we may not have to deal with looping constructs because they can be handled as special cases of recursion.

In a later post, I will discuss how recursion itself can be simulated with the help of higher-order functions so in the end we may not have to deal with either recursion or iteration and are left with a much simpler system to analyse.

Other Posts

(ordered by Tags then Date)