52nd MIDI研とプロ研のべるです。今年は主にMIDI研として企画などでは活動してました。皆様いかがお過ごしでしょうか。寒いと体調を崩しやすくて嫌ですね。
これを見ているのはおそらく、僕のことを知っている人か、いいとこOBやOG、後輩にあたる人だと思います。そんな人に向けて、僕が趣味でやっているLispの勉強の進捗を書いていきたいと思います。日ごろの進捗が止まっていたり、プログラミングをはじめてはみたものの、なんか面白いものないかなーと探している人には「新しいプログラミング言語のお勉強のススメ」的な感じの参考になれば幸いです。楽しいですよ。
1.出会い
出会いは今年の2月にまでさかのぼります。 その日、僕はとあるきっかけでもらった図書カードを使ってなにか技術書を買って読もうかなと思っていました。そのころ僕は推薦生としての課題もそこそこに、DQⅩと自動車教習所の両立をしていました。先輩になんか誘われて第38回Haskellもくもく会に行った後だったので、Haskellのモチベが高まっており、Real World Haskellを買おうかなーと思ってました。部活で先輩に勧められ、すごいH本、プログラミングHaskell、ふつけるをふんわりと読んだだけの僕でしたが、高3で妙に勉強に忙しく久方ぶりのプログラミングでした。そして、気が付いたらLand of Lispを買っていたのです。。2.なにこれおもしろい
そう、とにかく面白いんです。棚の前に積んであったその本に、引き寄せられるような感覚でした。なんか、モチベがむくむくわいてくるような感覚なんです。大抵の技術書って、なんとかを前提とする、とか、淡々とまあつまらなそうに書いてある本が大半で、そこで挫折やモチベが下がった人も多いと思いますが、この本は違います。「こんなことできるんだよ、面白いでしょ!」って言う雰囲気、大事だと思うんですよね。
あと絵がポップで面白いです。好き。
是非皆さんも手に取ってみてみてください。
3.やったこと
a.Land of Lisp
略称LoL。2月に買ったはいいものの、久しぶりだったので腰を据えてやりたくて、すべてのコードを写経しながら読んでたら読み切ったのは8月末でした。めっちゃかかった。。なんか2週間で読み終わったとか言う記事もぽろぽろ見るのでまあ多分読むだけなら、あと頭の強い人ならそのくらいなのかなと思いました。
写経したコードは僕のGithubにあげてあります。一応公式のページにもソースコードはあるのですが版が古いやつだったり細かいところが違っていたり、なにより自分で手を動かしたほうが身につくので。 6月のプロ研発表会ではこの本の9章のorc battleを発表しましたが、1年生であることに甘えて散々な発表になってしまいましたね。。苦い思い出だ。コマンドプロンプトでゲームを動かそうと思ってる皆さんはぜひせめて文字は見えるくらいにすべきだと思います。(戒め) まあ、C++の時はいまいち理解できなかったオブジェクト指向の理解が深まったので本当にすごい本だと思います。 Graphvizやhtmlに出力するときの書式などLispじゃないところで散々悩まされてましたね。。ほかの知識がなさ過ぎて辛い。二度それで挫折しかけましたからね。。まあなんとかやりきりましたが。現在はモチベが無くなってきたときに読んでモチベの材料にしています。あと本の誤植も見つけました。
b.実践Common Lisp(途中
夏休み~夏休み明けくらいまでちびちび学読で借りて読んでたんですが、あんま進みませんでした。。やっぱ本は買わないと読まないですね。手を動かさないとグダるんですが、動かし過ぎると進みが遅かったり。この本は理論系が多いのでそこでペースダウンした印象です。冬休みの宿題、ですかね。
c.渋谷Lisp
lisp.connpass.com これに行ってきました。正確には当日の16時くらいにすとまとさんの雑な空リプで急に行くことになったんですが。会場はLisp始めたてみたいな人からベテランまで揃ってて、まあ刺激的でした。なんか最初のプレゼンターの人がMIS.Wの先輩らしくて、印象としては90分もよく話せるな、強いな、全然わかんねえという感じでした(全体のプレゼン時間は2時間ほど)。皆さんって90分のプレゼンとか聞いたことあります?しかもgeekなやつ。質疑応答は言語仕様についての超マニアックな談義が交わされていました。もうちょっと強くなってから行くべきでしたね。。予定が急だったので懇親会にも参加できませんでした。この頃ちょうどLand of Lispを読み終わったんですよね。懐かしい。
d.lispstg(途中
stg-commonlisp
これを夏休み明けくらいにちびちびと進めていた記憶があります。というのも、なんかわかんないエラーを吐くようになり、資料に細かい間違いやざっくりてきとうに書いているところに理解を割いていたら、いつの間にかモチベが0に収束しました。もうちょっと余裕ができたら再開したいです。
e.On Lisp(途中
まあ小さいけど厚いんで2週間じゃ無理でしたねって思ったらネット上に翻訳版が上がっているという。じゃあ借りなくていいじゃん!って思ったら読まないっていう。 でも一つ感銘を受けたところがあるのでそこだけ引用します。
命令的プログラミングの裏返し
関数的プログラミングの狙いは, もっと普通のアプローチ,命令的プログラミングの狙いと対比させると はっきり見えてくるかもしれない. 関数的プログラムは,それが欲しがるものを求める. 命令的プログラムは,何をすべきかの指示を求める. 関数的プログラムの 「aと,xの第1要素の2乗から成るリストを返せ.」:(defun fun (x) (list 'a (expt (car x) 2)))
ざっくり解説
defun : 関数定義
fun : 関数名
list : いくつかのものをとってリストを返す関数
'a : シンボル(気にしなくてOK
expt x n : xのn乗を返す関数
car x : xというリストの先頭要素を返す。xが空リストの場合はnil
どうです?簡単でしょ?
命令的プログラミングではこうだ. 「xの第1要素を求め,それを2乗せよ. そしてaと,先程2乗した値から成るリストを返せ.」:
(defun imp (x) (let (y sqr) (setq y (car x)) (setq sqr (expt y 2)) (list 'a sqr)))
ざっくり解説2
imp : 関数名
let : ローカル変数宣言
setq a b : aにbを(破壊的)代入
読める、読めるぞ!!
このプログラムを両方の方法で書けるLispユーザは幸運だ. 命令的プログラミングだけに適したプログラミング言語もある ---Basicと,ほとんどのマシン語だ. 実際,impの定義はほとんどのLispコンパイラがfunに対して 生成するマシン語と構成が似ている.
コンパイラが代わりにやってくれるのに,どうしてそんなコードを書くのだろう? 多くのプログラマには,この疑問は浮かびすらしない. プログラミング言語はそのパターンを私達の考えに焼き付ける: 命令的プログラミングに慣れてしまった人は, プログラムを命令的プログラミング用語で考えるようになってしまった結果, 実際に関数的プログラミングより命令的プログラミングの方が易しく思えるのかもしれない. この精神的習慣は,乗り越える価値のあるものだ ---そうさせてくれるプログラミング言語を持っているなら.
他のプログラミング言語の卒業生にとっては, Lispを使い始めるのは始めてスケートリンクの上に立つのと似ているかもしれない. 氷の上で動き回るのは陸上で動き回るのより実際はずっと簡単だ ---スケート靴を使えば. そうするまではこのスポーツが何だというのかと一人ぽつんと悩むことになるだろう.
スケートと氷の関係は,関数的プログラミングとLispとの関係と同じだ. それらを組み合わせれば,優雅に,しかも苦労せずにあちこち回れるようになる. しかし別の種類の移動方法に慣れてしまっていると,始めは何やら感じが掴めないだろう. 第二言語としてLispを学ぶときにはっきり理解しにくい点の一つは, 関数的プログラミングのスタイルを学ぶことだろう.
幸運なことに,命令的プログラムを関数的プログラムに変換するうまい方法がある. この方法は完成したコードに適用することから始めるといい. すぐに勘が働くようになり,コードを書きながら変換できるようになるだろう. そうすればあっという間に, 始めから関数的プログラミングの用語でプログラムを考えるようになるだろう.
その方法は,命令的プログラムは関数的プログラムを裏返しにしたものと思うことだ. 関数的プログラムが命令的プログラムの中に隠れているのを見つけるには, ただ裏返しにすればいい. この方法をimpで試してみよう.
最初に気付くのは,先頭のlet内でyとsqrを作っていることだ. これはよくないことが続く前触れだ. 実行時のevalの呼び出しと同様, 初期化されていない変数は滅多に使われないので, 一般的に言ってプログラム内で変なことをした現れと見なしていい. そういった変数はしばしばプログラムを留める画鋲のようなもので, プログラムが自然な形になろうとするのを妨げている.
しかしここではそれは暫く無視し,関数の終わりまでまっすぐ進んでみる. 命令的プログラムで最後に起きることは,関数的プログラムでは一番最初に起きる. だから最初の一歩は最後に行われるlistの呼び出しを掴み, プログラムの残りをその中に詰め込むことだ ---シャツを裏返しにするように. そしてシャツを袖からカフスにかけて次第に裏返していくように, 同じ変換方法を繰り返し適用し続けていく.
後ろから見ていって,sqrを(expt y 2)で置き換え
(list 'a (expt y 2)))
が得られる. 次にyを(car x)で置き換えると
(list 'a (expt (car x) 2))
となる. こうしてコードの残りは最後の式の中に詰め込まれたので, それを捨て去ることができるようになった. ここに至るまでに変数yとsqrの必要性はなくなったので, letも削ることができる.
最終結果は始めのものより短く,理解も容易だ. 元のコードでは最後に(list 'a sqr)という式が現れたが, これではsqrの値がどこから来るのかすぐには明らかにならなかった. 今は返り値の元になるものは道路地図のように目の前に広がっている.
この章での例は短いものだったが,この方法のスケールは次第に拡大する. 実際,大規模な関数に適用すればそれだけ価値が出てくる. 副作用を起こす関数でさえ,副作用を起こさないパーツへときれいに分解できる.
めっちゃエレガントですよね。まあ数少ない例ではありますが、命令型のプログラムがこんなにもわかりやすく関数型に書き換えられるなんて。まあ要は最後から見てってそこを返り値にして、遡ってくって感じなんですが。
再帰のプログラム自体が帰納法の逆みたいな考え方(つまり、まず最後の終了条件を考えて、次にnがなんとかの時n-1の状態はどうなるか、みたいな)をするので、まあ当然なんですけど。
これで皆さんのLisp入門は完了です。いえーい。
おまけ.絶対カッコ感
突然ですが皆さんは次の閉じ括弧が何個連続かわかりますか??
)))))))))
まあ9個ですよね。瞬時にわかります。そう、この能力こそが、Lispを身につけるとつく能力なのです。すごいでしょ。
あとカッコの対応に敏感になります。 例えば、4クラの微積の参考書で見つけたカッコの誤植をツイートしたら意外に反響がありました。
参考:
まあパッと見で違和感ありますよね。ない?
4.まとめ
どうでしょう。On Lispを読み飛ばした人は瞬時に読み終わったんじゃないかなーと思います。途中ばっかでほんと僕の中途半端さが現れてますね。需要があったら、Lisp入門講座でも開きたいなと思ってはいるので気軽に言ってください。今年はMIDI始めたてだったのでこんなもんですが、来年はもうちょいこの二足の草鞋を磨いていきたいと思います。と言うかアルティメットなむさしんさん見てるとそんな言い訳していられませんね、はい。新しい言語を学ぶのは楽しいですよ。オススメはLispです。
明日はtedさんの記事です。飲み会ではハイテンションで仮想通貨の話ばかりが聞こえてきました。どんな記事なのか楽しみですね。