Schemeをつくろう(5) 作用ってなんだろう?
evalの処理について考えてみます。今のところネストした足し算には対応してないので(汗
「作用ってなんだろう?」
と考える。
(+ 1 2 3)
足し算です。
単純に考えると、
(シンボル . リスト)
な訳。
シンボルとリストの対が手続きである。
で、手続きを実行する行為を作用というのだ。
(と思う)
では、
「作用はいつのタイミングですれば良いか?」
と考えると、リストの最初がシンボルで、次がリストである時(手続きの時)に、リストの末尾に到達したら、作用出来る。
無駄な足し算で考えると、
(+ (+ 1 2) (+ 2 3)) (+ 3 (+ 2 3)) (+ 3 5) 8
閉じ括弧が来たら作用。見えてないけど空リストがあったら作用出来る。
最初は単純なfor(;;)なんてやってたけど大きな間違いであることに気付いた。
for(;;)なんてやってしまうと反復なのでリストが逆順に入ってしまう。つまり、appendでいちいちリストの末尾に追加していかなければならない。なんて非効率なんだ!!と思ってたら、
「map使えばいいじゃん」
ということに気付いた。
mapはとりあえずリストの末尾まで行って、nullがあったら、末尾からconsしていく。
「mapは再帰。reverseは反復。」
map使えばリストが逆順にならない
で、mapを書いてみた。
object *map(object *(*f)(object *), object *o)
{
return is_null(o) ?
null() :
cons(f(car(o)), map(f,cdr(o)));
}
全てSchemeオブジェクトで書けてしまう。
SchemeオブジェクトからSchemeオブジェクトを作る。これこそがScheme!!
うっは、スゲー。手続きもSchemeオブジェクトにしちゃえばいいんだ。
ちょっと先が見えてきた。
その後
map-eval再帰で足し算出来た!!
input> (+ (+) 1 (+ 2 (+ 3 4) 5) (+ (+ (+ 6) 7) 8 9) (+ 10)) 55
ネストした足し算に対応しました。
感動した。
追記2
SICPの1章に定義が色々書いてあった。やっと1章の最初の方が理解出来そうだ。そういえば、SICPを読み始めたときに1章の意味が全然わからなかった気がする。もう一度読みかえしてみると意味がすんなり入ってくる。ちょっと読み直してみるかなぁ。
追記3
mapを採用すると、評価順序が面白い。
(+ (+ 1 2) (+ 2 3)) (+ (+ 1 2) 5) (+ 3 5) 8
引数リストの末尾から評価していくことになる。
不思議な感覚。
追記4
マテ。
ってことはだよ。複文だったら逆順に評価されちゃうって事だよね。
こんなん。
(define (hoge) (display "hoge ") (display "moge ")) (hoge) moge hoge
だめじゃん。
map法に問題発生。
lambdaなら逆順に突っ込むとか(汗