The code for this section is in Scheme.
So, we commence with chapter 3 of the book – Modularity, Objects, and State. The video lecture relevant to this section – 5a was, in my opinion, excellent. The way Sussman modeled assignment as a point in time was very lucid. He also explained the environment model of evaluation quickly and efficiently. Even when all these concepts are long familiar to you, learning a new way to look at things helps a lot. The approach taken in SICP to fully understand how the language works under the hood is very educational.
This is very similar to the bank account example, except for a small twist – we want to be able to provide an initial value for the accumulation. Since this value is accepted by
make-accumulator, the correct way to place it is in this function’s argument list.
(define (make-accumulator initial) (let ((accumulator initial)) (lambda (addon) (set! accumulator (+ accumulator addon)) accumulator)))
Note that we don’t need
begin here, because the body of a
lambda definition is implicitly wrapped in one.
We’ll use the
dispatch method employed in the last
(define (make-monitored proc) (let ((call-count 0)) (define (dispatch m) (cond ((eq? m 'how-many-calls?) call-count) ((eq? m 'reset-count) (set! call-count 0)) (else (set! call-count (+ 1 call-count)) (proc m)))) dispatch))
The modification is only to
dispatch – the rest of the code should know nothing about passwords:
(define (make-account balance password) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch pass m) (if (eq? pass password) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m))) (error "Bad password -- " pass))) dispatch)
A part of this function may not be obvious, and although the book explains it I want to dwell on this point a little.
The local bindings created by
define’s arguments aren’t different from using the
let form1. So,
balance are captured inside the definition of
make-account the same way
call-count of exercise 3.2 is. When the interpreter executes the definition of
make-account, it also executes the definition of
dispatch, with an environment that contains
password having the values passed to
(define (make-account balance password) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (let ((bad-pass-count 0)) (define (dispatch pass m) (if (eq? pass password) (begin (set! bad-pass-count 0) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m)))) (begin (set! bad-pass-count (+ bad-pass-count 1)) (when (> bad-pass-count 7) (call-the-cops)) (printf "~a~%" "Bad password") (lambda (x) x)))) dispatch))
The weird combination of
(lambda (x) x) in the end of
dispatch allows me to signal an error and yet not trigger an exit of the interpreter on first error. It would exit otherwise, since
dispatch must return a function in order to work properly.
1 In fact,
let can be implemented using