We will follow the contents of the notes from '01 spring (pdf); the code shown in lecture has a couple of differences from the code in those notes:


The complete code from lecture:
;; A name is a symbol (a city's name).
;;
;; A city is
;;   (make-struct [name] [list-of names])
;;
(define-struct city (name neighbors))

; Examples of cities:
; No routes at all:
(define pre1905 (list (make-city 'hou  '())
                      (make-city 'nyc  '())
                      (make-city 'dc   '())
                      (make-city 'la   '())
                      (make-city 'slc  '())
                      (make-city 'reno '())
                      (make-city 'sf   '())
                      (make-city 'nome '())))

; Routes but no cycles
; (no flights from a city to one preceding it alphabetically)
(define dag     (list (make-city 'dc   '(nyc sf))
                      (make-city 'hou  '(nyc la sf))
                      (make-city 'la   '(nyc slc sf reno))
                      (make-city 'nome '())))
                      (make-city 'nyc  '(sf))
                      (make-city 'reno '(slc))
                      (make-city 'slc  '(sf))
                      (make-city 'sf   '())

; Routes with cycles:
(define routes-1 (list (make-city 'slc  '(nyc))
                       (make-city 'nyc  '(slc))))

; This inspires an even more basic test case:
(define routes-0 (list (make-city 'slc  '(slc))))
; (Note that empty isn't a fun test case, since src
;  and dest are requried to be valid city names.)

(define routes-big (list (make-city 'hou  '(la slc nyc))
                         (make-city 'nyc  '(dc hou la slc sf))
                         (make-city 'dc   '(dc hou nyc))
                         (make-city 'la   '(sf reno))
                         (make-city 'slc  '(reno))
                         (make-city 'reno '(slc))
                         (make-city 'sf   '(slc))
                         (make-city 'nome '())))


;; neighbors-of: symbol list-of-city --> list-of-symbol
;; Return the list of names of neighbors of "where".
;; Causes an error if symbol names no cities, or more than one city.
;;
(define (neighbors-of where routes)
  (local [(define lookup-results 
                  (filter (lambda (c) (symbol=? where (city-name c)))
                          routes))]
    (cond [(empty? lookup-results) 
           (error 'neighbors-of 
                  (format "City ~s not in ~s~n." where routes))]
          [(cons?  (rest lookup-results))
           (error 'neighbors-of 
                  (format "City ~s occurs more than once in ~s.~n" 
                          where routes)])

          ; Hopefully we always take this cond-branch:
          [else (city-neighbors (first lookup-results))])))

;;(neighbors-of 'slc  empty)
;(neighbors-of 'hou  pre1905) = empty
;(neighbors-of 'hou  dag) = '(nyc la sf)




;; path: symbol, symbol, list-of-city --> list-of-symbol or false.
;; Return false if no path possible; otherwise return a list of names
;; leading from src to dest (including both those endpoints).
;;
(define (path src dest routes)
  (cond [(symbol=? src dest) (list src)]
        [else (local [(define other-ways (map (lambda (c1) (path c1 dest routes))
                                              (neighbors-of src routes)))
                      (define other-successes (filter list? other-ways))]
                (if (empty? other-successes)
                    false
                    (cons src (first other-successes))))]))



;;;;;; Lecture note: try this, but "(filter list ...)".
;;;;;; What is happening?
;;;;;; Apologize for using advanced level.


  
(path 'hou 'nyc (list (make-city 'hou  '())
                      (make-city 'nyc  '())))
= false


(path 'slc 'nyc (list (make-city 'slc  '(nyc reno nyc))
                      (make-city 'reno '(nyc))
                      (make-city 'nyc  '())))
= '(slc nyc)
                
;(path 'slc  'nyc  routes) = false
;(path 'hou  'reno routes) = '(hou slc reno)




      

;; With accumulator -- not of the answer,
;; but of the cities previosly seen.
;; If we encounter a seen city, stop -- we alraedy know
;; no path from there (ish).
;;
(define (path-v2 src dest seen routes)
  (cond [(symbol=? src dest) (list src)]
        [(member src seen) false]
        [else (let* {[other-ways (map (lambda (c1) (path-v2 c1 dest (cons src seen) routes))
                                      (filter (lambda (n) (not (member n seen)))
                                              (neighbors-of src routes)))]
                      [other-successes (filter list? other-ways)]}
                (if (empty? other-successes)
                    false
                    (cons src (first other-successes))))]))

;(path-v2 'slc 'nyc empty (list (make-city 'slc  '(slc nyc reno nyc))
;                               (make-city 'reno '(nyc slc nyc))
;                               (make-city 'nyc  '(reno slc nyc))))
;= '(slc nyc)



#|
Note: there is a bug in this code;
can you find it?
Hint: make a city with a flight to itself (say, a sight-seeing tour).
See if your test cases come up with a buggy answer!
|#



;; path-v3: src:name dest:name seen:list-of-name routes:list-of-city
;;         --> (list false-or-list-of-name list-of-name)
;; Like path, except that we return a list of cities in the solution (or false),
;; AND a list of cities seen so far.
;; We do this so we never visit the same city twice, even in
;; separate branches of a recursive call:
;; We keep an accumulator of seen-cities, as path-v2,
;; but we do more: When visiting the 2nd neighbor of a city,
;; we will pass it all the cities seen during the processing of
;; the first neighbor.  How?  Well, we need to return
;; two things: the solution-or-false, AND the list of cities
;; seen in toto.  THat way, we use this part of the return value
;; from the first city, to pass it on to the second city.
;;
(define (path-v3 src dest seen routes)
  (cond [(symbol=? src dest) (list (list src) (cons src seen))]
        [(member src seen)   (list false       (cons src seen))]
        [else  
         (local 
             [(define try (path-from-list (neighbors-of src routes)
                                          dest 
                                          (cons src seen)
                                          routes))
              (define soln-so-far (first try))
              (define seen-more   (second try))]
           (cond [(list? soln-so-far)    ; Yay, we found it!
                  (list (cons src (first try)) seen-more)]
                 [(boolean? soln-so-far) ; Nope, no path; return false.
                  (list false seen-more)]
                 [else (error 'path-v3 "uh-oh: i goofed. Given ~s.~n" try)]))]))


;; path-from-list: nearby:list-of-name dest:name seen:list-of-name routes:list-of-city
;;   --> (list false-or-list-of-name list-of-name)
;; A helper for path-v3: In the returned list, the first item is either
;; a list of names (from some city in nearby to dest), or false if no such city.
;; The second item returned is a list of cities seen altogether,
;; even in different branches of a recursive call.
;;
;;
(define (path-from-list nearby dest seen routes)
  (cond [(empty? nearby) (list false seen)]
        [(cons? nearby)  
         (local [(define try-first (path-v3 (first nearby) dest seen routes))
                 (define path-from-first (first try-first))
                 (define seen-from-first (second try-first))]
           (cond [(list? path-from-first)    ; Yay, found path to dest from the first city!
                  try-first]
                 [(boolean? path-from-first) ; No luck from first city; try the next, but acknowledge seen-from-first.
                  (path-from-list (rest nearby) dest seen-from-first routes)]
                 [else (error 'path-from-list "uh-oh: i goofed. Given ~s.~n" try)]))]))
                   
                       


(path-v3 'slc 'nyc empty (list (make-city 'slc  '(slc nyc reno nyc))
                               (make-city 'reno '(nyc slc nyc))
                               (make-city 'nyc  '(reno slc nyc))))
= '(slc nyc)

(path-v3 'slc 'nyc empty routes)