CSC 153  Grinnell College  Spring, 2005 
Computer Science Fundamentals  
Laboratory Exercise  
This laboratory provides further analysis of recursion and tail recursion giving examples of how nontailrecursive procedures can be rewritten to become tail recursive.
Consider the following two solutions to the problem of finding the sum of a list of numbers.
The first solution seems to follow a nowfamiliar recursive format:
(define sum (lambda (ls) (if (null? ls) 0 (+ (car ls) (sum (cdr ls))) ) ) )
The second solution adds a running sum parameter.
(define sum (lambda (ls) (sumkernel ls 0) ) ) (define sumkernel (lambda (ls runningsum) (if (null? ls) runningsum (sumkernel (cdr ls) (+ runningsum (car ls))) ) ) )
Change the define statement to tracedefine for both versions of sum above. (For the second version, also use tracedefine to declare sumkernel.) Run each version of sum on the list '(1 2 3 4), as in the above example. Comment on how the traces of these versions relate to the call diagrams in the readings for this lab.
Next consider some solutions for finding the maximum value within a list of numbers.
(define maximum (lambda (ls) (cond ((null? (cdr ls)) (car ls)) ((< (car ls) (maximum (cdr ls))) (maximum (cdr ls))) (else (car ls)) ) ) )
Change define to tracedefine and run the code for (maximum '(2 6 4)). How many times is maximum called? Explain briefly how this number is obtained.
Draw a diagram, such as the ones in the reading, to trace the procedure calls for the procedure call (maximum '(2 6 4)). Be sure your diagram agrees with the count determined from step 2.
Using an additional parameter in a kernel procedure:
(define maximum (lambda (ls) (maximumkernel (car ls) (cdr ls)) ) ) (define maximumkernel (lambda (maxsofar lst) (cond ((null? lst) maxsofar) ((< (car lst) maxsofar) (maximumkernel maxsofar (cdr lst))) (else (maximumkernel (car lst) (cdr lst))) ) ) )
As before, change define to tracedefine and run the code for (maximum '(2 6 4)). How many times are maximum and maximumkernel called? Explain briefly how this number is obtained.
Draw a diagram, such as the ones above, to trace the procedure calls for the procedure call (maximum '(2 6 4)).
Explain why this approach is more efficient that the previous solution.
The following is a variation of the Solution 2.
(define maximum (lambda (ls) (maximumkernel (car ls) (cdr ls)) ) ) (tracedefine maximumkernel (lambda (maxsofar lst) (if (null? lst) maxsofar (maximumkernel (if (< (car lst) maxsofar) maxsofar (car lst)) (cdr lst) ) ) ) )
Again, change define to tracedefine and run the code for (maximum '(2 6 4)). How many times are maximum and maximumkernel called? Explain briefly how this number is obtained.
Draw a diagram, such as the ones above, to trace the procedure calls for the procedure call (maximum '(2 6 4)).
Compare the efficiency of Solution 3 with that of Solutions 1 and 2. What conclusions can you make about the various solutions? Which solution do you prefer? Justify your preference briefly.
Run the traces again, this time for the list '(2 6 4 5)). Explain how the patterns you observed for a 3element list extend to this list of 4element list.
Since an average requires both a sum of the items and a count of the number of items present, any solution must do both tasks. A tail recursive approach to this problem uses parameters to keep a running sum and a running count of the number of items processed.
Since three results are desired, we must decide how these results should be returned. One natural approach would be to place all three results on a single list, with the maximum first, the minimum second, and the average third.
Write a tail recursive solution to this problem, using a single kernel procedure. The kernel procedure may have as many parameters as desired.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~walker/courses/153.sp05/labs/labtailrecursion.shtml
created 7 March 1997 last revised 16 February 2006 

For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. 