(define (find-if pred? list) (cond ((null? list) #f) ((pred? (car list)) #t) (#f (find-if pred? (cdr list))))) (define (remove-if test? alist) (cond ((null? alist) ()) ((test? (car alist)) (remove-if test? (cdr alist))) (#t (cons (car alist) (remove-if test? (cdr alist)))))) (define (remove-if-not test? alist) (remove-if (lambda (x) (not (test? x))) alist)) ;; nil is a more readable way to say () (define nil ())
(define *bands* '((seattle-bands (nirvana pearl-jam foo-fighters green-river)) (la-bands (black-flag minutemen red-hot-chili-peppers)) (nz-bands (bats clean tall-dwarfs)))) ;; lookup the value associated with key. return nil if unassociated. (define (lookup tbl key) (cond ((null? tbl) nil) ((equal? key (caar tbl)) (cadar tbl)) (#t (lookup (cdr tbl) key)))) ;; return the new table which associates key and value (define (associate tbl key val) (cond ((null? tbl) (list (list key val))) ((equal? key (caar tbl))) (cons (list key val) (cdr tbl))) (T (cons (car tbl) (associate (cdr tbl) key val))))) (lookup *bands* 'la-bands) => (black-flag minutemen red-hot-chili-peppers) (lookup *bands* 'ny-bands) => nilWhat is the running time of these operations? Below we'll see that many dialects of Scheme provide more efficient tables.
(root left-stubtree right-subtree) ; general form ;; here are the functions that define our Tree ADT. ;; these function make the code more readable, and "hide" the implementation ;; of the tree from the user (although this is not enforced by Scheme) (define (empty-tree) nil) ;; constructor: give me an empty tree (define (make-tree root left right) ;; constructor: make a tree (list root left right)) (define (empty? tree) (null? tree)) ;; predicate: is the tree empty (define (root tree) (first tree)) ;; accessor: give me the root (define (left tree) (second tree)) ;; accessor: give me the left subtree (define (right tree) (third tree)) ;; accessor: give me the right subtree ;; here are some operations on a BST (define (bst-insert item tree) (cond ((empty? tree) (make-tree item (empty-tree) (empty-tree))) ;; item is less than the root, so insert into left subtree ((< item (root tree)) (make-tree (root tree) (bst-insert item (left tree)) (right tree))) ;; item is >= to the root, so insert into right subtree (#T (make-tree (root tree) (left tree) (bst-insert item (right tree)))))) (define (bst-lookup item tree) (cond ((empty? tree) nil) ;; we've found the item ! ((equal? (root tree) item) item) ;; item is less than the root, so look left: ((< item (root tree)) (bst-lookup item (left tree))) ;; item is >= the root, so look right: (#t (bst-lookup item (right tree))))) (bst-insert 6 (bst-insert 4 (bst-insert 3 (bst-insert 5 (empty-tree))))) => (5 (3 nil (4 nil nil)) (6 nil nil))
(define (set-member e set) (find-if (lambda (x) (equal? e x)) set)) (define (set-intersection set1 set2) (remove-if-not (lambda (x) (set-member x set2)) set1)) (define (set-union set1 set2) (append set1 (remove-if (lambda (x) (set-member x set1)) set2)))Again, these aren't terribly efficient implementations, but they work!
make-vector
and access arrays with
vector-ref
. We can set fields in the vector with
vector-set!
. Arrays are accessed starting at zero.
(define foo (make-vector 3)) ;; create an array of length 5 (vector-set! foo 2 'bar) ;; set an element foo => #(#<unspecified> #<unspecified> bar) (vector-ref foo 5) => Error! Index out of bounds. (vector-ref foo 2) => bar
(define (make-point x y) ; constructor (list x y)) (define (x point) ; accessor (first point)) (define (y point) ; accessor (second point)) (define (set-x! point x) ; setters (set-car! point x)) (define (set-y! point y) (set-car! (cdr point) y)) (define (midpoint p1 p2) (make-point(/ (+ (x p1) (x p2)) 2) (/ (+ (y p1) (y p2)) 2)))This allows us to hide the representation of the point, so we could change the internal representation to polar coordinates, while presenting the user with the same interface. Of course, some versions of Scheme provide us with built-in record support. For example, in DrScheme, we can use
define-struct
. When it evaluates, it creates a
constructor, type predicate, getters, and setters...
(define-struct point (x y)) (define p1 (make-point 10 20)) ; constructor (define p2 (make-point 20 30)) (define p3 (make-point 10 20)) (equal? p1 p2) => #f (equal? p1 p3) => #t (point? p3) => #t ; type predicate (point? 'hello) => #f (point-x p1) => 10 ; getter (point-y p2) => 30 (set-point-x! p1 100) ; setter (point-x p1) => 100
Object-oriented programming provides a more sophisticated and flexible way of providing information hiding. Mechanisms for doing OO-style programming abound in Scheme. We'll look at a fully OO language later in the course, called Smalltalk.
make-hash-table
(define tbl (make-hash-table)) (hash-table-put! 'foo 'bar) (hash-table-get 'foo) => bar (hash-table-get 'boo) => ; error!!