Lisp

さらばxyzzy

ブログを過去から見ていた人は知っていると思うが、僕はxyzzyユーザーだ。xyzzyを愛しているというのを書いてからもう10年だ。これは本当に素晴らしいエディタだった。今までemacsではなくxyzzyを使ってきたのはいくつか理由がある

・軽い
・小さい
・windowsのソフトである
・common Lispが使える

逆にemacsを使ってこなかったのは

・重い
・でかい
・基本的にwindowsソフトではない
・emacs Lispが嫌


ということだ。だが、結局xyzzyに未来はないという結論になった。xyzzyは数年前からメンテされなくなった。が、まだまだネット上には同志が居て、拡張lispなど更新されていた。ことの発端は去年働いた会社でフリーソフトとしてxyzzyを申請したとき「もう全然メンテされてないよね」と言われて却下されそうになったことだ。言い方は悪いが怪しいソフトとなり下がってしまっていた。そして今年あたりからだと思うが、ネット上から情報がどんどん消え始めた。そういう経緯もあって今年末からemacsを使い始めた。

ここ10年の僕の仕事は常にxyzzyと一緒だった。仕事でもlispでマクロが組めるので、非常に役立った。まだマシンパワーが弱い時代にあの軽さはすごく魅力的だった。このエディタが無かったらもっとレベルの低い仕事をしていたかもしれない。たいして力はないが、自分のハンドルをlisperとつけたのもxyzzyあってこそだった。いろいろと思い出深いが、今回お別れしてemacsに移行することにした。結構寂しいが、そもそも仕事で使うことを許可されなくなるかもしれないので仕方がない。

僕はフリーソフトの作者に別に感謝したことはない。だが、xyzzyを作った亀井哲弥さんだけは別で、ソフトに感謝というよりもエンジニア人生を豊かにしてくれたという意味で、感謝している。

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コード
  • ライブドアブログ