Relate missionaries & cannibals to the airplane-path finding: They are both looking for a path in a network. One difference: in find-path we saw the whole map all at once. In miss/cann, we had to generate the portions of the map as we came to them. We also created multiple copies of the same state. (Advanced problem (comp212): how to avoid copies .. efficiently?) Another difference: breadth-first (miss/cann) vs depth-first. Which guarantees finding the shortest path? Both have their uses; see comp212, comp314.

Finish up the class example from last time: we got find-path to work by marking cities previously seen. But -- when we try to run it again, it doesn't find the path!

vectors:

Vectors are structures, except that you access fields by number, not by name. (N.B. count from zero.)
  (define data (vector 'four 'score 'and 'twenty 'years 'ago))
  (vector-ref data 2) = 'score
  (vector-ref data 6) : error
  (define nums (vector 10 12 14 16 18))
Note that in comments, we'll often write data[i] rather than (vector-ref data i). Write a function:
  ;; sum-vec: vector(v), natNum(n) --> num
  ;; Return the sum of the first n elements of v: i.e., v[n-1]...v[0]

When should you use vectors of info, as opposed to lists of info?

Two other ways to construct vectors:

  (make-vector  [n] [val] )
  (build-vector [n] [rule])
Ex: Create a vector of 4 elements, so that the i'th element is i^2.
Now change the 4th element (element#3), so it is 29.


Return exams. See answer key, soln.
A couple notes: waterways (#3) follows template directly.


Back to vectors:
Write make-vector, using build-vector. And vice versa.
  (define (build-vector sz rule)
    (local [(define ans-vec (make-vector sz #f))
            (define (fill-vec! v n rule) ...)]
       (begin (fill-vec! ans-vec sz rule)
              ans-vec)))

It just remains to write fill-vec!: vec,natNum,(natNum-->value) --> (void) which sets the first n elements according to "rule":

(define (fill-vec! v n rule)
  (cond [(zero? n) (void)]
        [else (begin (vector-set! v (sub1 n) (rule (sub1 n)))
                     (fill-vec! v (sub1 n) rule))]))
(Is this tail-recursive? Yep.)

Could we write this going from 0 up to n-1, instead of n downto 0? Sure!

(define (fill-vec! v n rule)
  (fill-vec-helper! v 0 n rule))


(define (fill-vec-helper! v i n rule)
   (if (>= i n)
       (void)
       (begin (vector-set! v i (rule i))
              (fill-vec-helper! v (add1 i) n rule))))
Note, there are really two different things here:
  1. given i, do a task involving i: (vector-set! v i (rule i))
  2. rig the function to do this task, with i being 0, 1, 2, ..., n-1.

Hey, let's separate these two things explicitly. Example:
Write some code which takes a vector "nums" of numbers, and triples each value:

(fori= 0
       (vector-length nums) 
       (lambda (i) (vector-set! nums i (* 3 (vector-ref nums i)))))

;; fori=: num, num, (num-->(void)) --> (void)
;;
(define (fori= start-val stop-val body!)
  (if (>= start-val stop-val)
      (void)
      (begin (body! i)
             (fori= (add1 start-val) stop-val body!))))

Think about: what if we frequently wanted to do something to every other number:
should we modify the task (body!), or the control-logic in charge of applying body! to the right values? Both ways work; the latter is better.

Think about: what if we wanted to cumulate the vector? What if we want to triple every-other number? (Or, we wanted to cumulate downward?)
A more general version of fori=:
give it a starting value, a way to generate the next number, and a way to know when it's done: /* WHETHER TO CONTINUE (for C analogy) */ Example: (fori= n zero? sub1 ...body...) You write: a version which uses this new improved fori= to call the body with every-other value of i: What is the stopping condition? How to get the next value?

(fori= 0 (lambda (i) (>= i (vector-length nums))) (lambda (i) (+ i 2))
       ...body...)


(define (fori= start stop? next body!)
  (if (stop? start)
      (void)
      (begin (body! start)
             (fori= (next start) stop? next body!))))
We'll return to this in a while, to use fori= to even help with non-vectors.

Do we need to use mutation, to make use of a for-loop? No, we actually have been doing stuff all along which didn't: Recall:

(countdown 3) = (list 3 2 1 'blastoff)
(triangle-number 3)  = 3 + 2 + 1 = 6


(define (countdown n)
  (fori=-acc 0 n
             '(blastoff)
             (lambda (i so-far) (cons (add1 i) so-far))))

(define (triangle-number n)
  (fori=-acc 1 (add1 n)
             0
             (lambda (i so-far) (+ i so-far))))
Exercise: write a version of fori= which separates the work that you're doing from of doing that work with i, then with i-1, ..., then with 1, then with 0. (Note here, the sum is initially 0).
(define (fori=-acc strt stop init body )
   (cond [(>= stop start) init]
         [else (fori=-acc (add1 strt) stop body (body strt init))]))
Let's see how it actually works: A hand-evaluation quickly shows:
  (countdown 3) 
= (fori=-acc 0 3  '(blastoff)        (lambda (i so-far) (cons (add1 i) so-far)))
= (fori=-acc 1 3  '(1 blastoff)      (lambda (i so-far) (cons (add1 i) so-far)))
= (fori=-acc 2 3  '(2 1 blastoff)    (lambda (i so-far) (cons (add1 i) so-far)))
= (fori=-acc 3 3  '(3 2 1 blastoff)  (lambda (i so-far) (cons (add1 i) so-far)))
= '(3 2 1 blastoff)
We can use this to write, for example, functions from missionaries and cannibals:
;; boatloads<=n: num --> list-of-boatloads
;; Return all boatloads containing n or fewer people.
;;
;; Implementation: 
;; Make a boatloads with exactly k people, for all k in [1..n].
;;
(define (boatloads<= n)
  (fori=-accum 1 (add1 n)
               empty
               (lambda (i so-far) (append (boatloads= i) so-far))))

;; boatloads=: num --> list-of-boatloads
;; Return all boatloads containing exactly k people.
;;
;; Implementation: 
;; make a boatload with i missionaries and k-i cannibals, for i in [1..k].
;;
(define (boatloads= k)
  (fori=-accum 1 (add1 k)
               empty
               (lambda (i so-far) (cons (make-boatload i (- k i)) so-far))))

Aside, not covered in lecture: Question: how would you write a "while" loop: e.g. Note how for-loops are good for functions which process natNums; while-loops are good for generative recursion!

What are the parts in common here? That is, what should be the argumetns to "while"?

(define (while done? generate-next-val curr-val)
  (cond [(done? curr-val) curr-val]
        [else (while done? generate-next-val (generate-next-val curr-val))]))
For fun, let's use "local" to avoid repeating arguments:
(define (while done? generate-next-val curr-val)
  (local [(define (while-helper curr)
            (cond [(done? curr) curr]
                  [else (while-helper (generate-next-val curr))]))]
     (while-helper curr-val)))

; Example of using while:
(define (mergesort-bottomup lolon)
(while length>1? merge-all-pairs lolon))

;; NOTE: BAD EXAMPLE:
Let's try merge-all-pairs as a while-loop:

(define (merge-all-pairs lolon)
  (while length>1?
         (lambda (lolon) (cons (merge (first lolon) (second lolon))
                               (merge-all-pairs (rest (rest lolon)))))
         lolon))
This doesn't turn out so cleanly:
the inner lambda really does all the work (and the while does nothing -- exceedingly misleading!) The problem is that in the generative template, we created a subproblem and solved that one subproblem (merging the first two), but that's not our final answer! (we need to structurally recur down the entire list). It suggests that that work is mostly structural, not generative. It could be worked around of course -- we could have "curr" be not just a lolon, but a lolon and also the "next stage" being built up. That's essentially a second accumulator.

Hmmm -- this is actually the generalization of the template for generative recursion! when there is only one sub-problem. You could write super-while, which took in done?, curr, the n functions to create n subproblems (recur on each of those), and one more function to put together everything.

Thus we see that for-loops and while-loops are both specail cases of recursion: in particular, for loops are more idiomatic for structural recursion, and while loops more idiomatic for generative recursion.
(Something I'd never noticed while using languages which have this built-in; a language which separates these issues outs helps me better understand programming!)

For Those Who Are Bored:

Okay, end the digression on "while".


The complete code from lecture:
;; (no separate code for this lecture, yet.)