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

ネストした足し算に対応しました。

感動した。

追記

mapを応用すれば字句解析〜構文解析も改善出来そう。

「大体Schemeには構文なんてないんだから」

文字の写像Schemeオブジェクトに変換するだけでいい。すげぇシンプルに書けそう。

追記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なら逆順に突っ込むとか(汗