2013年8月28日水曜日

OCamlとRubyで同じプログラムを書いてみた

以前書いたとおり、OCamlRubyで簡単な家計簿プログラムmohを作った。全く同じというわけではなく、後に作ったRuby版のほうが機能を追加したりしているけど、面白い経験だったので感想を書いてみる。

客観的な比較


サイズを行数で比較すると、OCaml版は279、Ruby版は360で、Ruby版の方が機能が増えていることを考えるとほぼ同じくらいといえると思う。ただし、Rubyはユニットテストが作成に不可欠だったので、それを加えると499行とOCaml版に比べて倍くらいになる。まあユニットテストはOCamlでもした方がいいのだろうが、あとで述べる通り、しなくても動いてしまったのでユニットテストはしていない。いずれにしても、どちらも同じくらいコンパクトな言語だといえると思う。

OCamlのいいところ


これからは主観の入った比較となる。

よくOCamlは「コンパイルが通ればバグはないと言わしめる…」という枕詞が付く。もちろん、それは誇張だが、という但し書き付きで。しかし、mohを作ったときは本当にそうで、型検査が通った後のメインのロジックでバグは出なかった。悩まされたバグの原因はすべて正規表現だった。一方で、Rubyで書いたときは何度か型エラーに悩まされた。ユニットテストを書いてはいたが、型エラーが最後の段階、つまり誤ったメソッドが呼ばれるところまで分からないのでデバッグが難しかった。一方で、OCamlの型チェッカは型エラーの原因の比較的近くでエラーを検出するので型エラーではほとんど悩まされなかった。

よくユニットテストをするから型検査は不要、という議論があるけれどそれが間違いであることがわかると思う。一方で、ちゃんとした型システムをもった言語(JavaとかC++ではなく)を使うと実際に信頼性と生産性の向上に役に立つこともわかると思う。mohの開発では結局ユニットテストはしていない。(した方がいいことはわかっている。)

Rubyのいいところ

正直あまり感じなかった…ただ、オブジェクト指向というのは良い点かもしれない。OCaml版のmohでは内部のデータ構造にListを使っているのだけど、これをコードにベタに書いてしまっているのであとで変え難いというにはある。もちろんOCamlでもモジュールに隠蔽すれば簡単に変えられるようになるけれど、モジュールは若干構文が重い感じがする。全体的にOCamlは構文が重い気がする。

あと、正規表現の扱い。OCamlでは正規表現で何度も悩まされた。文字列リテラルでバックスラッシュ「\」をエスケープしなくてはいけないので、正規表現だと2重にエスケープしなくてはいけないことが多く、何がなんだかわからなくなる。これはStrモジュールを使った(Pcreではなく)せいもあるのかもしれないけど。

あと、CSVを解析するモジュールとか、ライブラリはやはり豊富でよく考えられていると思った。

まあまとめると、OCamlは中身、Rubyは見た目とHypeだなあと思いました。

2013年8月27日火曜日

お財布をプレーンテキストで管理する その2

mohとは?


moh (MOney Handler)はコマンドラインベースのシンプルな家計簿ソフトウェア。シンプルと言っても、複式簿記になんとなくは基づいているつもり。私は簿記は習ったことがないので、あまりあてにはならないけど。以前OCamlで書いた時のエントリはこちら。今回はRubyで書きなおして、家計簿の記法を変えたり機能を追加したりした。小さいソフトウェアだが、だいたい同じものをRubyとOCamlで書いてみて色々面白かったので、そのこともいずれ書きたいと思うが、とりあえず今回は使い方だけ。

インストール


まずRubyをインストールする必要がある。Macの人は、付属のruby 1.8.7を使えば良いので何もしなくて良い。

次にwhen_exeをインストールする
\$ sudo gem install when_exe

あとはhttps://github.com/yoriyuki/moh/tree/0.0.1からmoh.rbをダウンロードし、適当なところに置くだけ。家計簿データの置いてあるディレクトリのトップに置くと便利だと思う。

使い方

家計簿を書く

mohの家計簿はプレーンテキスト。指定したディレクトリの下にあるテキストファイルを全部スキャンして、mohの形式で書かれた行を家計簿データと見なす。例えば、

[2012-11-29]\$ Wallet  Expense 7-11 convenience store 700

と書くと、WalletからExpenseへ700円移動した(使った)ことになる。7-11 convenience storeはコメント。

口座は階層化することができる。

[2012-11-29]\$ Wallet  Expense:Lunch 7-11 convenience store 700

今、口座に何円あるか分かっていれば、それを記述できる。

[2013-08-01]\$= Wallet 6000

これで財布に6000円あることになる。もしこれがこれまでの集計結果と違えば、mohは自動的に使途不明金または不明な収入をこの時点に挿入する。

レポートの生成


\$ ruby moh.rb -d XXX -s Wallet 20130101 20131231

とすると、XXXディレクトリの下にある*.howmファイルを読み込んで、口座Walletの2013年1月1日から2013年12月31日までの取引に関するサマリーを生成する。-dを省略するとカレントディレクトリが仮定される。-sでサマリーを生成するが、-tとすると条件に対応する取引がすべて表示される。

--howm_suffix=[SFX]で読み込むファイルのサフィックスをSFXに変更できる。

\$ ruby moh.rb -d XXX --howm_suffix=txt -s Wallet 20120101 20121231

個人的な理由から、三井住友銀行と三井住友VISAが生成するcsvファイルを読み込むことができる。
\$ ruby moh.rb --smbc_dir=XXX -s -t Wallet 20120101 20121231
\$ ruby moh.rb --smbc_visa_dir XXX -s Wallet 20120101 20121231

便利にお使いいただければ幸い。