CSC153 2004S, Class 19: Tail Recursion
Admin:
* Visitor today.
* Reading on tail recursion for Monday.
* Questions on the homework?
Overview:
* Reflecting on sum.
* Comparing kinds of recursion.
* Making procedures tail-recursive.
* Lab.
Important result:
* suml does *not* require extra stack space
* sumr does require extra stack space
* Some would therefore claim that suml is better (particularly Microsoft programmers who hate recursion in principle).
* Claim: We should distinguish between the two kinds of recursion (names)
* Claim: We should try to write our recursive procedures the "better" way
Definition:
* A procedure in which all recursive calls require no further computation is "tail recursive"
* Anything else that is recursive is "not tail recursive"
;;; Procedure:
;;; map
;;; Parameters:
;;; proc, a procedure (that acts on single values)
;;; lst, a list (v1 ... vn)
;;; Purpose:
;;; Apply proc to every element of the list
;;; Produces:
;;; mapped, a list of the form (w1 ... wn)
;;; Preconditions:
;;; [Standard: proc must be a unary procedure, lst must be a list]
;;; proc must be applicable to each vi
;;; Postconditions:
;;; wi = (proc vi) for all reasonable i
(define map
(lambda (proc lst)
(if (null? lst)
null
(cons (proc (car lst)) (map proc (cdr lst))))))
Is this procedure tail-recursive?
* No! We apply cons after the recursive call!
Can we make it tail-recursive?
* Yes
Claim: Any recursive procedure can be made iterative
* Correct. Make a stack of the "stuff to do next" and make that stack a parameter to your kernel/procedure
How?
* Keep track of the items on the list that are already done
(define map2
(lambda (proc lst)
(let kernel ((done null) (remaining lst))
(if (null? remaining)
done
(kernel (cons (proc (car lst)) done) (cdr remaining))))))
What did we do?
* Used a kernel
* Created an extra parameter for the kernel that keeps track of what has been done so far.
Morals:
* Tail recursion is good
* You should try to write procedures tail recursively
Do the lab!