<< Previous exercise (3.2) | Index | Next exercise (3.4) >>
; An easier solution. (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 p m) (cond ((not (eq? p password)) (lambda (x) "Incorrect password")) ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m)))) dispatch)
; An alternative solution with explicitly defined password validator and method getter (define (make-account balance initial-password) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (get-method m) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown method" m)))) (define (valid-password? p) (eq? p initial-password)) (define (dispatch password method) (if (valid-password? password) (get-method method) (lambda _ "Incorrect password"))) dispatch)
Here's a solution that uses a high-order make-passworded function, which could be applied to any function with an identical API to the original make-account:
;; original make-account function from the text (define (make-account balance) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch m) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m)))) dispatch) (define (make-passworded f) (define (constructor initial password) (define (correct-pass? attempt) (eq? password attempt)) (let ((passworded-f (f initial))) (define (dispatch attempt x) (if (correct-pass? attempt) (passworded-f x) (lambda (x) "incorrect password"))) dispatch)) constructor) (define passworded-make-account (make-passworded make-account))
with the builtin apply procedure. I ran grep for the apply procedure on previous code and decided that it was legal.
(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 m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
;; really is just another wrapper over dispatch which itself is a wrapper
(define (make-password-protected password proc)
;; here is was inspired by another person's answer
(lambda (first . args)
(if (eqv? first password)
(apply proc args)
"Incorrect Password")))
;; returns a procedure object as implementation
(make-password-protected password dispatch))
(define (make-account balance password) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Not enough money")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch pass m) (if (not (eq? pass password)) (lambda (amount) "Wrong password") (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown call -- MAKE-ACCOUNT" m))))) dispatch)