めちゃくちゃ簡単な例でいくと、これまで関数は
(define add1;数字に1を足した値を返す
(lambda (x)
(+ x 1)
)
)
な感じに書いていたが、lambdaの処理部分は実は
(define example (lambda (x)
(+ x 4)
(* x 3)
(- x 7)
)
)
のように書いても問題ない。
関数を定義するときのdefineの書き方として
(define (example)
(+ x 4)
(* x 3)
(- x 7)
)
のように書いても同じことができる。
ただし返される値はいずれも最後の評価結果(つまりx-7)となる。
連続して式を書く
上記のように式を連続して記述でき、最後の評価の結果をその返り値として返す記述方法がいくつかある。
- (define (関数名 引数) (処理) (処理) (処理) ... (最後の処理))
- (lambda (引数) (処理) (処理) (処理) ... (最後の処理))
- 分岐cond文における((条件式)またはelse (処理) (処理) (処理) ... (最後の処理))
- (let (変数関連の記述) (処理) (処理) (処理) ... (最後の処理))
- (begin (処理) (処理) (処理) ... (最後の処理))
これらを使えばCなどで普段書いているやり方に似たような記述ができる。
代入
と言うよりこれを使わずに書いてきた時を考えると代入と言うより塗り替えのイメージ。
(set! i 0)
これでiの値に0が代入される。その都度iを増加させたければ
(set! i (+ i 1))
と書けばいい。
i=0;
i+=1;//i=i+1
と同じことをやっている。
具体例で実際に
次の関数があったとする。
(define sum-all
(lambda (x)
(define i 1);足す値
(define ans 0);答え用変数
(define inner
(lambda ()
(if (<= i x)
(begin
(set! ans (+ ans i))
(set! i (+ i 1))
(inner)
)
)
)
)
(inner);実際に計算
ans;これがsumの返り値
)
)
↑なにか正の整数値xを与えると1+2+...+xを返してくれる関数です。
2行目から始まるlambda式の中に、
- 加算するための変数iの定義
- 答えを格納する変数ansの定義
- 1からxまでの値を足していく関数をinnerとして定義
- 関数innerを実際に評価
- innerを評価したことにより塗り替えられたansを戻り値として記述
している。
またinner関数でansの更新、iの更新、再帰的に自身の呼び出しをbeginを使用することで連続で記述している。
ansやiの更新は前述のようにset!で行っている。
でもこれは再帰的プロセスで
(define sum-all2 (lambda (x)
(if (= x 1)
1
(+ x (sum-all2 (- x 1)))
)
)
)
こう書けるし、反復的プロセスでは
(define sum-all3 (lambda (x)
(define inner
(lambda (x ans)
(if (= x 1)
(+ 1 ans)
(inner (- x 1) (+ x ans))
)
)
)
(inner x 0)
)
)
こう書ける。
置き換えモデルが使えなくなるうえに思わぬバグを生み出す恐れもあるのであまり手続形式で書きたくないなとは思ってます。ていうかもう覚えていませんが、期末テストでおそらくそれ関係で序盤のめちゃ簡単な問題一つ爆死させました(笑)
置き換えモデルが使えないとは何か
例えば次のような関数があったとする。
(define f (lambda (x)
(set! x (* x 2))
(set! x (+ x 2))
x
)
)
実際(f 2)を評価すると6が返ってきます。当然ですが。
xを受け取って、xを2倍した値に入れ替えて、そのxにさらに2を足すからです。
が、これを置き換えモデルで置き換えると
「あ、2が返ってくるかも」と思えてしまいます(←違います)。
(f 2)
=(set! x (* 2 2)) (set! x (+ 2 2)) 2(←この2が最後の評価値)
=2;最後の値を評価して返すから(でもこれは違います)。
setとつくやつは今後書こうと思ってるリストのやつも最初「ん?」となった思い出があります。