SICPを読む(47) 問題 2.17 - 2.20 一発で解けた。
一発で解けた。スゲー嬉しい!!
問題 2.17
リストの最後の要素をリストで返す。
(define odds (list 1 3 5 7)) (define (last-pair items) (if (null? (cdr items)) items (last-pair (cdr items)))) (last-pair odds) ; (7)
(null? (cdr items))で一個先読みしてるのがポイント。
問題 2.18
リストの逆順を返す問題。
反復を使った。
(define (reverse items) (define (iter i j) (if (null? i) j (iter (cdr i) (cons (car i) j)))) (iter items `())) (reverse odds) ; (7 5 3 1)
cdrダウンする感覚を掴んできたぞぉ〜。
問題 2.19
コインの両替問題。
(define us-coins (list 50 25 10 5 1)) (define uk-coins (list 100 50 20 10 5 2 1 0.5)) (define no-more? null?) (define except-first-denomination cdr) (define first-denomination car) (define (cc amount coin-values) (cond ((= amount 0) 1) ((or (< amount 0) (no-more? coin-values)) 0) (else (+ (cc amount (except-first-denomination coin-values)) (cc (- amount (first-denomination coin-values)) coin-values))))) (cc 100 us-coins) ; 292 (cc 100 uk-coins) ; 104561
難しそうだったけど、car,cdr,null?置き換えれば良いだけ。coin-valuesの順を変えても影響はない。
「ccを作れ」という問題じゃなくて良かった。
uk-coinsはめちゃめちゃ時間がかかる。計算結果をキャッシュ出来たらなぁ・・・先は長い・・・。
問題 2.20
ドット末尾記法を使う。最初の引数を見て、偶数リスト、奇数リストを作る問題。
(define (same-parity . items) (let ((parity? (if (even? (car items)) even? odd?))) (define (iter i) (cond ((null? i) `()) ((parity? (car i)) (cons (car i) (iter (cdr i)))) (else (iter (cdr i))))) (iter items))) (same-parity 1 2 3 4 5 6 7) ; (1 3 5 7) (same-parity 2 3 4 5 6 7) ; (2 4 6)
parity?してみて、評価が#tなら、リストに追加。そうでなければ空読みするだけ。
(define parity? ...)と書くと、毎回(even? (car items)が呼ばれることになってしまうのでかなり無駄なので、letを使って計算回数を減らしてみた。奇数を評価する場合、even?は1度しか呼ばれない。
まとめ
- cdrダウンの感覚重要
- cdrダウン+consアップでリストのコピーが作れる。間に処理を加えれば任意のリストが出来上がる。
- 再帰だと正順、反復だと逆順になるっぽい。
- リストの末尾に値を加えたいとき、先頭からnull?を探さなければならないのだろうか?ポインタが欲しい!!
- 追記:リスト末尾に要素を加えたい時はキューを使えばいい3.3.2を参照。