sicp-ex-3.7



<< Previous exercise (3.6) | Index | Next exercise (3.8) >>


woofy

arbitrary number of layers of joint account:

 (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 pwd m) 
         (if (eq? m 'auth) 
             (lambda () (eq? pwd password)) 
             (if (eq? pwd password) 
                 (cond ((eq? m 'withdraw) withdraw) 
                       ((eq? m 'deposit) deposit) 
                       (else (error "Unknown request -- MAKE-ACCOUNT" m))) 
                 (error "Incorrect password")))) 
      
     dispatch) 
  
 (define (make-joint account orig-pwd joint-pwd) 
  
     (define (withdraw amount) ((account orig-pwd 'withdraw) amount)) 
     (define (deposit amount) ((account orig-pwd 'deposit) amount)) 
  
     (define (dispatch pwd m) 
         (if (eq? m 'auth) 
             (lambda () (eq? pwd joint-pwd)) 
             (if (eq? pwd joint-pwd) 
                 (cond ((eq? m 'withdraw) withdraw) 
                       ((eq? m 'deposit) deposit) 
                       (else (error "Unknown request -- MAKE-ACCOUNT" m))) 
                 (error "Incorrect password")))) 
  
     (if ((account orig-pwd 'auth)) 
         dispatch 
         (error "Incorrect original password of target account"))) 
  
 ; test 
 (define peter-acc (make-account 100 'open-sesame)) 
 (define paul-acc (make-joint peter-acc 'open-sesame 'rosebud)) ;joint account 
 (define woofy-acc (make-joint paul-acc 'rosebud 'longleg)) ; joint joint account 
  
 ((peter-acc 'open-sesame 'withdraw) 50) ;50 
 ((paul-acc 'rosebud 'withdraw) 10) ;40 
 ((peter-acc 'open-sesame 'deposit) 0) ;40 
 ((woofy-acc 'longleg 'withdraw) 33) ;7 
 ((peter-acc 'open-sesame 'deposit) 0) ;7 
  
 ((woofy-acc 'rosebud 'withdraw) 100) ;wrong pwd 
 ((woofy-acc 'open-sesame 'withdraw) 100) ;wrong pwd 
  

See sicp-ex-3.3-3.4 for the definition of make-account.

 (define (make-joint account old-pass new-pass) 
   (and (number? ((account old-pass 'withdraw) 0)) 
        (lambda (pass msg) 
          (if (eq? pass new-pass) 
              (account old-pass msg) 
              (account 'bad-pass 'foo))))) ;increment bad-passwords 

Alternative implementation:

 (define (call-the-cops) 
   (display "calling the cops\n")) 
  
 (define (password-protect password subject) 
   (let ((num-attempts 0)) 
     (lambda (provided-password msg) 
       (if (eq? provided-password password) 
           (begin (set! num-attempts 0) 
                  (subject msg)) 
           (begin (set! num-attempts (+ 1 num-attempts)) 
                  (when (>= num-attempts 7) 
                    (call-the-cops)) 
                  (lambda (arg . rest) "invalid password")))))) 
  
 (define (make-account password 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 msg) 
     (cond ((eq? msg 'withdraw) withdraw) 
           ((eq? msg 'deposit) deposit) 
           (else (error "Unknown request -- MAKE-ACCOUNT" 
                        msg)))) 
   (password-protect password dispatch)) 
  
 (define (make-joint-account account original-password password) 
   (define (dispatch msg) 
     (account original-password msg)) 
   (password-protect password dispatch)) 
 (define (make-account balance secret-words) 
  
   (define (withdraw amount) 
     (if (>= balance amount) 
         (begin (set! balance (- balance amount)) balance) 
         "Insufficient funds")) 
  
   (define (deposit amount) 
     (set! balance (+ balance amount)) balance) 
  
   (define (make-join secret-words) (set-sw secret-words)) 
      
   (define (dispatch m) 
     (cond ((eq? m 'withdraw) withdraw) 
           ((eq? m 'deposit) deposit) 
           ((eq? m 'make-join) make-join) 
           (else (error "Unknown request -- MAKE-ACCOUNT" 
                        m)))) 
  
   (define (set-sw secret-words) 
     (lambda(pw m) 
       (cond ((eq? pw secret-words)(dispatch m)) 
             (else (error "Permission Denied -- MAKE-ACCOUNT"))))) 
  
   (set-sw secret-words)) 
  

CrazyAlvaro

Implements above are correct, just provide another way to do it

  
 (define (make-account balance pass-origin) 
   (define (withdraw amount) 
     (if (>= balance amount) 
         (begin (set! balance (- balance amount)) 
                balance) 
         "Insufficient funds")) 
  
   (define (deposit amount) 
     (set! balance (+ balance amount)) 
     balance) 
  
   (define (make-joint another-pass) 
     (dispatch another-pass)) 
  
   (define (dispatch password) 
     (lambda (pass m) 
       (if (eq? pass password) 
         (cond ((eq? m 'withdraw) withdraw) 
               ((eq? m 'deposit) deposit) 
               ((eq? m 'make-joint) make-joint) 
               (else (error "Unknown request -- MAKE-ACCOUNT" 
                            m))) 
         (lambda (x) 
           "Incorrect password")))) 
            
   (dispatch pass-origin)) 

drugkeeper

This checks if you keyed in the 1st-password correctly when making the joint acc, by trying out the 1st pass to deposit 0 dollars into the account.

When depositing or withdrawing, it takes your password and checks it against new-pass, if its correct then it converts the password to 1st-pass and passes the message to the original account.

You can also make many joint accounts all sharing together as all the passwords are converted to the 1st-pass.

 (define (make-joint acc 1st-pass new-pass) 
   (define (dispatch p m) 
     (cond ((eq? p new-pass) (acc 1st-pass m)) 
           (else (error "Wrong password")))) 
   (begin ((acc 1st-pass 'deposit) 0) ;this does a clever check on whether 1st-pass is correct. 
          dispatch)) 

master

This solution uses a password list to keep track of all the authorized passwords. Anybody can use any of the passwords contained in the list, however. I don't really see any way around that because there aren't any usernames, and there's no easy way to tell at the moment under what name the account is invoked. No need to make a dummy transaction as the only way to create a joint account is via an internal procedure in make-account.

 (define (make-account balance password) 
   (let ((tries 0) 
         (password-list (list password))) 
     (define (withdraw amount) 
       (if (>= balance amount) 
           (begin (set! balance (- balance amount)) 
                  balance) 
           "Insufficient funds")) 
     (define (deposit amount) 
       (set! balance (+ balance amount)) 
       balance) 
     (define (contains? p plist) 
       (if (not (eq? (memq p plist) #f)) 
           #t 
           (lambda (x) "Incorrect password"))) 
     (define (add-user pass) 
       (set! password-list (cons pass password-list))) 
     (define (dispatch p m) 
             (if (contains? p password-list) 
              (cond ((eq? m 'withdraw) withdraw) 
                    ((eq? m 'deposit) deposit) 
                    ((eq? m 'add-user) add-user) 
                    (else (error "Unknown request: MAKE-ACCOUNT" 
                                 m))) 
             (lambda (x) "Incorrect password"))) 
     dispatch)) 
  
 (define (make-joint account password new-password) 
   ((account password 'add-user) new-password) 
   account)