僕がLispを美しいと感じるのは、ストレスを感じなく書けるからだと思う。アナフォリックマクロの記事でもそうなのだが、プログラムを書いている最中に不愉快な思考の停止が起こることがあり、これがプログラム中に感じるストレスの最たるものだと思う。ちょっと改変すると途端に問題が起こる上、汚くなる。それを嫌うならば書き直す必要が出てくるのでプログラム効率はかなり落ちる。こういったちょっとした「何でこんなことが出来ないのか!」というのは多々あるが、今回のmultiple-value-bindもそれだ。
先日ソースコードのコメントに”破壊的代入”と書いたら、変なコメント入れないでくださいと言われた。今まで会話の中で普通に破壊的代入という言葉を使ってきたのだが、改めて意味を知っているかと聞くと知らないと言う人が多かった。ていうか、知らないなら聞けよ。
破壊的代入とはCならポインタとか、VBならbyrefとかいう奴のことだ。関数の呼び出し元で渡された変数が、読み出し先の関数で改変されてきてしまう場合だ。
例1
呼び出し元
a = 3
plus1(a)
呼び出し先
void plus1(ref a1)
a1 = a1 + 1
まぁ、普通一つの値を返すだけの関数ならこんなことをせずに、計算結果を復帰すればよい。明らかにこちらのほうが綺麗であるし、バグが少ない。
例2
呼び出し元
a = 3
x = plus1(3)
呼び出し先
int plus1(a1)
return a1 + 1
ただし、複数の値を復帰したい場合に、通常の手続き型言語ではそういった機構がなく、やるならばそのためにわざわざ構造体(クラスでも可だが、クラスにした瞬間に参照型になるので破壊的代入と同じになる言語が多い)、型が同一ならば配列を定義する必要がある。そういうのは正直やりすぎなので、例1のようなアプローチを応用するのは仕方が無い。
例3
呼び出し元
a = 3
b = 4
plus1(a,b)
呼び出し先
void plus1(ref a1,ref b1)
a1 = a1 + 1
b1 = b1 + 1手続き型の言語(大方のオブジェクト指向型含む)は副作用を容認するため仕方が無い。本当は 以下のように書きたいが、文法上不可能である。
例4
呼び出し元
a = 3
b = 4
(x,y) = plus1(a,b)
呼び出し先
void plus1(a1,b1)
return a1 + 1 , b1 + 1
Lispならばこれで良い。
例4Lispでの実現版
呼び出し元
(let ((a 1) (b 2))
(multiple-value-bind (x y)
(plus1 a b)))
呼び出し先
(defun plus1(a1 b1)
(values (1+ a1) (1+ b1)))
くどくなるがポイントは
1.呼び出し先で複数の値を復帰できる
2.呼び出し元で複数の値を取得できる
3.すべて値渡しなので副作用は無い(つまり例1ではなく例2のようなアプローチが可能となる)
このようにLispは綺麗でストレスがたまらない言語なのだ。これ読んでLispはじめる人が居たら良いなぁ。いないと思うけど。