こないだ買ったSICPを通勤電車で読んでるんだけど、面白すぎる。
(でも通勤電車で本文を読んで家で練習問題をやろうと思ってたけど、家ではついニコニコなんぞを見てしまうので、本文は3章を読んでるのに練習問題はまだ1章の途中までしかやれてない。くやしいのうwwwwくやしいのうwwww)
例えば1章では、√xを求める関数を書くのを題材に、関数による処理の抽象化について説明している。
√xの近似値を guess として、(x/guess + guess)/2 でよりよい近似値が得られる(これはf(x)=√xに関するニュートンラプソン法による)というのを天下り的に与えられたとき、手続き型言語脳だとどう書くだろう?
new function(){
function average(a, b){ return (a + b)/2 }
function square(a){ return a * a }
//手続き型言語的に √x を求める
function procedual_sqrt(x){
var guess = 1.0; //暫定初期値
var EPSILON = 0.0001; //許容する誤差
//指定した誤差より大きい間ループ
//(あまりよい評価方法ではない)
while(Math.abs(square(guess)-x)>EPSILON){
//近似値をニュートンラプソン法により改善
guess = average(x/guess, guess);
}
return guess;
}
alert(procedual_sqrt(2));
}
JavaScriptで書くならこんな感じだろうか。average()とかsquare()とかは関数にするまでもないと感じる人もいるかもしれない。
短いプログラムながら、ループとか変数の宣言とか関数とか色んな要素がつまってる。
初心者向けの練習問題としてはいい感じだが、一方で色んな要素がつまってるからこれを書けるようになるように説明するのは大変といえば大変だ。
一方SICPでは、Schemeでこの問題を解くまでに、
- 式の評価のされ方
-
Schemeの式は (演算子 オペランド1 オペランド2 ... ) のようになっており、
(+ 1 2)
は 3 と評価される。
- 関数の使い方
-
(define (関数名 仮引数...) (関数本体の式))
のように関数を定義する。
(define (average x y) (/ (+ x y) 2)) (average 1 3)
は 2 と評価される
- 条件分岐
-
ifやcondの書き方。
絶対値を求める関数は次のように書ける
(define (abs x) (if (> x 0) x (- x) ))
以上3つだけ説明して、この知識だけで問題を解いてみせる。
ちょwww変数は?ループ制御構文は???
Schemeコード貼ってもアレなので、ほぼ等価なJavaScriptコードで示すと次のような感じ。
new function(){
function functional_sqrt(x){
function average(a, b){ return (a + b)/2 }
function square(a){ return a * a }
function good_enough(guess){
//近似値の評価
//(あまりよい評価方法ではない)
return Math.abs(square(guess)-x)<0.0001;
}
function improve(guess){
//ニュートンラプソン法
return average(x/guess, guess);
}
function sqrt_iter(guess){
return ( good_enough(guess) ) ? guess
: sqrt_iter( improve(guess) );
}
return sqrt_iter(1.0);
}
alert(functional_sqrt(2));
}
確かに変数もループも使ってない!
本質的に再帰的なアルゴリズムを実装してやることで、しれーっと再帰的手続きの概念を教えちゃう辺りがすごい。
このあともこんな感じで、2章など有理数や複素数の加減乗除を行うモジュールを作るのを題材に、データ抽象について説明するのだが、ここでも変数なんて軟弱な概念は出てこない。ちょwwww
3章で状態をどう取り扱うかという話題になって初めて変数とか出てきた。でもそれも変数使うと参照透過性が失われて色々ややこしいことが起きるから、後半では変わりにstreamを使える場合ががあるよとかいう話になってたり。
色々面白いのでみんなもっとSICPを読むべきだと思いました。

