CS 227 Lecture -*- Outline -*- Warning: to be given to students We'll do this differently than the book, in a way that will hopefully explain where the really neat thing in this chapter vector-generator comes from. That is, we do it inductively. focus: vector as mapping, mutation examples * Vectors (Chapter 9) (also called Arrays) Be aware of: different looping style (count up) ** Motivation (9.1) It should be clear that we can do whatever we want with the tools we already have. But in Computer Science, we are also concerned with how much it costs: time and space. *** stories about random access trying to think how to explain random access... leaf through notes, and say "just a minute, I'm looking for where my notes on random access start..." have TA say: didn't you leave a marker there? so I started looking for my book of algorithms, I always start from the top left, go through the top shelf... but next semester I'll be able to find it faster, because I wrote down the location in my notes later when I found the book I tried to look up random access but I had to start at the front and read through the book until I found it... so I wrote down the page number on a bookmark *** Sequential Access Suppose we have a list of a thousand elements. If we want the 900th element, we have the computer do 900 cdr's. If we want to change the 900th element, we have to copy the first 899. ----------------------- SEQUENTIAL vs. RANDOM ACCESS SEQUENTIAL ACCESS (define long-list (list 1 2 3 ... 1000)) (list-ref long-list 900) COST: 900 cdr's (subst 111 900 long-list) COST: 900 cdr's 900 cons reading nth element O(n) time writing nth element O(n) time and space ------------------------ What lists provide is *sequential access*, reading or writing an element takes O(n) time, where n is the length of the list. *** Random Access What we want is *random access*, the ability to read or write any element in O(1) time --- constant time. This is what vectors provide (in the built-in implementation.) -------------------- RANDOM ACCESS (define long-vec (vector 1 2 3 ... 1000)) (vector-ref long-vec 900) COST: 1 unit of time (vector-set! long-vec 900 111) COST: 1 unit of time reading nth element: O(1) time writing nth element: O(1) time -------------------- no extra space needed for writing vector! ** The vector ADT (9.2) In this section we focus on vectors without worrying about mutation. The mutation of vectors is discussed in the next section. Here our procedures take vectors as arguments and return new vectors as results. Abstractly, a vector is a function from {0, ..., n-1} to values. (Like string indexes) (Picture this as a table.) ----------------------------------- A PICTURE OF A VECTOR VALUE < a, b, c, d, e > values 0 1 2 3 4 indexes #( a b c d e ) printed v: (-> ({0,1,2,3,4}) symbol) v(0) = a v(1) = b v(2) = c v(3) = d v(4) = e Def: a (vector T) value is a mapping from {0,1,...,n-1} to objects of type T, where n >= 0 ----------------------------------- the external representation is what Scheme prints. the line "values" is a mathematical notation. ----------------------------------- Def: a (vector T) object is (make-vector n x) or (vector e0 e1 ... en) where n has type natural and x, e0, e1, ..., en have type T ----------------------------------- Note that each such object is unique, calling make-vector twice with the same args produces vectors that are equal? but not eq? (like twins) *** Basic procedures (9.3, pp. 281ff) **** Constructors one way to make a vector is to quote the printed form (quote #(1 2 3)) = (vector 1 2 3) '#(1 2 3) = (vector 1 2 3) '#((+ 1 2)) = (vector '(+ 1 2)) ==> #((+ 1 2)) But there are procedures that do it too ***** vector is like list for lists Try (vector 'a 'b 'c) (vector 1 2 3) ***** make-vector is lays out space, optionally fills it in This type means you can call it with 1 or 2 arguments. Try (make-vector 3) They should not count on the fill value default. Better (make-vector 3 '()) or (make-vector 3 0) But it may be faster to call it without a fill value, they may be uninitialized. **** type test (vector? (vector 1 2 3)) (vector? '#(1 2 3)) (vector? (make-vector 4)) (vector? (cons 'a '())) (vector? (list 1 2 3)) **** observers ***** vector-length (define v (make-vector 7)) (vector-length v) (vector-length (vector 1 2 3)) this is constant time ***** vector-ref (define v2 (vector 0 1 2 3)) (vector-ref v2 0) ; etc., including 4 and -1 (define v3 (vector 'the 'rain 'in 'spain)) (vector-ref v3 3) remind them that it's constant time access **** mutator ***** vector-set! try (define v1 (vector 0 2 4 6 8)) v1 (vector-set! v1 2 999) v1 (define v2 v1) (vector-set! v2 2 77) v2 (vector-ref v1 2) remind them it's constant time Draw picture of this on the board! ***** Convention for names of mutators End in exclamation mark, like commands in english. (imperatives) pronounced "bang" as in "vector set bang". ***** stories about mutation By the way, I've been trying to get my car fixed lately. The engine is knocking and the brakes need fixing, and I wanted it to be painted green. I took it to the dealer, and when I came back, the mechanic said: it's green and as good as new! I said: I can see that, but this is not my car! the mechanic says: yes but it is the same kind of car, it's green, the engine works and the brakes work I say: fine, but I rather liked my old car the mechanic: why? it doesn't work and it's not green. I: well, it had my stuff in it mech: we moved it into this car I: but my registration is for the old car! This one has a different vehicle ID number! mech: oh, well so far I don't know how to fix cars, I can only change them! ***** can use vector-set! to define what eq? does for vectors (eq? v1 v1) (eq? v2 v1) (eq? v2 (vector 0 2 77 6 8)) (equal? v2 (vector 0 2 77 6 8)) two vectors are eq? if they are the same thing (object, address) that's in general what eq? does get 2 dollar bills from the audience, same value, different serial numbers so type of eq? is really (-> (datum datum) boolean) as is equal? (refer them to pages 26-28 again) **** summary ----------------------------------- BASIC PROCEDURES FOR VECTORS vector : (-> (T ...) (vector T)) make-vector : (and (-> (natural T) (vector T)) (-> (natural) (vector datum))) vector?: (-> (datum) boolean) vector-length : (-> ((vector T)) natural) vector-ref: (-> ((vector T) natural) T) ; REQUIRES: the natural number is ; less than the length of the vector. vector-set! : (-> ((vector T) natural T) void) ; REQUIRES: the natural number is ; less than the length of the vector ----------------------------------- Q: What equations hold about vectors? in the following it is assumed that "variables" are side-effect free '#(x y ...) = (vector 'x 'y ...) (vector? (vector x y ...)) = #t (vector? (make-vector n)) = #t (vector? (make-vector n x)) = #t (vector-length (vector x0 x1 ... xn-1)) = n (vector-length (make-vector n)) = n (vector-length (make-vector n x)) = n (vector-ref (vector x0 x1 ... xn-1) i) = xi if 0 <= i < n, (vector-ref (make-vector n x) i) = x if 0 <= i < n the above two equations also hold with = replaced by eq? (let ((v (make-vector n))) (p v) (vector-length v)) = n (let ((v (vector x0 x1 ... xn-1))) (vector-set! v i x) (vector-ref v j)) = (if (= i j) x xj) (eq? v (vector x y ...)) = #f (eq? v (make-vector n)) = #f (eq? v (make-vector n x)) = #f (begin (vector-set! v1 i x) (vector-set! v2 i y) (eq? (vector-ref v1 i) y)) = (eq? v1 v2) if 0 <= i < (vector-length v1) = (vector-length v2) *** Some helpful procedures, examples. **** View ------------------ (view (vector 1 2 3)) =prints=> #(1 2 3) (view (vector 3)) =prints=> #(3) ; assume length > 0 ------------------ plan: print the #( show the elements, separated by " " print the ) do a newline how to show the elements? loop for each i display ith element if there are more, print " " and loop -- Need to print elements in order, so start at 0 and stop when i reaches the highest index. -- Need to separate elements by a space, so only print space if more elements coming. -- Perhaps should use write to display elements, but want to be like program 9.1 (define view ; TYPE: (-> (vector) void) (lambda (vec) (letrec ((loop ; TYPE: (-> (natural) void) (lambda (i) ; REQUIRES: 0 <= i ; and i < (vector-length vec) ; EFFECT: display elements ; of vec from i to highest (display (vector-ref vec i)) (if (< i (sub1 (vector-length vec)) (begin (display " ") (loop (add1 i))))))) (display "#(") (loop 0) (display ")") (newline))))) ; added newline Q: any improvements possible? (use a let to avoid recomputing the length) see homework for others ; - Program 9.1, pg. 269 - (define view ; TYPE: (-> (vector) void) (lambda (vec) (let ((highest-index (sub1 (vector-length vec)))) (letrec ((loop ; TYPE: (-> (natural) void) (lambda (i) ; REQUIRES: 0 <= i ; and i < (vector-length vec) ; EFFECT: display elements ; of vec from i to highest (display (vector-ref vec i)) (if (< i highest-index) (begin (display " ") (loop (add1 i))))))) (display "#(") (loop 0) (display ")") (newline))))) ; added newline **** list->vector We would like to convert lists into vectors. ------------------ (list->vector '()) ==> #() (list->vector '(a b)) ==> #(a b) (list->vector (list 0 1 2 3)) ==> #(0 1 2 3) list->vector : (-> ((list T)) (vector T)) ------------------ A simple and elegant way is: (define list->vector (lambda (ls) (apply vector ls))) But this didn't work in MIT CScheme, probably because vector is defined using list->vector (!) In any case want to do it more directly, to demonstrate the pattern. plan (which will be typical): 1. make a vector of the right length 2. put the elements into the vector by recursing down the list, plopping each car into the appropriate spot in the vector (could use list-ref, but this is more efficient) need to keep an index as we go. 3. When done, return the vector ----------------------- ; compare Program 9.23 (define list->vector ; TYPE: (-> ((list T)) (vector T)) (lambda (ls) (let ((vec (make-vector (length ls)))) (letrec ((convert! ; TYPE: (-> ((list T) natural) ; void) (lambda (ls* i) ; REQUIRES: i <= (length ls), ; ls* = (list-tail ls i), ; for all 0 <= j < i: ; (vector-ref vec j) ; = (list-ref ls j) ; MODIFIES: vec ; EFFECT: put ls* into vec ; starting at index i (if (not (null? ls*)) (begin (vector-set! vec i (car ls*)) (convert! (cdr ls*) (add1 i))))))) (convert! ls 0)) vec))) --------------------- Note the begin, no else part on the if. (this is like a while loop). The REQUIRES clause is a loop invariant. Q: What is true when the loop ends? This is the standard imperative scheme for vector programs. **** vector-copy ---------------------- (vector-copy '#(3 4 5)) ==> #(5 8 9) (eq? v (vector-copy v)) ==> #f (equal? v (vector-copy v)) ==> #t vector-copy : (-> ((vector T)) (vector T)) Don't mutate the arguments. Return a new vector. ---------------------- what do these instructions mean? ---------------------- Def: an object is mutated if it's value is changed (e.g., by vector-set!). Def: an object is new iff it didn't exist before the call (so it's not eq? to anything else) ------------------------ same plan as before in essence 1. make a new vector of the right length 2. mutate the new vector to hold the elements, by looping for each element 3. return the new vector ----------------------- (define vector-copy (lambda (v) ; ENSURES: result is a new vector ; that has the same value as v (let ((len (vector-length v))) (let ((vec (make-vector len))) (letrec ((init! ; TYPE: (-> (natural) void) (lambda (i) ; REQUIRES: i < len ; for all 0 <= j < i: ; (vector-ref v j) ; = (vector-ref vec j) ; MODIFIES: vec ; EFFECT: put the elements ; of v into vec, start at i (if (< i len) (begin (vector-set! vec i (vector-ref v i)) (init! (add1 i))))))) (init! 0)) vec)))) ---------------------- **** vec+ ---------------------- YOU WRITE (vec+ '#(3 4 5) '(2 3 4)) ==> #(5 8 9) vec+ : (-> ((vector number) (vector number)) (vector number)) Assume the arguments have the same length. Don't mutate the arguments. Return a new vector. ---------------------- have them do this in groups (define vec+ (lambda (v1 v2) ; REQUIRES: v1 and v2 have the same length ; ENSURES: result is a new vector of the same length ; as v1 such that its ith element is the sum ; of the ith elements of v1 and v2 (let ((len (vector-length v1))) (let ((vec (make-vector len))) (letrec ((add-in! ; TYPE: (-> (natural) void) (lambda (i) (if (< i len) (begin (vector-set! vec i (+ (vector-ref v1 i) (vector-ref v2 i))) (add-in! (add1 i))))))) (add-in! 0)) vec)))) This is a very standard paradigm.