set-[struct]-[field]!.

So, what does the hand-evaluation of

(cons 3 (cons 4 (cons 5 empty)))
look like? How about for
(cons (make-cat 'morris 93) (cons (make-cat 'garfield 87) empty))
What if , as in previous lectures, cats had an additional field bestbuddy refering to a cat?

Exercise: make the smallest example of you can, of a structure which somehow contains a reference to itself. How does it print out? Remember shared from last lecture.

Now, we can make our map of cities properly, no?

(define-struct city (name nhbrs seen?))

; Initially no neighbors:
(define nyc  (make-city 'nyc  empty false))
(define la   (make-city 'la   empty false))
(define slc  (make-city 'slc  empty false))
(define hou  (make-city 'houston empty false))
(define nome (make-city 'nome empty false))
(define reno (make-city 'reno empty false))

(define cities (list nyc la slc hou nome reno))


(set-city-nhbrs! nyc  (list la slc hou nyc))
(set-city-nhbrs! la   (list reno slc nyc))
(set-city-nhbrs! hou  (list nyc la))
(set-city-nhbrs! slc  (list reno))
(set-city-nhbrs! reno (list slc))

What is our algorithm for searching?

;; path: city, city --> list-of-city or false
;; Return false if no path, otherwise return
;; a path which includes both endpoints.
;;
(define (path src dest)
  (cond [(eq? src dest) (list src)]
        [else (local [(define otherway (path-from-list (city-nhbrs src) dest))]
                 (if (list? otherway)
                     (cons src otherway)
                     false))]))

;; path-from-list: list-of-city, city --> list-of-city or false
;; Return either a path from one of srcs to dest,
;; or false if no such path exist from anybody in srcs.
;;
(define (path-from-list srcs dest)
  (cond [(empty? srcs) false]
        [else (local [(define try-first (path (first srcs) dest))]
                 (if (list? try-first)
                     try-first
                     (path-from-list (rest srcs) dest)))]))

(path slc nyc) = 
;;  Uh-oh, runs forever! (as discussed in lects 24-27)
;;  Hand-evaluation reveals
;;  It's checking slc, reno, slc, reno, slc, ...
N.B. In the class example from lects 24-27, Instead of a separate function path-from-list, we just mapped "find a path to dst" on each of the cities neighbors, and then grabbed the first element from all these solutions (or, checked if map returned a list containing only false from each neighbor). Same diff.

What do we need to do, to avoid loops?

;; path: city, city --> list-of-city or false
;; Return false if no path, otherwise return
;; a path which includes both endpoints.
;;
(define (path src dest)
  (cond [(eq? src dest) (list src)]
        [(city-seen? src) false] ; Already visited, so don't go here again.
        [else 
         (begin
           (set-city-seen?! src false)
           (local [(define otherway (path-from-list (city-nhbrs src) dest))]
             (if (list? otherway)
                  (cons src otherway)
                  false))]))


(find-path slc nyc) = false
(find-path nyc slc) = ; nyc -> la -> slc, printed in "shared" form.
 ; Note, it's not the shortest path.
This approach is called "depth-first search": it searches the first neighbor, and the first of that neighbor, and ... and backtracks when it reaches a dead end (or something seen before).

Missionaries and Cannibals, on the other hand, is a fundamentally *different* way of finding a path from one place to another. "breadth-first search" (look at all places one step away from src, then two steps away, then three steps, ...) *does* always return a shortest path (can you seen why?).


The complete code from lecture:
(define-struct city (name nhbrs seen?))

; Initially no neighbors:
(define nyc  (make-city 'nyc  empty false))
(define la   (make-city 'la   empty false))
(define slc  (make-city 'slc  empty false))
(define hou  (make-city 'houston empty false))
(define nome (make-city 'nome empty false))
(define reno (make-city 'reno empty false))

(define cities (list nyc la slc hou nome reno))


(set-city-nhbrs! nyc  (list la slc hou nyc))
(set-city-nhbrs! la   (list reno slc nyc))
(set-city-nhbrs! hou  (list nyc la))
(set-city-nhbrs! slc  (list reno))
(set-city-nhbrs! reno (list slc))

What is our algorithm for searching?

;; path: city, city --> list-of-city or false
;;
(define (path src dest)
  (cond [(eq? src dest) (list src)]
        [else (local [(define otherway (path-from-list (city-nhbrs src) dest))]
                 (if (list? otherway)
                     (cons src otherway)
                     false))]))

(define (path-from-list srcs dest)
  (cond [(empty? srcs) false]
        [else (local [(define try-first (path (first srcs) dest))]
                 (if (list? try-first)
                     try-first
                     (path-from-list (rest srcs) dest)))]))


;; Okay, how to stop loops?

;; path: city, city --> list-of-city or false
;;
;; Implementation note:
;; As previous version, but 
;; (a) mark a city when seen,  and
;; (b) coming across a marked city means we're trivially done:
;;     don't proceed further from this city.
;; 
(define (path src dest)
  (cond [(eq? src dest)    (list src)]
        [(city-seen? src)  false]
        [else
         (begin
           (set-city-seen?! src true)
           (local [(define otherway (path-from-list (city-nhbrs src) dest))]
               (if (list? otherway)
                   (cons src otherway)
                   false))]))
#|
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!
|#