Recall the find-flights program that we wrote in the last class:

;; find-flights : city city route-map -> (listof city) or false
;; creates a path of flights from start to finish
(define (find-flights start finish rm)
  (cond [(symbol=? start finish) (list start)]
	[else (local [(define possible-route
			(find-flights/list (direct-cities start rm)
					   finish rm))]
   	        (cond [(boolean? possible-route) false]
		      [else (cons start possible-route)]))]))

;; direct-cities : city route-map -> (listof city)
;; return list of all cities in route map with direct flights from given city
(define (direct-cities from-city rm)
  (local [(define from-city-info
	    (filter (lambda (c) (symbol=? (city-info-name c) from-city))
		    rm))]
    (cond [(empty? from-city-info) empty]
	  [else (city-info-fly-to (first from-city-info))])))
	  
;; find-flights/list :
;;        (listof city) city route-map -> (listof city) or false
;; finds a flight route from some city in the input list to the destination,
;;   or returns false if no such route exists
(define (find-flights/list loc finish rm)
  (cond [(empty? loc) false]
	[else (local [(define possible-route
			(find-flights (first loc) finish rm))]
		(cond [(boolean? possible-route)
		       (find-flights/list (rest loc) finish rm)]
		      [else possible-route]))]))

The program find-flights/list uses a common algorithmic technique that
we call backtracking.  In backtracking, we try one potential solution
to a solution, then go back and try another potential solution if the
first one fails.  You'll see this technique come in handy on many
problems.

What's the termination argument on find-flights?  Each recursive call
looks for a route that uses fewer flights.  Eventually, each path must
eventually end in the finish city or have no outgoing flights.

This program works fine on our initial route map.  Let's try the
following route map, which adds a flight from Dallas to Houston:

  (define routes2
     (list (make-city-info 'Houston (list 'Dallas 'NewOrleans))
           (make-city-info 'Dallas (list 'Houston 'LittleRock Memphis))
           (make-city-info 'NewOrleans (list 'Memphis))
           (make-city-info 'Memphis (list 'Nashville))))

Let's write down the calls that occur when we try to find a route from
Houston to Memphis in the new route map.

  (find-flights 'Houston 'Memphis routes2)
  (find-flights/list (list 'Dallas 'NewOrleans) 'Memphis routes2)
  (find-flights 'Dallas 'Memphis routes2)
  (find-flights/list (list 'Houston 'LittleRock 'Memphis) 'Memphis routes2)
  (find-flights 'Houston 'Memphis routes2)
  ...

Notice that we end up with an infinite loop.  What happened?  First,
this tells us that our termination condition was wrong.  Our previous
condition assumes that there are no cycles in the route.  However,
here's a map where we do have cycles, and it's clear that our approach
won't terminate.  Therefore our current program only works on route
maps with no cycles.

Why does the program break on cycles?  Because our program has no
knowledge about which cities it has already tried.  Each recursive
call is independent of all of the others.  In this problem, however,
we want to remember which cities we have already tried.  How could we
give our program this knowledge?

We could add a parameter to find-flights that stores the cities that
have already been visited.  If we attempt to find flights from a city
on the list, find-flights should return false to truncate the search
through that path.  Here's the new code:

;; find-flights : city city route-map (listof city) -> (listof city) or false
;; creates a path of flights from start to finish
(define (find-flights start finish rm visited)
  (cond [(symbol=? start finish) (list start)]
	[(memq start visited) false]
	[else (local [(define possible-route
			(find-flights/list (direct-cities start rm)
					   finish rm
					   (cons start visited)))]
   	        (cond [(boolean? possible-route) false]
		      [else (cons start possible-route)]))]))

;; direct-cities : city route-map -> (listof city)
;; return list of all cities in route map with direct flights from given city
(define (direct-cities from-city rm)
  (local [(define from-city-info
	    (filter (lambda (c) (symbol=? (city-info-name c) from-city))
		    rm))]
    (cond [(empty? from-city-info) empty]
	  [else (city-info-fly-to (first from-city-info))])))
	  
;; find-flights/list :
;;      (listof city) city route-map (listof city) -> (listof city) or false
;; finds a flight route from some city in the input list to the destination,
;;   or returns false if no such route exists
(define (find-flights/list loc finish rm visited)
  (cond [(empty? loc) false]
	[else (local [(define possible-route
			(find-flights (first loc) finish rm visited))]
		(cond [(boolean? possible-route)
		       (find-flights/list (rest loc) finish rm visited)]
		      [else possible-route]))]))

What's the termination argument now?  Each city can serve as the start
to at most one search for flights.  Since we have a finite number of
cities in the graph, the search must eventually stop.

We call a parameter like visited an accumulator, because it
accumulates information over the course of a computation.  In the next
few classes, we're going to look at various aspects of designing
programs that use accumulators.

There is one small problem with the above solution: it changed the
contract on find-flights.  Could we avoid that?  Yes, by using a
helper function.  This helper function, new-find-flights, has the same
contract as the original find-flights.  However, it controls the
initial value passed to visited, which prevents someone from calling
your code with the wrong initial value.

;; new-find-flights : city city route-map -> (listof city) or false
;; creates a path of flights from start to finish
(define (new-find-flights start finish rm)
  (find-flights start finish rm empty))

As an exercise, use local to hide the accumulator version of
find-flights inside new-find-flights.  Where can you refrain from
passing parameters in this new version?

Let's turn to another example.  A company maintains a list for each
employee of how many sick days that person earned or used in each
month.  For example, (list 2 -1 1) indicates that a person earned
2 sick days the first month, used 1 in the second month, and earned 1
in the third month.  Employees are allowed to accumulate sick days.
The company needs a program that consumes the list of sick days earned 
or used per month and returns a list showing how many sick days the
employee has available at any given month.

;; available-days : (listof number) -> (listof number)
;; computes sick days available per month based on those earned per month
(define (available-days alosd) ...)

(available-days (list 1)) = (list 1)
(available-days (list 2 -1 1)) = (list 2 1 2)

Let's write a purely structural recursive solution first.

;; 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))

What are the calls that result when we compute (available-days (list 2 -1 1))?

  (available-days (list 2 -1 1))
= (cons 2 (add-to-each 2 (available-days (list -1 1))))

= (cons 2
    (add-to-each 2
      (available-days (list -1 1))))

= (cons 2
    (add-to-each 2
      (cons -1
        (add-to-each -1
          (available-days (list 1)))))

= (cons 2
    (add-to-each 2
      (cons -1
        (add-to-each -1
	  (cons 1
            (add-to-each 1 (available-days empty))))))

Notice how often we end up traversing the lists in order to process
all the calls to add-to-each.  Consider the last element in the list.
By the time we finish this program, we will end up adding all of the
previous elements to the last element.  Can we use an accumulator to
reduce the amount of traversing that we need to do?

;; avail-days-accum : (listof number) num -> (listof number)
;; uses an accumulatpr tp compute sick days available per month based
;; on those earned per month 
(define (avail-days-accum alosd accum-days)
  (cond [(empty? alosd) empty]
	[else (cons (+ (first alosd) accum-days)
		    (avail-days-accum (rest alosd) 
                                      (+ (first alosd) accum-days)))]))

How should we call the new function?

	(avail-days-accum (list 2 -1 1) 0)

As we did for new-find-flights, we should define a function with the
same contract as the original function.  Our accumulator version
should be defined as a local function within the new function.

;; available-days : (listof number) -> (listof number)
;; computes sick days available per month based on those earned per month
(define (available-days alosd)
  (local [(define (avail-days-accum alosd accum-days)
	    (cond [(empty? alosd) empty]
		  [else (cons (+ (first alosd) accum-days)
			      (avail-days-accum 
                                (rest alosd) 
				(+ (first alosd) accum-days)))]))]
    (avail-days-accum alosd 0)))