Tutorial notes #9 for 4/23 or 4/24/2007

TA:       Stephen McCamant
Email:    smcc@mit.edu
Location: 36-113A or 36-115
Quiz 2

As threatened, this quiz was harder than
quiz 1; the median grade among my
students was 75. Two common
misunderstandings it highlighted:

- set! can only change variable bindings
  in the environment. Its first argument
  must be the name of a variable. Anything 
  else is a syntax error.

- If you want a variable to remember a
  value between calls to a procedure, it
  has to be defined outside that

Project 3

Quality is more important than quantity
in testing.

When feasible, test helpers separately.


Why copying grids usually doesn't work

Don't use mutation when return values or
loop parameters would work.

Convenient procedures to know about:
 - (foldr append '() ...)
 - list-ref
 - member

Design choices in backtracking

User experience and the game

Project 4

I'm hoping to be able to reply to all of
your extension proposals by Tuesday
night. Previews:

- If everything you've thought about
  your idea can fit in one paragraph,
  you should plan some more before
  starting to implement.

- Conversely, if your idea involves
  creating a completely different game,
  beware of biting off more than you can
  chew. Don't neglect the other parts of
  the project, and if possible, do a
  subset of your idea in the MIT world

Review of OO syntax

(define (create-<class> <params>)
  (create-instance <class> <params>))

(define (<class> self <params>)
  (let ((<superclass>-part
         (<superclass> self <params>))
        (<instance-var> <initial-val>)
        (<instance-var> <initial-val>))
        (lambda (<args>) <body>)
        (lambda (<args>) <body>))

(ask <instance> 'MESSAGE <args>)


For practice in modelling real-life
things with OO, let's make classes for
some home appliances. All our appliances
have on-off switches and a fuse that
blows and prevents them from operating
if they draw too much current.

(define (create-appliance manufacturer)
  (create-instance appliance manufacturer))

(define (appliance self manufacturer)
  (let ((switch-on? #f)
        (fuse-blown? #f))
       'MANUFACTURER (lambda () manufacturer)
       'SWITCH       (lambda () (set! switch-on? (not switch-on?)))
       'SWITCH-ON?   (lambda () switch-on?)
       'BLOW-FUSE    (lambda () (set! fuse-blown? #t))
       'FUSE-BLOWN?  (lambda () fuse-blown?)
       'ON?          (lambda () (and switch-on?
                                     (not (ask self 'fuse-blown?))))))))

(define (create-hair-dryer manufacturer)
  (create-instance hair-dryer manufacturer))

(define (hair-dryer self manufacturer)
  (let ((appliance-part (appliance self manufacturer))
        (speed 0))
        'SPEED     (lambda () speed)
        'SET-SPEED (lambda (s) (set! speed s))
        'DRY       (lambda ()
                     (if (ask appliance-part 'on?)
                         (printf "Hot air comes out~n")
                         (printf "Nothing happens~n"))))

(define (create-toaster manufacturer)
  (create-instance toaster manufacturer))

(define (toaster self manufacturer)
  (let ((appliance-part (appliance self manufacturer))
        (darkness 0))
        'DARKNESS     (lambda () darkness)
        'SET-DARKNESS (lambda (d) (set! darkness d))
        'TOAST        (lambda ()
                        (if (ask appliance-part 'on?)
                            (printf "Your bread turns golden brown~n")
                            (printf "Nothing happens~n"))))

;; A toaster-hair-dryer as a subclass of both toaster and hair-dryer
;; with the same manufacturer. (This is convenient, because they're
;; both things people do in the morning.) If you try to toast when the
;; hair-dryer is running too fast at the same time, the fuse will
;; blow.

;; In effect, the THD has two switches but only one manufacturer and
;; fuse. Notice the difference between using instance variables versus
;; methods, and between calling a method on self versus calling it on
;; a subobject.

(define (create-toaster-hair-dryer manufacturer)
  (create-instance toaster-hair-dryer manufacturer))

(define (toaster-hair-dryer self manufacturer)
   (let ((toaster-part (toaster self manufacturer))
         (hair-dryer-part (hair-dryer self manufacturer)))
         'SWITCH-TOASTER    (lambda () (ask toaster-part 'switch))
         'SWITCH-HAIR-DRYER (lambda () (ask hair-dryer-part 'switch))
         'TOAST             (lambda ()
                              (if (and (> (ask self 'speed) 5)
                                       (> (ask self 'darkness) 5))
                                  (ask self 'blow-fuse))
                              (ask toaster-part 'toast)))
       toaster-part hair-dryer-part)))
(define t (create-toaster "Proctor-Silex"))
(define hd (create-hair-dryer "Sunbeam"))
(define thd (create-toaster-hair-dryer "Acme"))

(ask thd 'set-darkness 5)
(ask thd 'switch-toaster)
(ask thd 'set-speed 7)
(ask thd 'dry)
; -> Nothing happens
(ask thd 'switch-hair-dryer)
(ask thd 'dry)
; -> Hot air comes out
(ask thd 'toast)
; -> Your bread turns golden brown
(ask thd 'set-darkness 10)
(ask thd 'toast)
; -> Nothing happens
(ask thd 'set-darkness 3
(ask thd 'toast)
; -> Nothing happens
(ask thd 'dry)
; -> Nothing happens