SICPを読む(96) 問題 3.7 - 共同口座

ムズイな。

問題 3.7

パスワードつき銀行口座を改造して、共同口座を作れ。という問題。

口座は同じで、パスワードが違うように設定する。


問題は、パスワードを変更してしまうと、引きずられて、元のパスワードまで変わってしまう点。

ユーザーオブジェクトを中に仕込むのは大変そうなので、make-accountの変更点は一箇所。パスワードが合っているのか確認するだけ。

make-jointで、アカウント作成時に作ったパスワードを保持できるので、実際にはアカウント作成時のパスワードでログインすることにする。

(define (make-account balance pass)
  (let ((incorrect 0))
       (lambda (p m)
         (let ((withdraw (lambda (amount)
                           (if (>= balance amount)
                               (begin (set! balance (- balance amount))
                                      balance)
                               (error "Insufficient funds")))))
              (cond ((eq? p 'check-pass) (eq? pass m))
                    ((eq? p pass)
                     (case m
                           ('withdraw withdraw)
                           ('deposit (lambda (x)
                                       (withdraw (- x))))))
                    (else
                      (begin (set! incorrect (+ incorrect 1))
                             (if (<= incorrect 3)
                                 (error "Incorrect password.")
                                 (error "逮捕します。")))))))))

(define (make-joint account pass joint-pass)
  (if (account 'check-pass pass)
      (lambda (p m)
        (if (eq? p joint-pass)
            (account pass m)
            (error "Incorrect password.")))
      (error "Incorrect password.")))

テスト。

(define peter-acc (make-account 100 'open-sesame))

((peter-acc 'open-sesame 'withdraw) 10) ; 90

(define paul-acc (make-joint peter-acc 'hoge-hoge 'rosebund)) ; Incorrect password.
(define paul-acc (make-joint peter-acc 'open-sesame 'rosebund))

((paul-acc 'open-sesame 'withdraw) 10) ; Incorrect password.
((paul-acc 'rosebund 'withdraw) 10) ; 80

((peter-acc 'open-sesame 'withdraw) 10) ; 70

ここまではうまくいってるんだけど、


「孫アカウントは作れない」

ちと動作もおかしい。


解決。

(define (make-joint account pass joint-pass)
  (if (account 'check-pass pass)
      (lambda (p m)
        (cond ((eq? p 'check-pass) (eq? joint-pass m))
              ((eq? p joint-pass) (account pass m))
              (else
                (error "Incorrect password."))))
      (error "Incorrect password.")))

親と同じようにしてやればいいんだ。

  • 孫アカウント作成時には親の認証は済んでるはずなので、親までは見に行かないようにした。
  • ログインした場合は再帰的に問い合わせていく。


口座オブジェクトと、アカウントオブジェクトを別にしたほうがわかりやすいけど、再帰的になってるアカウントというのも面白いね。