Let's write a program reverse, which reverses a list of elements.  For
a first version, let's use simple structural recursion.

;; reverse : (listof alpha) -> (listof alpha)
;; constructs the reverse of a list of items
(define (reverse aloa)
  (cond [(empty? aloa) empty]
	[else (make-last-item (first aloa)
			      (reverse (rest aloa)))]))

;; make-last-item : alpha (listof alpha) -> (listof alpha)
;; adds an elt to the end of a list
(define (make-last-item an-elt aloa)
  (cond [(empty? aloa) (list an-elt)]
	[else (cons (first aloa)
		    (make-last-item an-elt (rest aloa)))]))

What does a call to reverse look like?

  (reverse (list 1 2 3))
= (make-last-item 1 (reverse (list 2 3)))
= (make-last-item 1 (make-last-item 2 (reverse (list 3))))
= (make-last-item 1 (make-last-item 2 (make-last-item 3 (reverse empty))))
= (make-last-item 1 (make-last-item 2 (make-last-item 3 empty)))
= (make-last-item 1 (make-last-item 2 (make-last-item 3 empty)))
= ...

In order to process all of these nested calls to make-last-item, we
will end up traversing the end of the original list many times, once
for each element in the original list as a matter of fact.  This seems
like a potential waste of computation.  Once we realize this, we may
wish to consider using an accumulator to simplify the program.  First,
let's consider a question though -- is there any way that we could
have detected this pattern from the original program, without working
out an example by hand?

This pattern seems similar to what we saw in the structural version of
available-days in the last class.  Let's compare the structural
versions of those two programs to see if we detect any similarities.
Here's the definition of available-days as a reminder:

;; available-days : (listof number) -> (listof number)
;; computes sick days available per month based on those earned per month
(define (available-days alosd)
  (cond [(empty? alosd) empty]
	[else (cons (first alosd)
		    (add-to-each (first alosd) 
                                 (available-days (rest alosd))))]))

(define (add-to-each n alon)
  (map (lambda (x) (+ x n)) alon))

Notice that in each of reverse and available-days, we pass the return
value from the recursive call to another recursive procedure.  This
pattern is what gives rise to the behavior that we noticed in the hand
evaluation.  Thus, we can formulate a rule for when to consider using
an accumulator:

  Consider using an accumulator if the program processes the return
  value of a recursive call with another recursive program.

The rest of this lecture discusses a process for transforming a
program without an accumulator to one with an accumulator.  Let's do
this for reverse.

First, recall what we talked about in the last class regarding writing
a wrapper for accumulator programs, so that we maintain the interface
of our original program.  Thus, we can write a skeleton for the new
reverse program as follows:

;; reverse : (listof alpha) -> (listof alpha)
;; constructs the reverse of a list of items
(define (reverse aloa)
  (local [(define (rev a-loa accum)
	    (cond [(empty? a-loa) ...]
		  [else
		   ... (rev (rest a-loa) ...
			    (first a-loa) ... accum ...) ]))]
     (rev aloa ...)))

Notice that the skeleton for rev is a little different from our usual
template for structural recursion.  We've moved the (first a-loa) into
the recursive call for rev.  Why?  Because rev will probably combine
(first a-loa) and the current value of the accumulator to get the new
value of the accumulator.

Now, we're ready to start filling in the body of the program.  But we
can't really do that until we decide what the accumulator is
accumulating (a count of items in the list, certain items in the list,
etc).  Therefore, before you start filling in the program, you must
write down a statement to describe the contents of the accumulator.
We put this in a comment above the definition of rev:

;; reverse : (listof alpha) -> (listof alpha)
;; constructs the reverse of a list of items
(define (reverse aloa)
  (local [;; accum : contains the reversed list of items in aloa 
          ;;  that precede a-loa
          (define (rev a-loa accum)
	    (cond [(empty? a-loa) ...]
		  [else
		   ... (rev (rest a-loa) ...
			    (first a-loa) ... accum ...) ]))]
     (rev aloa ...)))

This comment should do two things: it should describe the type of the
accumulator variable and it should describe some property of the value
in the accumulator.  We think of this property as an invariant, i.e.,
something that should hold after each recursive call to rev.

Now that we understand what the accumulator represents, we can write
the rest of the program.

;; reverse : (listof alpha) -> (listof alpha)
;; constructs the reverse of a list of items
(define (reverse aloa)
  (local [;; accum : contains the reversed list of items in aloa 
          ;;  that precede a-loa
          (define (rev a-loa accum)
	    (cond [(empty? a-loa) accum]
		  [else
		   (rev (rest a-loa) 
			(cons (first a-loa) accum))]))]
     (rev aloa empty)))

The most important step in this process was the development of a
description for the accumulator.  Until you understand (i.e., can
write down) what the accumulator represents, you won't be able to
write the accumulator version of the program.