SICPラン④(p31~1.2 手続きとそれが生成するプロセス)
これまでは、プログラミングの組み立て方を学んできた。算術演算子を用いた単純な手続きを組み合わせ、ときに特殊形式のifを用いて複雑な手続きを組み立てた。(例えば、ある整数の平方根を求める手続き)
ここからは、それらの手続きをどのようにして用いればより効果が得られるかを学ぶ。いわば定石の習得に入る。
1.2.1 線形反復と反復
n! = n・(n-1)・(n-2)・・・3・2・1
階乗を計算する方法は二つ
①n・(n-1)!
②の答えに、数字をかけていく。そしてnまでカウントアップしていく、
という方法がある。
①に関しては、掛け算の連鎖により式が展開されその後縮約されていく。これを再帰(recursive process)プロセスと呼び、次に、インタプリタは計算のため計算プロセスを保持しておく必要がある。つまり展開する式の長さと計算に必要なステップ数はnに比例していることがわかる。ここまで含めて線形再帰プロセス(linear reucrisve process)と呼ばれる。
②の方法は、①と違って展開式が伸びることはない。なぜなら積に対してカウンターとなる数をかけ、カウンターがnになった時点でそれが階乗の答えとなる。このように展開式は伸びないが、ステップ数は①と同様にnに比例する。これを線形反復プロセス(linear iterative process)と呼ぶ。
練習問題 1.9
>|scheme|
(define (+ a b))
(if (= a 0) b (inc (+ (dec a) b)))
||<
上記の式の場合、式は再帰プロセスとなる。
しかし、以下の式においては
>|scheme|
(define (+ a b))
(if (= a 0) b (+ (dec a) (inc c)))
||<
ifの条件式以下で式が展開し、それは反復プロセスとなる。
SICPラン③(p26~1.18. 例: ブラックボックス抽象化としての手続き)
作られた手続きは、中身が詳細に知られる必要がない。簡単な例だとニュートン法で用いたsquareで、返り値として引数の二乗がかえってこれば良いのであって、どのように実装されているのかは知る必要がない。ブラックボックスで構わないのである。
そして、一連の手続きのネストを作ると、その他の手続きにおいても同じ名前で使うことができる。(つまりある数の平方根を求める手続き以外でも、improveやgood-enough?などが利用できるようになる。sqrt以外の手続き、いわゆるサブ手続きをsqrt内に局所化することで実現する。これをブロック構造という*1。)
(define (sqrt x) (define (good-enough? guess x) (< (abs (- (square guess) x)) 0.001)) (define (improve guess x) (average guess (/ x guess))) (define (sqrt-iter guess x) (if (good-enough? guess x) guess (sqrt-iter (improve guess x) x))) (sqrt-iter 1.0 x))
そしてさらにsqrt内で変数xを自由変数として扱うと、そのスコープ内における各手続きに明示的に値を渡す必要がなくなる。
(define (sqrt x) (define (good-enough? guess) (< (abs (- (square guess) x)) 0.001)) (define (improve guess) (average guess (/ x guess))) (define (sqrt-iter guess) (if (good-enough? guess) guess (sqrt-iter (improve guess)))) (sqrt-iter 1.0))
xの値の探索は、その手続きが定義されている手続きの環境内で探索される。その範囲をレキシカルスコーピング(lexical scoping)という。(第三章で詳しく扱う)
SICPラン②(p22~ 1.1.7. 例: ニュートン法による平方根)
今まで出てきた「手続き」というのは、数学の関数によく似ている。しかしそれらには決定的に異なることがある。「手続き」では物事をどのようにして行うのかについて述べており、関数では物事の性質を述べているという違いである。(命令的知識と宣言的知識というらしい)
例として、平方根の関数の定義は
かつ であるような
とされている。これは、平方根の性質を述べているだけでじゃあ具体的にどのように導出するのか、ということを「手続き」という。
そこで、ニュートン法による近似値導出をおこなう。これが手続き。
(define (square x) (* x x)) (define (sqrt-iter guess x) (if(godd-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 (godd-enough? guess x) (< (abs (-(square guess) x))0.001)) (define (sqrt x) (sqrt-iter 1.0 x))
Ex 1.6: ifを特殊形式ではなく、condを使って新たにnew-ifを定義した場合の挙動の説明
(define (square x) (* x x)) (define (new-if predicate then-clause else-clause) (cond (predicate then-clause) (else else-clause))) (define (sqrt-iter guess x) (new-if(godd-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 (godd-enough? guess x) (< (abs (-(square guess) x))0.001)) (define (sqrt x) (sqrt-iter 1.0 x))
この時、処理が終わらない。特殊形式としてのifは、述語式が真のときに一つ目の引数を評価し、偽の場合にのみ二つ目の引数を評価する。しかし手続きとして定義されたnew-ifはすべての引数を最初に評価するので、sqrt-iterが評価され無限ループになる。
SICPラン①(p20~)
SICPを読む過程で解いた問題の解答や、理解したことなどを書く。なお私はCSの学位などとは全く無縁であるので、参考になるものはなく間違っていることが多いと思う。
Exercise 1.3:三つの数値のうち大きい二つを選び、それらの二乗和をかえす(三つは任意の異なる整数)
(define (square x) (* x x)) (define (sum x y z) (cond ((and (> x y ) (> y z )) (+ (square x) (square y))) ((and (> x y ) (> z y )) (+ (square x) (square z))) ((and (> y x ) (> x z )) (+ (square y) (square x))) ((and (> y x ) (> z x )) (+ (square y) (square z))) ((and (> z x ) (> x y )) (+ (square z) (square x))) ((and (> z x ) (> y x )) (+ (square z) (square y)))))
Exercise 1.4:以下の手続きの挙動の説明
(define ( a-plus-abs-b a b ) (( if ( > b 0 ) + - ) a b ))
という式の挙動は、a-plus-abs-bという手続きを定義している。そしてその手続きは、bの正負を判断し、正の場合は+、負の場合は-というオペレータを返す。最後に、そのオペレータはaとbのオペレータとなっている。オペレータの部分が複合式(今回だとifの条件式の部分を指す)でも、問題なく評価は行われる。ifのconsequent部分をb - に置き換えるとnot a procedureというエラーが出る。
Exercise 1.5:以下の手続きについて、適用順序評価と正規順序評価による違いの説明
(define (p) (p)) (define (test x y) (if (= x 0 ) 0 y )) <|| Applicative-Order Evaluation(AOE)だと、引数を評価してから手続きを適用するので、上記の式だとpはpのまま際限なく評価され続ける。一方Normal-Order Evaluation(NOE)だと、基本演算子のみの式になるまで簡約するので条件式が適用されるので、無限に評価されることは生じない。
コトバンクにおける不可解な記載
ほんの興味から「イオン(ion)」の語源について調べていた。全く科学系の事柄に関しては昏いのであるが、今回少し調べてみたので備忘録的に記す。
結論から言うと、イオン(ion)はギリシャ語の「行く(ienai)」をもとに、ファラデー(Michael Faraday, 1791-1867)が、電気分解の際に移動する物質に対して名付けたものである*1。
その際に陽イオンを「下がる」を意味するcation、陰イオンを「上がる」を意味するanionと名付けた。(ここのあたりで、なぜ陽イオンを「上がる」、陰イオンを「下がる」としたのかは調べられていない。)
ここで、コトバンクにおいて不可解な説明がなされていた。ファラデーの電気分解の実験には同じように触れているのだが、陽極と陰極にひきつけられる物質を、「行く」ものとしてイオンと名付けたとしている。そこで、陽極(anode)に引き寄せられるものを陰イオン、つまりanionと名づけ、陰極(cathode)に引き寄せられるものを陽イオン、つまりcationと名付けたとしている。しかし、これは関係が逆ではないのか。なぜ陽極をanodeと名付けたのか。『気ままに有機化学』のようにその振る舞いから名付けられた後に陽極、陰極ともに名付けられたのではないのか。
■
コンラッド『闇の奥』、サン=テグジュペリ『夜間飛行』を読んだ。
コンラッドに関しては、映画『地獄の黙示録』でベトナムに舞台をうつしてリメイクされている。初めは、植民地主義に対する批判的姿勢をとるものだと思ったが(実際その読みは正しいのだろうけど、物語の主張したいこととは異なる気がする)、私の感想で言えば、アフリカというフィールド(コンラッドの実体験に基づくものであり、その時代におけるアフリカに対する認識を念頭において)を考えるとそれは、人間の原初的な恐怖を味わったひとの語る物語である。当時の未知というと、それは現代とは比べられないほど知られていないものであり、全くの異世界であった。その中において、自身と同じ世界に属し同じ原理で動いていたはずのクルツ氏が、未知の世界で大いなる影響力をふるっているという事態に遭遇する。その真実の姿に近づくまでの恐怖、知りたいけれども、知ってしまったが最後、自分自身はどのようになるのだろうか。未知のものに触れた自分は、これまで属してきた世界を離れ、クルツ氏のようになってしまうのか。自身の存在が揺るがされてしまうような現実に触れるまでの船乗りマーロウの心情の吐露は半ば自嘲気味に茶化す様子も感じられる。それは、未知のものに触れて自身の存在がゆるがせにされたものの、何とか生きている自分、しかし確実に未知の世界に触れる前の時分とはちがう自分を意識している。そのギャップから生ずる安堵感、違和感が彼の語り口に浪漫的な冒険話の趣を与え、自分で体験することでしか把握できないという彼の言明のとおり、語られることと自分の語り口から生ずる違和感も感じていた。
サン=テグジュペリ『夜間飛行』
飛行機を飛ばす使命を帯びたリヴィエール氏とそこで働くファビアン。リヴィエール氏が作り出す仕事の世界。そこは非人道的なものであり一切の人情の介入を許さない。仕事というと何か陳腐なものに思えるかもしれないが、命を失う危険をはらむ仕事である。しかしどうしてか非常に美しい。それはファビアンが墜落直前に雲海の上に飛び出し、満点の星空が頭上をゆったりと瞬くのを眺めそれに心奪われるシーンを見れば一目瞭然であろう。それは宮崎駿の『紅の豚』の飛行機の墓場、『風立ちぬ』の最後の煉獄のシーンを思い起こさせる。非情な夢の残骸は、美しさを放ち心をとらえた。ファビアンは夢の残骸の予兆を感じたのだろうか。死の瀬戸際で、ほぼ死に傾いているときにその美しさはいやがうえにも増すのだろうか。
『闇の奥』ではクルツ氏に対するマーロウの思いを、『夜間飛行』ではファビアンの雲海に出るまでの心理描写の機微、そして彼の妻がリヴィエール氏に与えた影響を、次にもうちょっと考えて読む。