問題3-1〜4

第三章に入りました。最初は代入に関する練習問題です。
問題3-1のアキュムレータ手続きです。

(define (make-accumulator init)
  (let ((sum init))
    (lambda (x)
      (begin (set! sum (+ sum x))
             sum))))

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

問題3-2は、予め登録しておいた手続きが利用された回数を呼び出せるmake-monitoredを定義します。

(define (make-monitored proc)
  (let ((count 0))
    (lambda (x)
      (if (eq? x 'how-many-calls?)
          count
          (begin (set! count (+ 1 count))
                 (proc x))))))

(define s (make-monitored sqrt))
(s 100)  ;=>10
(s 'how-many-calls?)  ;=>1
(s 'how-many-calls?)  ;=>1
(s 64)  ;=>8
(s 'how-many-calls?)  ;=>2

問題3-3では、パスワードで保護されたmake-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 (show-error-message amount)
    "Incorrect password")
  (define (dispatch pw m)
    (if (eq? pw password)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
              (else (error "Unkown request" m)))
        show-error-message))
  dispatch)

(define acc (make-account 100 'takuya))

((acc 'takuya 'withdraw) 50)  ;=>50
((acc 'takuya 'withdraw) 60)  ;=>"Insufficient funds"
((acc 'takuya 'deposit) 40)  ;=>90
((acc 'takuya 'withdraw) 60)  ;=>30
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"

さらに問題3-4は、7回以上パスワードを間違えたら警察に通報するような機能を追加します。

(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 show-error-message
    (let ((err-count 1))
      (lambda (acount)
        (if (< 7 err-count)
            call-the-police
            (begin (set! err-count (+ 1 err-count))
                   "Incorrect password")))))
  (define call-the-police "we are calling the police")
  (define (dispatch pw m)
    (if (eq? pw password)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
              (else (error "Unkown request" m)))
        show-error-message))
  dispatch)

(define acc (make-account 100 'takuya))

((acc 'takuya 'withdraw) 60)  ;=>40
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"Incorrect password"
((acc 'itoh 'withdraw) 20)  ;=>"we are calling the police"
((acc 'itoh 'withdraw) 20)  ;=>"we are calling the police"