2009-10-11

"プログラミング言語 Ruby" David Flanagan & まつもとゆきひろ 著

ここ数年、遊びで書くコードはほとんどRubyである。毎日遊んでいるようなもんだが。というのも仕事では認可してくれない。管理ができないからという理由らしい。では、C++ならば管理ができるのか?正確にはC++コンパイラさえ通ればいいようだ。
ところで、遊びとはいえ使う言語の解説書を一冊も持たないことがよくある。試してみてから気に入ったら、その解説書を買うといったところだろうか。これも、安易に誤魔化すことができるネット社会の恩恵であり、惚れっぽい酔っ払いにはありがたい。ということで、Rubyの解説書を手にするのは初体験である。初体験という言葉はなんとなくドキドキさせる。これも若さというものか。

Rubyは、全般的に型の定義が緩やかである。いわゆる、動的言語で見られる「ダックタイピング」という特徴がある。ちなみに、この言葉は「あひるのように歩き、あひるのようにクワっと鳴けば、それはあひるに違いない。」という諺からきているそうな。個人的には型チェックの曖昧さを好まないのだが、あまり厳格過ぎるのも鬱陶しい。そこで、Rubyは、数値リテラルなど厳格に型チェックされる場合もあるので、その按配が肌に合うようだ。
Rubyは純度の高いオブジェクト指向言語として世界でも評判が高い。その普及を後押ししたのがRuby on Railsだと言われている。日本人が開発した言語が世界を席巻するというのもなんとなく嬉しい。文法的にもLisp, Smalltalk, Perlの影響を受けつつも、CやJavaのプログラマでも親しみやすいという特徴がある。特に、procやlambdaといったメソッド思想によって関数プログラミング風に書けるのがいい。ちなみに、lambdaは数理論理学などで見られるラムダ計算にちなんだ名前。
ところで、関数プログラミングといえば、Lispを思い浮かべる。ポール・グレアム氏は著書「ハッカーと画家」で、Lispを学べば実際にLispプログラムを作ることがなくても良いプログラマになれると熱く語っていた。現代のプログラム構造は、動作部からデータ構造へと注目点が移ってきた。Lispにはデータ構造の本質を覗かせてくれる何かがあるのだろうか?と、なんとなく興味を持っているが、いまだに手が出せないでいる。Rubyのおかげで、ずーっと避けていたLispにも手を出す日が近づいた予感がする。来年の目標はschemeあたりか?Lispは古い言語で、時代を前後した奇妙な順を追っているようでもあるが、そこは酔っ払いらしく千鳥足で言語選びの旅に出かけるのも悪くない。

本書は、改めてRubyの持つ思想や慣習といったもの味あわせてくれる。そして、かなり慣習を無視した書き方をしていることに気づかされる。やはり言語の勉強はいい加減にできるものではない。おいらの勉強は、いつも勘で始まって勘違いへと変化する。まったく学習能力がなく、いつも同じ轍を踏むわけだが、なぜか?気持ちええ。アル中ハイマー病とはそうした病である。
本書は言語リファレンスではない。それでも重要なクラスやモジュールを押さえている。Rubyの特徴は、豊かなAPIを備えていることであろう。特に、String, Array, Hash, Enumerable, IOといった主要クラスは押さえておきたい。また、数値と数学関係のNumericメソッドやMathモジュールも紹介してくれる。
Rubyのコレクションクラスには、ArrayやHashやSet(集合)があるが、いずれもEnumerableモジュールがミックスインされる。ちなみに、Enumerableオブジェクトは対象のオブジェクトを数えたり列挙したりできる。
ネットワーク機能も豊富だ。インターネットのクライアント側にはTCPSocketクラスが、サーバ側にはTCPServerクラスが、UDPデータグラムにはUDPSocketクラスが提供される。ソケットライブラリは、http, smtp, pop, imapといったプロトコルに対応している。Kernel.openは、File.openと同じような動作をするが、URLをファイルのように扱える柔軟性がある。仕事でWebアプリを作ることはないが、これを機会にRailsで遊んでみるのもいいかもしれない。
マルチスレッドも書きやすそうだ。ただ、プラットフォームに依存するのは仕方が無いか。スレッドの優先順位はOSによってまちまちである。だが、Linux上のRuby1.9はネイティブスレッドを実装していて、スレッドの優先順位の設定もRuby側で設定できるという。スレッドが一定の条件で自動で切り替わるのは多くの場合でありがたい。スレッドはキューで管理できる。

コンピュータ構造が、人間の思考方法や社会構造からヒントを得ることが往々にある。オブジェクトは、プラトンのイデア論に通ずるものを感じる。クラスという雛形からインスタンスを生成するあたりは、いわば実存認識といったところだろうか。そして、スーパークラスという基底なるものが理想イデアであって、それを継承しながら様々なクラスがイデアとして現れる。継承が複雑化して手に負えなくなると、遺伝子コピーの不完全性も現れる。ところで、オブジェクト指向という言葉が流行ったのは20年ぐらい前であろうか。Smalltalkのセミナーにも参加した記憶が蘇る。当時マイコン開発をしていた。いわゆる組込み系のプログラム設計である。そして、データ隠蔽の思想を好み、ローカルルールで実践していた。システムの規模は小さく、CPUで言うと8bitが主流で、まだ4bitも多く、稀に16bitを使うといった具合である。プログラムサイズで言うと、16KBから32KB程度でリアルタイムモニタといったところだろうか。今でこそリアルタイムOSが盛んであるが、当時は高価で手が出せかった。小規模なシステムでは助長したプログラムは実装できないので、OSもどきのカスタムプログラムを作る必要があった。使用する言語はCライクのものも登場していたが、コンパイラの性能から依然アセンブラが勢力を保っていた。小規模なシステムでは、あまり高級言語の恩恵が受けられないので、独自ルールで工夫することになる。アセンブラ言語のデータ構造はほとんど制約がないので、あらゆるアクセスを想定しなければならない。そこで、グローバルデータを思想から排除し、すべてメソッド経由でしかアクセスを認めないなどの規定をマクロ機能を駆使しながら実装する。データアクセスで必要なメソッドは、基本的にset/get系のみで集約できるはず。当時は、カプセル化の概念といった学術的な意義を知らなかったが、自然と実践していたような気がする。また、組込み系システムでは、コンピュータ上で動作する一般のアプリケーションと比べてソフトウェアの構成がハードウェアと対応付けしやすいというメリットがある。これは、ソフトウェア仕様を製品の取扱説明書と結び付けやすく、ソフトウェア部品であるオブジェクトは製品の部品のようなイメージで捉えられ、分かりやすいプログラム構造を実現できる。ちなみに、おいらはソフト屋ではない。どちらかといえばハード屋か?はたしてその実体は...雑用係なのだ。したがって、アル中ハイマーは、ソフトなピロートークを武器にハードボイルドに生きるのであった。

1. オブジェクト
Rubyの値は全てオブジェクトで、他の言語のような原始型なるものがないという。単純な数値やリテラルも普通にオブジェクトであり、それぞれのメソッドが実行できる。ちなみに、true, false, nilもオブジェクトで、Booleanクラスなるものがない。
Rubyは不要になったオブジェクトをガベージコレクションで自動的に解放するので、明示的な解放を必要とする言語に比べて、メモリリークを起こしにくいという。だからといって、メモリリークが絶対に起こらないというわけでもないだろう。やたらと寿命の長い参照を作るコードが危険であるのは同じである。
Rubyは、オブジェクトに対するテストや調査する機能が豊富である。インスタンスがどのクラスに属するかを問い合わせる機能や、クラスの親子関係を調査するのに比較演算子や等値演算子がオブジェクト間でも使える。オブジェクトの初期化ではコンストラクタを使うのがお馴染みであるが、Rubyではinitializeメソッドを使う。クローンやコピーを作らせないために、Singletonを定義することができる。ちなみに、シングルトンとは、インスタンスを一つしかもたないクラス。

2. メソッド
Rubyの構文の基本は式であり、制御文も式として評価できる。これがコンパクトに書ける理由でもある。演算子の多くはメソッドとして実装されており、クラスはメソッドを自由に定義できる。コアライブラリであってもメソッドを追加できる。ちなみに、どのクラスでもオブジェクトを文字列で表現するto_sインスタンスメソッドを定義しておいた方がよいという。デバッグ時に役立つからである。set/getメソッドを実装するのに、attr, attr_accessor, attr_reader, attr_writer(あまり使われん)によって抽象化できるのは便利である。C++のようにpublic, protected, privateを定義できるが、Rubyでの対象はメソッドのみとなる。インスタンス変数やクラス変数は、カプセル化されるからである。initializeメソッドは暗黙でprivateであるが、その他は明示しない限りpublicである。ただし、メタプログラミング機能を使えば、protected, privateメソッドも起動できるようだ。ここが、オープンな言語仕様ということなのだろう。一見矛盾した思想にも思えるが、デバッグ機能として活用できそうだ。

3. リフレクションとメタプログラミング
Rubyは、リフレクションのためのAPIを豊富に揃えている。リフレクションとは、プログラムが自分の状態や構造を解析することである。そして、その状態や構造を書き換えることさえできるという。こうした動的な性質が柔軟性をもたらし、メタプログラミングを可能にする。リフレクションAPIには、オブジェクトの型を判定するメソッドがある。文字列やブロックの評価、変数の取得、設定、テストが容易にできる。また、メソッドのリストアップと有無テストや、言語仕様の中でトレース機能を持つ。

4. セキュリティ
Webアプリでは、SQLインジェクション攻撃などのセキュリティ問題が発生するが、Rubyはこうしたリスクを追跡する手段を提供している。また、信頼されないデータやコードを区別するためのメカニズムが提供され、オブジェクトをフリーズさせたり、オブジェクトの汚染状態を明示することができる。

5. ファイバ
Fiberオブジェクトは、他の環境では一種の軽量スレッドを表すために使われるらしいが、Rubyではコルーチンと呼んだ方がいいという。これは、数値演算で数列のジェネレータなどに使えそうだ。わざわざ裏処理させる必要もないかもしれないが。ファイバは、少々分かりにくい制御構造である。スレッドのように即実行するものではなく、resumeメソッドを呼び出すことによって実行開始する。Fiber.yieldメソッドで呼び出し元へ戻したりと、スレッドを裏で実行させたい時だけ実行させるといったことができる。

6. Ruby1.8とRuby1.9の違い
細かく挙げると切りが無いので、気になったところだけを列挙しよう。
(1) スーパークラスは、1.8ではObject、1.9ではBasciObject。
(2) 1.9からパッケージ管理システムのRubyGemsが標準ライブラリ。
(3) procは、1.8ではlambdaと同じ意味であるが、1.9ではProc.newと同義語だという。1.8では曖昧さがあるのでprocを使うべきではないという。
(4) 1.9ではマルチバイト文字がシームレスに扱える。例えば、文字配列のポインタがバイト単位ではなく文字単位となる。このマルチバイト文字への対応がRuby1,8とRuby1.9の大きな違いのようだ。おいらは、jcodeライブラリあたりを意識するのが嫌いなので、もともとマルチバイトを避ける習慣がある。とはいっても、書籍購入予定リストなど日本語テキストをフィルタリングすると、マルチバイトを避けるわけにはいかない。したがって、エンコーディングクラスが提供されるのはありがたい。
そういえば、6月頃だろうか、Ruby1.8系の1.8.6-p368以前と、1.8.7-p160以前のバージョンで、DoS攻撃を受ける脆弱性があると発表された。BigDecimalオブジェクトからfloatへの変換の時、巨大な数値を扱うと、segmentation faultsを引き起こされる可能性があるといった話である。尚、1.9系は問題はないらしい。

7. 対話的Ruby
irb(Interactive Ruby)は、Rubyのシェル。
$ irb --simple-prompt # Rubyプロンプトを起動する
$ ri "keyword" # ドキュメント表示

0 コメント:

コメントを投稿