Continuing our study of vectors, let's write a function
vector-foreach.  Like for-each on lists, vector-foreach should consume
a function and a vector and apply the function to every element in the
vector, producing no output:

;; vector-foreach : (alpha num[ beta) (vectorof[size N] alpha) -> void
;; applies function f to each element of vector v
(define (vector-foreach f v)
  (local [(define len (vector-length v))
	  (define (helper index)
	    (cond [(= index len) void]
		  [else
		   (begin
		     (f index (vector-ref v index))
		     (helper (add1 index)))]))]
    (helper 0)))

> (define v (vector 1 2 3 4 5))
> (vector-foreach (lambda (i n) (add1 n)) v)
void
> v
(vector 1 2 3 4 5)

Why didn't this change v?  Remember, the call to f is the first
statement inside a begin.  If f doesn't involve set! or set-structure!
(which includes vector-set!), it won't have any effect.  We need to
fix the function as follows:

> (vector-foreach (lambda (i n) (vector-set! v i (add1 n))) v)
void
> v
(vector 2 3 4 5 6)

This example demonstrates why f needs to take the index as an
argument.  If the argument function to vector-foreach uses
vector-set!, it has to have the current index.

As for lists, we could write a more general abstract function,
vector-foldr.  As with foldr on lists, vector-foldr can produce any
kind of output, based on the base and combine function provided:

;; vector-foldr : 
;;  ((num[ beta) beta (vectorof[size N] alpha) -> beta
;; combines all elements of vector v using function combine with given 
::   base.
(define (vector-foldr combine base v)
  (local [(define len (vector-length v))
	  (define (helper index)
	    (cond [(= index len) base]
		  [else
                   (combine index (vector-ref v index)
                            (helper (add1 index)))]))]
    (helper 0)))

> (define v (vector 1 2 3 4 5))
> (vector-foldr (lambda (index vec-elt res-rest) 
                  (vector-set! v index (add1 vec-elt)))
                void v)
> v
(vector 2 3 4 5 6)
> (vector-foldr (lambda (index vec-elt res-rest)
                  (cons vec-elt res-rest))
                empty v)
(list 2 3 4 5 6)
> 

What's the difference between vector-foldr and vector-foldl?
Vector-foldl starts with an index one less than the length of the
vector and stops when the index falls below 0:

(define (vector-foldl combine base v)
  (local [(define len (vector-length v))
	  (define (helper index)
	    (cond [(< index 0) base]
		  [else
                   (combine index (vector-ref v index)
                            (helper (sub1 index)))]))]
    (helper (sub1 len))))

Notice the similarities between vectors and lists.  For lists, we have
build-list and length; for vectors we have build-vector and
vector-length.  We've now seen three forms of compound data: lists,
vectors, and structures.  How would you characterize each?

lists			structures		vectors
-----------------------------------------------------------------
build-list		define-struct		build-vector
arbitrary size		fixed size		fixed size
accessing ith elt	ith elt is		ith elt is
 requires i		 directly computed	 directly computed
 recursive calls         (struct-field struct)   (vector-ref ...)

Do not abuse vectors!  People commonly use vectors when lists are more
appropriate; sorting is a prime example.  Remember, you should only
use a vector if your data size is fixed and the natural numbers are a
natural way to refer to components of your data.  Otherwise, use
structures (for small fixed data) or lists!

SOME ADDITIONAL POINTS ON OBJECTS

Over the weekend, someone asked whether caves (for the current
assignment) should be structures or objects.  They should be
structures, because a cave is a combination of three pieces of
information (a name, some items, and the doors).  Remember what we use 
objects for: to group together functions that operated on the same
data, where at least one function could modify the data.  We grouped
the function into an object so that all functions could modify/access
the same data structure without that data structure having to be
globally accessible.  We said that having the data structure be
globally accessible was bad because someone else could modify the data 
structure accidentally.  At what point do we actually notice data
structure modifications?  When we try to run other programs.  

This raises an important point about programming.  When we write
programs that interact through data, we can express conditions on
those interactions.  For the address book programs, for example,
lookup should always return the phone number that was most recently
added for name.  If someone can modify the address book, it's possible 
to break this requirement.  Maintaining these requirements, or
invariants, is crucial in programming.  In 280, we'll look at how to
prove these sorts of requirements about programs.

Here's a related problem on objects.  We've just said that the point
of using objects is to protect information.  Consider our banking
program again.  Here's one with a service for creating new accounts:

(define-struct acct (name balance))

(define bank-sys
  (local [(define accounts empty)
          (define (new-account! name bal)
            (local [(define new-acct (make-acct name bal))]
              (set! accounts (cons new-acct accounts))))
	  (define (lookup-bal name) ...)]
    (lambda (msg)
      (cond [(symbol=? msg 'new) new-account!]
	    [(symbol=? msg 'lookup) lookup-bal]
            [else ...]))))

For extra security, the bank wishes to add passwords to their
accounts.  Whenever  the system creates a new account, it should
return a password that the user must send to lookup in order to access 
the account balance.  How would you implement passwords?  Here's a new 
program skeleton:

(define-struct acct (name balance passwd))

(define bank-sys2
  (local [(define accounts empty)
          (define (new-account! name bal)
            (local [(define new-passwd ...)
		    (define new-acct (make-acct name bal new-passwd))]
              (set! accounts (cons new-acct accounts))
	      new-passwd))
	  (define (lookup-bal name passwd)
	    (local [(define match
		      (filter (lambda (acct)
				(and (symbol=? name (acct-name acct))
				     (eq? passwd (acct-passwd acct))))))]
	      (cond [(empty? match) false]
		    [else (first match)])))]
    (lambda (msg)
      (cond [(symbol=? msg 'new) new-account!]
	    [(symbol=? msg 'lookup) lookup-bal]))))

> (define my-password ((bank-sys2 'new) 'kathi 100))
> ((bank-sys2 'lookup) 'kathi my-password)
100

The trick here is to fill in the definition of new-passwd.  What could
you use?  You want something that can't be guessed -- someone can only 
access a password by stealing the password that new-account! returns.
This rules out using numbers, because someone could write a program to 
try numbers in order until they got to my password.  The problem here
is that eq? works on numbers.  For passwords to work, we need a value
that is only eq? if I have the exact password returned when my account 
was created.

This suggests structures.  If I define

	(define-struct passwd ())

and write (make-passwd) in place of the ..., then I get a unique
password.  This works because

	(eq? (make-passwd) (make-passwd))

is false. Thus, uniqueness of structures provides a way to identify
and distinguish structures.  This example is meant to highlight this
feature of eq? on structures, as opposed to eq? on numbers or other
atomic data (such as booleans or symbols).