SICP 3.1.1

SICPを読み進める上での個人的なメモです。

# 気になった部分の引用

## 3.1.1 局所状態変数
```lang:scheme
(define balance 100)
(define (withdraw amount)
  (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
      "Insufficient funds"
  )
)
```

実行例：
>\>(withdraw 25)
75
\>(withdraw 25)
50
\>(withdraw 60)
"Insufficient funds"
\>(withdraw 15)
36

>
式 (withdraw 25) は二回評価されていますが、それぞれ違う値を返しているというところに注意してください。これは、手続きのふるまいとして、これまでになかった種類のものです。これまでは、手続きはすべて数学関数を計算する仕様として捉えることが可能でした。手続きの呼び出しを行うと、与えられた引数を関数に適用したときの値の計算が行われ、同じ手続きを同じ引数で二回呼び出すと、いつでも同じ値が返ってくることになっていました。

### (set! \<name> \<new-value>)
>balanceの減算はset!という特殊式を用いる。
変数\<name>の値を\<new-value>に変更する。

### (begin \<exp1> \<exp2>...\<expk>)
>ifがTrueの場合、二つの式が評価されるようにするために、beginという特殊形式を用いる。\<exp1>～\<expk>までの式が順番に評価され、最後の式(\<expk>)を評価した結果の値がbegin全体の値として返される。

### カプセル化

```lang:scheme
(define new-withdraw
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 　balance)
          "Insufficient funds"
      )
    )
  )
)
```

実行結果
>\>(new-withdraw 10)
>90
>\>(new-withdraw 10)
>80
>

>この局所環境の中で、amount を引数に取り以前の withdraw 手続きと同じようなふるまいをする手続きを lambda を使って作っています。let 式を評価した結果として返されるこの手続きは new-withdraw で、withdraw と正確に同じふるまいをしますが、変数 balance はほかのどの手続きからもアクセスできません。

>プログラミング言語の業界用語では、変数 balance は手続き new-withdraw の中に **カプセル化 (encapsulated)** されていると言います。カプセル化は、隠蔽原理 (hiding principle) として知られる、一般的なシステム設計の原則を反映したものです。隠蔽原理とは、システムの各部品をほかの部品から守ることによって、システムをよりモジュール的で頑健なものにできるという考え方です。これはつまり、“知る必要” のあるシステムの部品にだけ、情報へのアクセス権を与えるということです。

>set! を局所変数と組み合わせるというのは、今後局所状態を持つ計算オブジェクトを構築するのに使うことになる一般的なプログラミングテクニックです。残念ながら、このテクニックを使うと深刻な問題が出てきます。

>問題は、言語に代入を導入したとたん、置換というのは手続き適用の適切なモデルではなくなるということです (どうしてそうなるのかは3.1.3 節で見ていきます)。

##　メッセージパッシング
```lang:scheme
(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"))
    )
  )
  dispatch
)
```

実行結果：
>
\>(define acc (make-account 100))
\>((acc 'withdraw) 60)
40
\>((acc 'deposit) 30)
70


>
make-account を呼び出すたびに、局所状態変数 balance を持つ環境が構築されます。この環境の中で、make-account は balance にアクセスする手続き deposit, withdraw と、また “メッセージ” を入力として取り二つの局所手続きのうちひとつを返す dispatch という追加の手続きを定義します。銀行口座オブジェクトを表現する値としては、dispatch 手続きそのものが返されます。これはまさに、2.4.3 節で学んだ **メッセージパッシング (message-passing)** のプログラミングスタイルですが、ここでは局所変数を変更する能力と合わせて使っ ています。

# Exercise
###Ex 3.1
初期値に加算していく

```lang:scheme
(define (make-accumulator init)
  (lambda (operand)
    (set! init (+ init operand))
    init
  )
)
```

実行結果
>
\> (define A (make-accumulator 5))
\> (A 10)
15
\> (A 10)
25

### Ex 3.2
呼び出し回数を数える。特定のメッセージの場合に呼び出し回数を返したり、
カウンタをリセットする。

```lang:scheme
(define (make-monitored procedure)
  (let ((cnt 0))
    (define (dispatch args)
      (cond ((eq? args 'how-many-calls?) cnt)
            ((eq? args 'reset-count)
              (begin (set! cnt 0)
                     cnt))
            (else (begin
                   　(set! cnt (+ cnt 1))
                   　(procedure args)
                  )
　　　　　　　　　　　　　　　　　　　　　　　　)
      )
    )
    dispatch
  )
)
```
実行結果：
>
\> (define s (make-monitored sqrt))
\> (s 100)
10
\> (s 'how-many-calls?)
1
\> (s 'reset-count)
0
\> (s 'how-many-calls?)
0

### Ex 3.3
銀行口座にパスワードをつける。パスワードを間違えるとメッセージを表示

```lang:scheme
(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 (incorrect DUMMY)
    "Incorrect password"
  )
  (define (dispatch p m)
    (if (eq? p password)
      (cond ((eq? m 'withdraw) withdraw)
            ((eq? m 'deposit) deposit)
            (else (error "Unknown request: MAKE-ACCOUNT"))
      )
      incorrect
    )
  )
  dispatch
)
```
実行結果：
>
\> (define acc (make-account 100 'secret-password))
\> ((acc 'secret-password 'withdraw) 40)
60
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"

### Ex 3.4
パスワードを７回間違えると警察を呼ぶ（call-the-cops）

```lang:scheme
(define (make-account balance password)
  (let ((err-count 0) (ERR-LIMIT 7))
    (define (withdraw amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                     balance)
          "Insufficient funds"
      )
    )
    (define (deposit amount)
      (set! balance (+ balance amount))
      balance
    )
    (define (incorrect DUMMY)
      (begin (set! err-count (+ err-count 1))
             (if (>= err-count ERR-LIMIT)
                  "call-the-cops!!"
                  "Incorrect password"
             )
      )
    )
    (define (dispatch p m)
      (if (eq? p password)
        (cond ((eq? m 'withdraw) withdraw)
              ((eq? m 'deposit) deposit)
              (else (error "Unknown request: MAKE-ACCOUNT"))
        )
        incorrect
      )
    )
    dispatch  
  )
)
```

実行結果：
>
\> (define acc (make-account 100 'secret-password))
\> ((acc 'secret-password 'withdraw) 40)
60
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"Incorrect password"
\> ((acc 'some-other-password 'deposit) 50)
"call-the-cops!!"

# 参考
[SICP非公式日本語版 翻訳改訂版](http://vocrf.net/docs_ja/jsicp.pdf)