SICP読書女子会 3.1.1 (#39)


## 3.1.1

```lisp
(define balance 100)
(define (withdraw amount)
  (if (>= balance amount)
  (begin (set! balance (- balance amount)) balance)
"Insufficient funds"))

;(begin ⟨exp1⟩ ⟨exp2⟩ . . . ⟨expk⟩)
;⟨exp1 ⟩ から ⟨expk ⟩ までの式が順番に評価され、最後の式 ⟨expk ⟩ の値が begin
;形式全体の値として返されます。
(print "withdraw 100 ~ ")
(print (withdraw 10))
(print (withdraw 10))


;　誰もbalanceにアクセスできない
(define new-withdraw
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
        (begin 
          (set! balance (- balance amount))
          balance)
      "Insufficient funds"))))

(print "new-withdraw")
(print (new-withdraw 20))
(print (new-withdraw 20))

; 引き出し器をつくる
(define (make-withdraw balance)
  (lambda (amount)
    (if (>= balance amount)
    (begin 
      (set! balance (- balance amount))
      balance)
    "Insufficient funds")))

(print "make-withdraw")
(define withdraw-func (make-withdraw 100))
(print (withdraw-func 30))
(print (withdraw-func 30))


; 引き出しも預入もできるようにする
(define (make-account 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 m)
    (cond 
      ((eq? m 'withdraw) withdraw)
      ((eq? m 'deposit) deposit)
    (else (error "Unknown request: MAKE-ACCOUNT" m))))
dispatch)

(print "==make-account")
(define account (make-account 0))
(print ((account 'withdraw) 10))
(print ((account 'deposit) 9))
(print ((account 'withdraw) 8))

```

### Ex 3.1

```lisp
;練習問題 3.1: 
;アキュムレータ (accumulator) は、ひとつの数値引数を伴って繰り返し呼ばれる⼿続きで、
;引数を合計に集積していくというものである。
;呼び出されるたびに現在までに集積された合計を返す。
;それぞれ独⽴した合計を持つアキュムレータを返す
;⼿続き make-accumulator を書け。make-accumulator への⼊⼒は、
;合計の初期値を指定する。例えば、以下のようになる。
;(define A (make-accumulator 5))
;(A 10)
;15
;(A 10)
;25

(define (make-accumulator init)
  (let ((current init))
    (lambda (value)
      (begin 
        (set! current (+ current value))
        current
      ))
  )
)

(define (ex-3.1)
  (define A (make-accumulator 5))
  (print "== Ex 3.1")
  (print (A 10))
  (print (A 10))
;== Ex 3.1
;15
;25
)

(ex-3.1)

; こちらでも○
(define (make-accumulator2 value)
    (lambda (v)
      (begin 
        (set! value (+ v value))
        value
      )
    )
)

```

### Ex 3.2

```lisp
;練習問題 3.2: ソフトウェアをテストするアプリケーションでは、
;計算の過程で、ある⼿続きが何回呼ばれたかを数えられると便利
;だ。⼀引数の⼿続き f を⼊⼒として取る⼿続き make-monitored
;を書け。make-monitored が返すのは第三の⼿続き (仮に mf とす
;る) で、この⼿続きは内部カウンタを保持することによって呼び
;出された回数を覚えておく。mf に対する⼊⼒が how-many-calls?
;という特別な記号である場合、mf はカウンタの値を返す。⼊⼒が
;reset-count という特別な記号である場合、mf はカウンタをゼロ
;にリセットする。それ以外の⼊⼒に対しては、mf はその⼊⼒によ
;って f を呼び出した結果を返し、カウンタを 1 増やす。例えば、監
;視をつけたバージョンの sqrt ⼿続きを作ることもできる。
;(define s (make-monitored sqrt))
;(s 100)
;10
;(s 'how-many-calls?)
;1


(define (make-monitored f)
  (define counter 0)
  (define (call args)
    (begin 
      (set! counter (+ counter 1))
      (apply f args)
    ))
  (define (how-many-calls?) counter)
  (define (reset-count) (set! counter 0))
  (define (dispatch . args)
    (cond 
      ((eq? (car args) 'how-many-calls?) (how-many-calls?))
      ((eq? (car args) 'reset-count) (reset-count))
    (else 
      (call args)
  )))
dispatch)


(define (ex-3.2)
  (define m (make-monitored sqrt))
  (define m2 (make-monitored +))
  (print "== Ex 3.2")
  (print "- sqrt" )
  (print "sqrt 100 = " (m 100))
  (print "how-many-calls = " (m 'how-many-calls? 100))
  (print "sqrt 25 = " (m 25))
  (print "how-many-calls = " (m 'how-many-calls? 100))
  (print "reset-count" (m 'reset-count))
  (print "sqrt 25 = " (m 25))
  (print "how-many-calls = " (m 'how-many-calls? 100))

  (print "- add" )     
  (print "+ 100 10 = " (m2 100 10))
;== Ex 3.2
;- sqrt
;sqrt 100 = 10
;how-many-calls = 1
;sqrt 25 = 5
;how-many-calls = 2
;reset-count0
;sqrt 25 = 5
;how-many-calls = 1
;- add
;+ 100 10 = 110
)
(ex-3.2)
```

### Ex 3.3

```lisp
;make-account ⼿続きを修正し、パスワードで守ら
;れた⼝座を作成するようにせよ。具体的には、次のように makeaccount
;が追加の引数としてひとつの記号を取るようにする。
;(define acc (make-account 100 'secret-password))
;結果としてできる⼝座オブジェクトは、アカウント作成時のパス
;ワードを伴っている場合にだけ要求を処理し、その他の場合には
;エラーメッセージを出す。
;((acc 'secret-password 'withdraw) 40)
;60
;((acc 'some-other-password 'deposit) 50)
;"Incorrect password"

(define (make-account password balance)
  (define correct-password 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 password m)
    (if 
      (eq? password correct-password)
      (cond 
        ((eq? m 'withdraw) withdraw)
        ((eq? m 'deposit) deposit)
        (else (error "Unknown request: MAKE-ACCOUNT" m)))
      (lambda (args) "Incorrect password"))
  )
dispatch)

(define (ex-3.3)
  (define account (make-account 'nyan 0))
  (print ((account 'nyan 'withdraw) 10))
  (print ((account 'nyan 'deposit) 10))
  (print ((account 'nyan 'withdraw) 10))
  (print ((account 'nyanko 'withdraw) 10))
  (print ((account 'nyanko 'deposit) 10))
;Insufficient funds
;10
;0
;Incorrect password
;Incorrect password
)

(ex-3.3)
```

### Ex 3.4

```lisp
;make-account ⼿続きを修正し、パスワードで守ら
;れた⼝座を作成するようにせよ。具体的には、次のように makeaccount
;が追加の引数としてひとつの記号を取るようにする。
;(define acc (make-account 100 'secret-password))
;結果としてできる⼝座オブジェクトは、アカウント作成時のパス
;ワードを伴っている場合にだけ要求を処理し、その他の場合には
;エラーメッセージを出す。
;((acc 'secret-password 'withdraw) 40)
;60
;((acc 'some-other-password 'deposit) 50)
;"Incorrect password"

(define (make-account password balance)
  (define correct-password password)
  (define incorrect-num 0)

  (define (withdraw amount)
    (if (>= balance amount)
      (begin 
        (set! balance (- balance amount))
        balance)
    "Insufficient funds"))

  (define (deposit amount)
    (set! balance (+ balance amount))　balance)

  (define (call-the-cops) "がおー！警察だぞー！")

  (define (dispatch password m)
    (if 
      (eq? password correct-password)
      (cond 
        ((eq? m 'withdraw) withdraw)
        ((eq? m 'deposit) deposit)
        (else (error "Unknown request: MAKE-ACCOUNT" m)))
      (lambda (args) 
        (begin 
          (set! incorrect-num (+ incorrect-num 1))　
          (if 
            (> incorrect-num 7) 
            (call-the-cops)
            "Incorrect password"
          ))))
  )
dispatch)

(define (ex-3.4)
  (define account (make-account 'nyan 0))
  (print ((account 'nyan 'withdraw) 10))
  (print ((account 'nyan 'deposit) 10))
  (print ((account 'nyan 'withdraw) 10))
  (print ((account 'nyanko 'withdraw) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))
  (print ((account 'nyanko 'deposit) 10))  
;Insufficient funds
;10
;0
;Incorrect password
;Incorrect password
;Incorrect password
;Incorrect password
;Incorrect password
;Incorrect password
;Incorrect password
;がおー！警察だぞー！
;がおー！警察だぞー！
)

(ex-3.4)
```sc