SICPを読む2
おもいがけず休日だったのでSICPの練習問題を解く。問題1.7から1.11まで。はいはい末尾再帰末尾再帰。反復的プロセスですね。このペースだといつやり終えるんだろうか。
問題1.7
(define (square x) (* x x)) (define (sqrt-iter guess x) (if (good-enough? guess x) guess (sqrt-iter (improve guess x) x))) (define (improve guess x) (average guess (/ x guess))) (define (average x y) (/ (+ x y) 2)) (define (sqrt x) (sqrt-iter 1.0 x)) (define (good-enough? guess x) (= guess (improve guess x))) ; good-enough? (sqrt 2.0) ; 1.414213562373095 (sqrt 100) ; 10.0 (sqrt 1000) ; 31.622776601683793 (sqrt 14024194) ; 3744.889050425927
になるまでやっていただきます。
元々組み込まれてるsqrtだと、
(sqrt 2.0) ; 1.4142135623730951 (sqrt 100) ; 10.0 (sqrt 1000) ; 31.622776601683793 (sqrt 14024194) ; 3744.889050425927
(sqrt 2.0)
で一桁多いですね。なんか近似の式がちょっと違うのかな?
まあでもだいたいよし。
問題1.8
(define (cbrt x) (define (iter guess x) (if (good-enough? guess x) guess (iter (improve guess x) x))) (define (improve guess x) (/ (+ (/ x (* guess guess)) (* 2 guess)) 3)) (define (good-enough? guess x) (= guess (improve guess x))) (iter 1.0 x)) ; cbrt (cbrt 27.0) ; 3.0 (cbrt 6) ; 1.8171205928321397 (cbrt 1000) ; 10.0 (cbrt 100)
あ、止まらねえ。これはあれか。真の立方根の値の前後で小さい振動を続けるんだろうな。
じゃあやっぱり
(define (cbrt x) (define (iter guess x) (if (good-enough? guess x) guess (iter (improve guess x) x))) (define (improve guess x) (/ (+ (/ x (* guess guess)) (* 2 guess)) 3)) (define (good-enough? guess x) (< (abs (- (cube guess) x)) (/ guess 1000000))) (define (cube x) (* x x x)) (iter 1.0 x)) (cbrt 27.0) ; 3.0000000000000977 (cbrt 6) ; 1.817120681877066 (cbrt 1000) ; 10.000000000000002 (cbrt 100) ; 4.64158883361313
こんなもんだろか。うーん、(cbrt 100)
とかの評価をできるようになったのはいいけれど。「振動してる」ってのを自覚させられればいいのですが。
1.2 手続きとその生成するプロセス
うーん末尾再帰が素敵ってことがよくわかってきますね。
問題1.9
線形再帰プロセス
(define (+ a b) (if (= a 0) b (inc (+ (dec a) b))))
これは以下のように計算されていきますね。
(+ 3 2) (inc (+ (dec 3) 2)) (inc (inc (+ (dec 2) 2))) (inc (inc (inc (+ (dec 1) 2)))) (inc (inc (inc 2))) (inc (inc 3)) (inc 4) 5
こちらが線形反復プロセス
(define (+ a b) (if (= a 0) b (+ (dec a) (inc b))))
こちらは以下のとおり。
(+ 3 2) (+ (dec 3) (inc 2)) (+ (dec 2) (inc 3)) (+ (dec 1) (inc 4)) 5
プロセスはこっちの方がコンパクトですね。
問題1.10
アッカーマン関数。なんだそれ聞いたことはある気がする。
(define (A x y) (cond ((= y 0) 0) ((= x 0) (* 2 y)) ((= y 1) 2) (else (A (- x 1) (A x (- y 1))))))
(or (= x 0) (= y 0) (= y 1))の時に終了。他はなんか再帰。
(A 1 10) ; (A 0 (A 1 9)) ; (A 0 (A 0 (A 1 8))) ; ... ; (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1)))))))))) ; (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 2))))))))) ; (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 4)))))))) ; ... ; 1024 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1)))))))))) ;テスト ; 1024 (A 2 4) ; (A 1 (A 2 3)) ; (A 1 (A 1 (A 2 2))) ; (A 1 (A 1 (A 1 (A 2 1)))) ; (A 1 (A 1 (A 1 2))) ; (A 1 (A 1 (A 0 (A 1 1))) ; (A 1 (A 1 (A 0 2)) ; (A 1 (A 1 4)) ; ... ; (A 1 (A 0 8)) ; (A 1 16) ; ... ; 65536 (A 3 3) ; (A 2 (A 3 2)) ; (A 2 (A 2 (A 3 1))) ; (A 2 (A 2 2)) ; (A 2 4) ; 65536
わかるよーなわかんねーよーな。いやよくわからんかな。
(define (f n) (A 0 n)) (define (g n) (A 1 n)) (define (h n) (A 2 n)) (define (k n) (* 5 n n))
だね。
問題1.11
。
再帰的プロセス
(define (f n) (if (< n 3) n (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3)))))) ; f (f 4) ; 11 (f 10) ; 1892
反復的プロセス
(define (f n) (define (iter v1 v2 v3 nown n) (if (= nown n) (g v1 v2 v3) (iter v2 v3 (g v1 v2 v3) (+ 1 nown) n))) (define (g v1 v2 v3) (+ (f (- n 1)) (* 2 (f (- n 2))) (* 3 (f (- n 3))))) (if (< n 3) n (iter 0 1 2 3 n))) ; f (f 4) ; 11 (f 10) ; 1892
できたー。って、なんか滅茶苦茶遅いんですけど。ああ、gの定義がむちゃだ。なんでfとか呼ぶよ。
(define (f n) (define (iter v1 v2 v3 nown n) (if (= nown n) (g v1 v2 v3) (iter v2 v3 (g v1 v2 v3) (+ 1 nown) n))) (define (g v1 v2 v3) (+ v3 (* 2 v2) (* 3 v1))) (if (< n 3) n (iter 0 1 2 3 n))) ; f (f 4) ; 11 (f 10) ; 1892
うひょー。
うひょーじゃねえよ。nownとかあるのも超だせえ。ああそうだ、計算式の中にnが関係してこないんだから、
(define (f n) (define (iter v1 v2 v3 n) (if (= n 3) (g v1 v2 v3) (iter v2 v3 (g v1 v2 v3) (- n 1)))) (define (g v1 v2 v3) (+ v3 (* 2 v2) (* 3 v1))) (if (< n 3) n (iter 0 1 2 n)))
これでいいのね。nownとかださいけど、別にそういう書き方でもいい気がしてきた。