<< Previous exercise (3.3) | Index | Next exercise (3.5) >>
A solution that uses a high-order, reusable make-passworded function, which should work with any function with an identical API to make-account, and also reuses the make-monitored solution from exercise 3.2:
;; original 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) ;; solution from exercise 3.2 (define (make-monitored f) (let ((count 0)) (define (how-many-calls?) count) (define (reset-count) (set! count 0)) (define (monitored x) (begin (set! count (+ 1 count)) (f x))) (define (dispatch x) (cond ((eq? x 'how-many-calls?) (how-many-calls?)) ((eq? x 'reset-count) (reset-count)) (else (monitored x)))) dispatch)) (define (make-passworded f) (define (failed-entry x) "incorrect password") (define (call-the-cops x) "weeeoooo weeeeoooo weeeeeoooo") (define (constructor initial password) (define (correct-pass? attempt) (eq? password attempt)) (define try-pass? (make-monitored correct-pass?)) (define (reset-login-attempts) (try-pass? 'reset-count)) (define (failed-attempts) (try-pass? 'how-many-calls?)) (let ((passworded-f (f initial))) (define (dispatch attempt x) (if (try-pass? attempt) (begin (reset-login-attempts) (passworded-f x)) (if (< 6 (failed-attempts)) call-the-cops failed-entry))) dispatch)) constructor) (define passworded-make-account (make-passworded make-account))
(define (make-account balance password) (define (call-the-cops) "Call the cops") (let ((count 0) (limit 7)) (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) (if (> count limit) (call-the-cops) (begin (set! count (+ count 1)) "Wrong password"))) (begin (set! count 0) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown call -- MAKE-ACCOUNT" m)))))) dispatch))
Shawn
This solution won't work because when call-the-cops returns a string literal, there will be a object not applicable error since a parameter amount is provided to it. "Wrong password" has the same problem.
We should wrap these in a lambda function that accepts an input.
Kenneth
Besides what Shawn mentioned, this solution also has a logic error. It will actually invoke the "call-the-copes" function when there's more than 8 wrong inputs in a row, instead of 7.
Daniel-Amariei
eliyak
I put all the password-checking functionality into a separate internal function called correct-password?. Unfortunately my implementation of scheme does not know how to (call-the-cops).
(define (make-account balance password) (define bad-password-count 0) (define (correct-password? p) (if (eq? p password) (set! bad-password-count 0) (begin (if (= bad-password-count 7) (call-the-cops) (set! bad-password-count (+ bad-password-count 1))) (display "Incorrect password") #f))) (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) (if (correct-password? p) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m))) (lambda (x) (display "")))) dispatch)
(define (make-account balance password) (let ((bad-passwords 0)) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) (print "Insufficient funds"))) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch p m) (if (good-password? p) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m))) (lambda (x) (print "Incorrect password") (newline)))) (define (good-password? p) (cond ((eq? p password) (set! bad-passwords 0) true) ((< bad-passwords 7) (set! bad-passwords (+ bad-passwords 1)) false) (else (call-the-cops)))) (define (call-the-cops) (print "Cops called!") false) dispatch))
Caleb Gossler
I wrapped dispatch in a function that handles the password checking. It keeps dispatch much cleaner.
Han Chan
My implementation is much simpler. I just wrap the dispatch function with an "auth-layer" function, which will do the authentication. Then I wrap the "auth-layer" function with a "secure-layer", which will check the error counter and decide to call the cops or not.
(define (secure-make-account balance password) (define error-count 0) (define (call-the-police) "Too much errors, we have called LAPD") (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))) ) (define (auth-layer pass m) (if (eq? pass password) (dispatch m) (lambda (x) (begin (set! error-count (+ 1 error-count)) "Incorrect password")))) (define (secure-layer pass m) (if (= 2 error-count) (lambda (x) (call-the-police)) (auth-layer pass m))) secure-layer)
(define (make-account balance password) (let ((bad-passwords 0)) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) (print "Insufficient funds"))) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch p m) (if (good-password? p) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request -- MAKE-ACCOUNT" m))) (lambda (x) (print "Incorrect password") (newline)))) (define (good-password? p) (cond ((eq? p password) (set! bad-passwords 0) true) ((< bad-passwords 7) (set! bad-passwords (+ bad-passwords 1)) false) (else (call-the-cops)))) (define (call-the-cops) (print "Cops called!") false) dispatch))
Sphinxsky
-------
zxymike93
My solution is not so original but it's plain and simple.
(define (make-account balance password) (define psswd password) (define lock 0) (define (count-incorrect-password amount) (begin (set! lock (+ lock 1)) (display "Incorrect password") (newline))) (define (call-the-cops amount) (error "Bee-boo Bee-boo Bee-boo")) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch pw m) (cond [(eq? pw psswd) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request: MAKE-ACCOUNT" m)))] [else (cond [(>= lock 6) call-the-cops] [else count-incorrect-password])])) dispatch)