Lisp

xyzzyを愛している

ある職場でエディタがさくらエディタのみの場所があった。自分の好きなものを入れてはいけないというのだ。xyzzyを入れたい僕としては、これがとてつもないストレスになった。別にLispでそれほどコードを組むわけではないが、何か気晴らしにマクロを書いたり、Webで検索したコードを試したりするのが息抜きとなる。設計書作りというのも悪くは無いのだが、やっぱり僕はプログラマだなぁと思う。


emacs系のエディタを使っている人はその使い勝手だけではなく、強力なLispという言語が付属していることによる、キッチンシンクな世界を味わっているのではないだろうか。そういう理由もあって、僕はxyzzyエディタが無いとそれだけで落ち着かない。

破壊的代入とmultiple-value-bind

僕が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はじめる人が居たら良いなぁ。いないと思うけど。

アナフォリックマクロ

昔居た会社にシンボリック社日本法人の元社員が数人居た。当然というか何と言うか彼らはLisperであり、それについて熱く語る人たちだった。そういった会社に居たにもかかわらず、結局Lispがどれほどすごい言語なのかさっぱりわからなかった。


Lispの真価を知ったのは、そのマクロ機能を解説した以下の本を読んでからだ。


On Lisp
クチコミを見る





どんな言語でも良いのだが、ある関数(hogeCalcとする)の結果を元に分岐をしたいとする。多分ほとんどのプログラマは次のように書くことだろう。


-----------------------------------------------------------

if (hogeCalc() == XXX) {

     処理

}

-----------------------------------------------------------

コード1


で、ここでhogeCalcで計算した結果を他の部分でも使わなければならないという事態が発生したとする。そうするともうこの構文は使用できない。if文の中を変更するのが嫌ならばhogeCalcをもう一度呼ぶしかない。スマートでないけど、忙しいとこんなことをしてしまう人も居ることだろう(僕だ)

------------------------------------------------------------

if (hogeCalc() == XXX) {

     処理

}

//後で追加した処理

result = hogeCalc() + 100

------------------------------------------------------------

コード1-2



追加処理が一つだけなんで問題ないが、さらに追加した場合はこうなる


------------------------------------------------------------

if (hogeCalc() == XXX) {

     処理

}

//後で追加した処理

result = hogeCalc() + 100

result2 = hogeClac() - 100


------------------------------------------------------------

コード1-3



こうなるとソースコードを見られた場合に結構恥ずかしい。気にしない人もいるが(当然僕だ)

hogeCalcが軽い処理の場合は3回呼び出そうが1回呼び出そうが関係なくソースコードが汚いだけで済むのだが、hogeCalcがデータベースに接続したりする場合は、処理の時間が秒単位で変わる。悪態をつきながらも、以下のように書き直さざるを得ない。


------------------------------------------------------------

hoge = hogeCalc()


if (hoge == XXX) {

     処理

}

//後で追加した処理

result = hoge + 100

result2 = hoge - 100


------------------------------------------------------------

コード1-4


これは現行の手続き型言語ではどうしようもない。それはIF文がそういったものだからだ。では、”IF文の条件で実行された値を保持する”というIF文を作ってしまえば良いではないか、これがLispの発想だ。


新たにaif(アナフォリックIF)という特殊なIF文を作成する。これはIFの条件部分で実行された結果が特定の変数に代入されるというものだ。この場合"it"と変数に結果が入る。



では、コード1に戻ろう

-----------------------------------------------------------

aif (hogeCalc() == XXX) {

     処理

}

-----------------------------------------------------------

コード1


そしてコード1-3は以下のようになる

------------------------------------------------------------

aif (hogeCalc() == XXX) {

     処理

}

//後で追加した処理

result = it + 100

result2 = it - 100


------------------------------------------------------------

コード1-3


説明しやすいようにC言語(Java C# でも同じ)のように書いているが、残念ながらこれら現在主流となっている言語ではこのようなことは出来ない。主にLisp等の関数型言語(Perlでも出来そうだけど、未確認)以外では実現困難だ。


別にif文だけでなく、特殊なループ文を自分で作成することが出来る。例えばLispにはCで言うところのfor文がないので、自前でfor文を用意することになる。つまり、言語自体はいかようにも拡張可能な言語ということだ。


これ読んでLispはじめる人が居たら良いなぁ。いないと思うけど。





-----------------------------------------------------------
最新コメント
QRコード
QRコード
  • ライブドアブログ