Tail Recursion

Tail recursive functions are functions in which the recursive call only occurs as the last step in the function prior to the return statement.
; tail recursive factorial routine,
;    using an extra parameter to store the factorial computed so far
(define factorial_tr (lambda (N Acc)
    (cond
        ((< N 2) Acc)
        (else (factorial_tr (- N 1) (* N Acc)))
    )
))

; if the desired result is (factorial N) 
;    then the initial call to the tail recursive version
;         is made with N and an "accumulated value" of 1
(define factorial (lambda (N)
   (cond
       ((and (integer? N) (> N 0)) (factorial_tr N 1))
       (else '())
   )
))

Tail recursive functions are of interest because they allow efficiency improvements to take place in either of two very straight-forward ways:

Using such methods, we can eliminate most of the perceived disadvantages of recursion (loss of run time efficiency or excessive memory use) while still allowing the programmer some of the flexibility recursive solutions provide.

Stack optimization during tail recursion

In most systems, the information for each active function call is stored on the system stack, e.g.
  1. the location of the instruction to be run after returning from the function call
  2. space for the value(s) to be returned by the function
  3. the parameters to be passed to the function
  4. space for the local variables for the function
  5. etc
Each time a function call is made an activation record for the new call is created and pushed on the stack, and when the function call completes this record is popped off the stack.

In a tail recursive call, the return value from the caller is precisely the return value from the callee. This means that, rather than creating and pushing a new activation record at the point of the call, we can effectively substitute the callee's activation record for that of the caller (saving stack space - especially if there are a lot of recursive calls).

We keep the caller's return location and space for return values, wipe out the parameter/local variable space and replace it with the callee's parameter/local variable space.

Thus the total amount of stack space used for the series of recursive calls is the same as the stack space used for a single call: extremely beneficial when compared to recursion without such optimization (where the stack space used is the number of calls TIMES the space for a single call).

Replacing tail recursion with iteration

We can carry out a simple translation from a tail-recursive structure into an interative (loop) structure.

Consider the almost-tail-recursive C function shown below, and the iterative translation of it afterward:

int factorial(int N)
{
   if (N <= 1) return 1;  // base case and value
   return N * factorial(N-1);  // recursive call
}

int factorial(int N)
{
   int result;                // accumulator to hold iteratively-computed value
   result = 1;                // base value from original
   while (!(N <= 1)) {        // negative of original base case
      result = N * result;    // returned expression from above
      N = N - 1;              // updated parameter
   }
   return result;             // final result
}
Note that the general translation is based on the following structure:
returntype functionname(Param1, Param2, Param3, ..., ParamP)
{
     if (BaseCase) return BaseValue;

     MainLoopActions;

     return Value Operator functionname(RevisedParam1, ... , RevisedParamP)
}
The corresponding translation would then be:
returntype functionname(Param1, Param2, ..., ParamP)
{
   Result = BaseValue;
   while (!BaseCase) {
      MainLoopActions;
      Param1 = RevisedParam1;
      ...
      ParamP = RevisedParamP;
      Result = Value Operator Result;
   }
   return Result;
}
This kind of translation could be applied by any interpretter or compiler working with recursive calls in any language: i.e. it could be applied to translate a tail recursive Scheme function into machine code instructions corresponding to a while loop.

If the Scheme code were being run on a typical von Neumann architecture this would produce far more efficient results than if the true recursive structure were used.

To take advantage of this, we often have our "standard" recursive Scheme function call a base case of a tail recursive version, which typically introduces intermediate variables to compute partial results en route to producing the desired final answer.

This is clearly the approach used in the efficientfib routine discussed earlier:

(define 
    efficientfib (lambda (i N fibi fibiminus1)
    (cond
        ((equal? N (+ 1 i)) (+ fibi fibiminus1))
        (else (efficientfib (+ i 1) N (+ fibi fibiminus1) fibi))
    )
))