第1章振り返り
手続きの抽象がメインテーマと言うことで、Square Rootの定義を振り返ってみましょう。
定義(p.12)
(define (sqrt x) (the y (and (>= y 0) (= (square y) x))))
yが0以上でかつ2乗するとxになるもの
と言う定義をそのままS式にしてみました、って感じ。
でも、これも結構奥が深いらしくて、このような平叙文で書くと、それを自動的に命令文に変える、とかっていう研究が昔盛んに行われてたそうだ。
確かに、定義をそのまま書けると素敵だ。偏微分方程式を定義すると、それを解いてくれる、みたいな。
命令文的記述(p.13)
(define (sqrt 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 (good-enough? guess x) (< (abs (- (square guess) x)) 0.001)) (iter 1.0 x))
上記の定義式を反復法で記述したもの。コンピュータには、命令を与えてやらないといけない、と言うことですね。
Fixed-Point版1(解が振動するやつ) (p.39)
(define (sqrt x) (fixed-point (lambda (y) (/ x y)) 1.0))
x(n+1) = k / x(n) と言う更新式にしたやつ。
ここでのミソは、平方根を求めると言うプロセスを不動点を求める、と言う一般的な手続きに置き換えたこと。
sqrtの場合には、同じ点をぐるぐる回るだけで、収束しません。
Fixed-Point版2(緩和法を組み込んだもの) (p.39)
(define (sqrt x) (fixed-point (lambda (y) (average y (/ x y))) 1.0))
x(n+1) = (x(n) + k/x(n))/2 と言う更新式にしたやつ。移動量を減らしたことになります。これで不動点に収束します。
Fixed-Point版3(平均緩和の汎用化) (p.41)
(define (average-damp f) (lambda (x) (average x (f x)))) (define (sqrt x) (fixed-point (average-damp (lambad (y) (/ x y))) 1.0))
この辺から、段々鬼畜化して行く感じ。手続きを返す、と言うものが出てくると、頭が混乱してくる。
これは、平均緩和法は汎用的に使えるので、average-dampと言う手続きを作っちゃおう、と言うことですね。
Newton法でWrapした版 (p.42)
(define (newtons-method g guess) (fixed-point (newton-transform g) guess)) (define (newton-transform g) (lambda (x) (- x (/ (g x) ((deriv g) x))))) (define (sqrt x) (newtons-method (lamba (y) (- (square y) x)) 1.0))
今度は、Newton法は、不動点を求めるプロセスの専用化であり、さらに、平方根を求めるプロセスはNewton法の専用化だよね〜、と言うこと。
抽象化最強 (p.43)
(define (fixed-point-of-transform g transform guess) (fixed-point (transform g) guess)) (define (sqrt x) (fixed-point-of-transform (lambda (y) (/ x y)) average-damp 1.0)) (define (sqrt x) (fixed-point-of-transform (lambda (y) (- (square y) x)) newton-transform 1.0))
鬼畜過ぎ。でも、抽象化の壁がちゃんとしているため、コード自体は見やすい。でも、動作を追っかけるのは難しい。
結局、ぼくらは仕事をする上で、常に抽象化を考えているわけだけど、抽象化の仕方によって、きれいなインターフェースができたり、とんでもないインターフェースになったりする。APIを作る時には、いつも一貫性の取れたインターフェースになるように、適切な抽象化をしないと行けない。
この本は、ぼくに抽象化の難しさを教えてくれている、気もする。