my attempt to do the exercises in sicp.

Monday, August 17, 2009

sicp exercise 3.7

;; Exercise 3.7.  Consider the bank account objects created by make-account, with the password modification described in exercise 3.3. Suppose that our banking system requires the ability to make joint accounts. Define a procedure make-joint that accomplishes this. Make-joint should take three arguments. The first is a password-protected account. The second argument must match the password with which the account was defined in order for the make-joint operation to proceed. The third argument is a new password. Make-joint is to create an additional access to the original account using the new password. For example, if peter-acc is a bank account with password open-sesame, then

;;(define paul-acc
;;  (make-joint peter-acc 'open-sesame 'rosebud))

;;  will allow one to make transactions on peter-acc using the name paul-acc and the password rosebud. You may wish to modify your solution to exercise 3.3 to accommodate this new feature.

;; The solution is to seperate the account balance management and password management.

(define (make-balance balance)
  "This procedure is exclusively for balance management"
  (define (withdraw amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
        "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
  (define (dispatch m)
      (cond ((eq? m 'withdraw) withdraw)
            ((eq? m 'deposit) deposit)
            (else (error "Unknown request -- MAKE-ACCOUNT"

(define (make-account balance password)
  "This procedure is exclusively for passwd management
   and gaining access to balance management if the password is correct"
  (define (get-balance)
    (define balance-ref balance)
  (define (wrong-passwd arg)
    "Incorrect password")
  (define (check-pass pass)
    (eq? pass password))
  (define (set-passwd pass)
    (set! password pass))
  (define (dispatch passwd cmd)
    (if (eq? cmd 'check-passwd)
          (check-pass passwd)
        (if (eq? passwd password)
              (cond ((eq? cmd 'get-balance) (get-balance))
                    ((eq? cmd 'change-passwd) set-passwd)
                    ((eq? cmd 'check-passwd) #t)
                    (else (balance cmd)))

(define (make-joint-account accnt accnt-passwd passwd)
  (if (accnt accnt-passwd 'check-passwd)
      (make-account (accnt accnt-passwd 'get-balance) passwd)
      "Wrong passwd..."))

(define peter-acc (make-account (make-balance 1000) 'open-sesame))

(display ((peter-acc 'open-sesame 'withdraw) 40)) (newline)
(display ((peter-acc 'open-sesame 'withdraw) 40)) (newline)
(display ((peter-acc 'open-sesame 'withdraw) 40)) (newline)
(display ((peter-acc 'some-other-password 'deposit) 50)) (newline)

(define paul-acc (make-joint-account peter-acc 'open-sesame 'rosebud))

(display ((paul-acc 'rosebud 'withdraw) 40)) (newline)
(display ((paul-acc 'rosebud 'withdraw) 40)) (newline)
(display ((paul-acc 'rosebud 'withdraw) 40)) (newline)
(display ((paul-acc 'some-other-password 'deposit) 50)) (newline)

;; paul can change his passwd, independent of peter
((paul-acc 'rosebud 'change-passwd) 'lotus-leaf)
(display ((paul-acc 'lotus-leaf 'withdraw) 40)) (newline)

;; peter is not affected by change in paul's password
(display ((peter-acc 'open-sesame 'withdraw) 40)) (newline)


ferd said...

This seemed to work fine, while being shorter:

(define (make-joint account oldpass newpass)
  (lambda (try method)
    (if (eq? try newpass)
        (account oldpass method)
        (error "Incorrect password"))))

Any apparent problem with that approach?

weima said...

As i have already mentioned in the solution that "The solution is to seperate the account balance management and password management."

Thats why my solution is so elaborate.

Thanks for your comment :)

léon said...

ferd's solution is more elegant, since it doesn't require changes to pre-existing code.