SICPを読む(11) 1.2.2 再帰脳を作ろう。
段々再帰な人たちの言っていることが判りはじめた気がする。
再帰脳を作ろう。
僕等が1から10まで足すとしたら、
「あ、1から10までねハイハイ。」
1 + 2 + 3 + 4 + ... + 10 = 55
1から計算する。
再帰な人たちは、
「10の前は9だ。1まで足せば良い。」
10 + 9 + 8 + 7 + ..... + 1 = 55
後ろから数える。
1まで足せば良いと考えると、終端を考えなくて良い。
面倒そうだが、慣れると断然楽かもしれない。
ってことで、フィボナッチ数列
お馴染みのフィボナッチ数列。
0 1 1 2 3 5 8 13 21
頭から数えると、
0 + 1 = 1 1 + 1 = 2 1 + 2 = 3 2 + 3 = 5 ...
これが僕等的考え。反復。メリットは計算回数が少ない。
再帰な人は、
f(0) = 0 f(1) = 1 f(n) = f(n - 1) + f(n - 2)
1個前と2個前の状態を見る。終端を考える。メリットは、数式をそのまま当てはめるだけ。
それを踏まえて、再帰と反復を書く。
; 再帰 (define (fib n) (cond ((= n 0) 0) ((= n 1) 1) (else (+ (fib (- n 1)) (fib (- n 2)))))) ; 反復 (define (fib n) (define (iter a b count) (if (= n count) a (iter b (+ a b) (+ count 1)))) (iter 0 1 0)) (fib 8)
反復はやたら複雑になった。
サンプルは0まで計算していたけど、反復の練習の為、nまでに書き直してみた。
段々、Schemerの言う、「再帰で書いた方が楽じゃん」的な考え方がわかってきた気がする。
両替問題
1,5,10,25,50セントのコインがある。1ドルの両替に何パターンあるのか。
1ドルの両替をちょっと考える。
50 50 50 25 25 50 25 10 10 5 ... 50 1 1 1 1 1 ... 25 25 25 25 25 25 25 10 10 5 ... 1 1 1 1 1 1 ...
あぁぁぁ・・・となってしまったらアウト。再帰脳が出来ていない証拠。
両替問題では、再帰のポイントが違う。
- コインの種類を減らす(5種類あるものを4種類に)
- 金額を減らす(50セント引く)
- 金額が0になったらパターンを1個追加して、再帰終了。
理解するのに随分時間がかかった。とにかくシンプルなパターンを見つけ出すことが重要。
(define (count-change amount) (cc amount 5)) ; return 合計パターン (define (cc amount kinds-of-coins) (cond ((= amount 0) 1) ; 丁度両替出来た ((< amount 0) 0) ; 出来なかった。 ((= kinds-of-coins 0) 0) ; 最後までテストが完了した (else (+ (cc amount (- kinds-of-coins 1)) ; コインを減らす (cc (- amount (first-denomination kinds-of-coins)) ; 金額を減らしてテストする。 kinds-of-coins))))) (define (first-denomination kinds-of-coins) (cond ((= kinds-of-coins 1) 1) ((= kinds-of-coins 2) 5) ((= kinds-of-coins 3) 10) ((= kinds-of-coins 4) 25) ((= kinds-of-coins 5) 50)))
実は、同じパターンを何度も繰り返している。この本を読んでいくうちに、改善の方向に向かうんじゃないかなぁと思う。
追記
実はまだリスト脳が無かったりする。