SICPを読む(90) 問題3.1 - 3.4

簡単だったので、やっちまおう。

問題 3.1

アキュムレータを作れ。

(define ((make-accumulator n) x)
  (set! n (+ n x))
  n)

(define A (make-accumulator 5))
(A 10) ; 15
(A 10) ; 25

lambdaイラネー。

問題 3.2

これは面白い。関数の呼び出し回数を数える問題。

(define (make-monitored f)
  (let ((n 0))
       (lambda x
         (if (equal? x '(how-many-calls?))
             n
             (begin (set! n (+ n 1))
                    (apply f x))))))

(define sqrt (make-monitored sqrt))
(sqrt 4) ; 2
(sqrt 100) ; 10
(sqrt 'how-many-calls?) ; 2

関数のフックが出来るようになった。破壊的代入があるからこそ出来る。

traceとかこんな仕組みかな。再defineしなきゃいけない所が面倒そうだ。

問題 3.3

銀行口座にパスワードを設定する。

(define ((make-account balance pass) p m)
  (let ((withdraw (lambda (amount)
                    (if (>= balance amount)
                        (begin (set! balance (- balance amount))
                               balance)
                        (error "Insufficient funds")))))
       (if (eq? pass p)
           (case m
                 ('withdraw withdraw)
                 ('deposit (lambda (x)
                             (withdraw (- x)))))
           (error "Incorrect password."))))

(define tanaka (make-account 100 'himitu))

((tanaka 'himitu 'withdraw) 40) ; 60
((tanaka 'himitu 'deposit) 60) ; 120
((tanaka 'hogehoge 'withdraw) 'all) ; Incorrect password.

caseを使ってみた。

問題 3.4

不正なパスワードが連続3(7だけど)回以上入力されたら、逮捕。

(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")))))
              (if (eq? pass p)
                  (begin (set! incorrect 0)
                         (case m
                               ('withdraw withdraw)
                               ('deposit (lambda (x)
                                           (withdraw (- x))))))
                  (begin (set! incorrect (+ incorrect 1))
                         (if (<= incorrect 3)
                             (error "Incorrect password.")
                             (error "逮捕します。"))))))))

(define tanaka (make-account 100 'himitu))
((tanaka 'hogehoge 'withdraw) 'all) ; Incorrect password.
((tanaka 'hogehoge 'withdraw) 'all) ; Incorrect password.
((tanaka 'hogehoge 'withdraw) 'all) ; Incorrect password.
((tanaka 'hogehoge 'withdraw) 'all) ; 逮捕します。

キャー。

まとめ

関数のフックは使えそうだ。