2009-09-13

"Beautiful Code" Brian Kernighan, Jon Bentley, まつもとゆきひろ 他著

冒頭には、竹内郁雄氏の「推薦のことば」が記される。
「...プログラムコードには、およそ人が「書く」もののエッセンスのほとんどが詰まっている。よい問題解決に始まり、設計、製造、検査、保守改良に至るソフトウェアのライフサイクルをきちんと制御する能力の大半は、実は文章の力であり、文章の力はよいコードを書く力とほぼ等価である。つまり、「美しいコード」を書けるということは、たとえ、コードを書くチャンスがなくても「美しいソフトウェア」を開発できるということなのだ。...」
アル中ハイマーは、このフレーズにいちころなのだ。本書には、33ものプログラマによるエッセンスが熱く語られる。そこには、一流と言われる技術者たちの哲学や美学が現れる。そして、プログラムを書くということは、単に記号を打ち込むというものを超越した世界があることを教えてくれる。
ところで、プログラムを書くことと文章を書くことが等価であるならば、ごちゃごちゃな文章を書くアル中ハイマーは、いつもスパゲティコードを書いていることになる。ちょっと落ち込むなぁ!それにもめげず、今宵も酔っ払った文章を綴るとしよう。ちなみに、おいらはソフト屋ではない。電子回路に実装するためのアルゴリズムやアーキテクチャを設計するハード屋である。それでも、検証モデルを構築するためにプログラムを書く。また、回路設計ではハードウェア記述言語を用いるので、本書がまるっきし別世界というわけではない。むかーし、組込み系のソフトを作っていた時代もある。リアルタイムOSとまではいかないが、リアルタイムモニタ程度の規模でOSもどきを作っていた。はたしてその実体はハード屋か?ソフト屋か?いや、雑用係だ!したがって、アル中ハイマーは、ハードボイルドをモットーに、ソフトなピロートークを持ち味に生きるのであった。

コンピュータの分野が客観の領域にあるのに対して、「美しさ」という感性は主観の領域にある。プログラムは正確に動作しないと全く意味をなさない。正しいか間違っているかは客観の領域にある。ここに、ソフトウェアの本質は主観と客観の双方の領域をまたぐことになる。ポール・グレアム氏の著書「ハッカーと画家」では、プログラマの芸術的感覚に迫っていた。客観に固執したところで所詮は人間のやることであり、主観を無視することもできまい。いや、むしろ主観の領域にこそ、技術者の哲学や美学が顕になる。こうした感覚は経験則から培われるところが大きい。自らの失敗を振り返り、より効率性を求めた結果、技術者の細かいこだわりが現れ、それが「美しさ」への探求へと進化する。こうした過程は、技術者の生き方を物語っていると言ってもいいだろう。
プログラムに求める美しさには、外観の美しさ、インターフェースの美しさ、構造上の美しさ、数学的単純さ、アルゴリズムのエレガントさなどがあり、その視点は多様である。ただ、共通して言えることは、ある機能をソフトウェアで実現するということである。つまり、実現性を前提とした芸術性の探求である。
プログラムに現れる効率性や移植性や柔軟性といったものには、芸術家を思わせるものがある。すべての要素が調和した時にのみ、信頼性と美が融合した時にのみ、コード作成者に対して芸術家に対するのと同様の敬意が表される。それがシンプルで長期間に渡って恩恵を与えているものであれば尚更。しばしば、入門書に登場する決まり文句も、発案者への敬意として使われる。単純な数学的考察であっても、ユークリッドは永遠に崇められるであろう。
美しいプログラムを書きたければ、美しいプログラムを見ることだとは、よく言われる。確かに、優れたプログラマのコードを眺めるだけでも勉強になる。優雅さと経済性を見せ付けられれば、そこには美しいコードとなる要素があるはず。科学の美的感覚は単純さを求めるが、プログラムにも同様の感覚がある。ただ、コンパクト過ぎると逆に読み辛い場合もあれば、難しいテクニックで記述を短縮して理解し辛い場合もある。コードが処理速度に制約を受けることもあり、自由に書けるとは限らない。プログラマの腕の見せ所は、コンパクト性とメンテナンス性のバランスの按配であろう。

データ構造の抽象化によって一般化する様式は、美的感覚を共有することができる。非連続性のデータ構造に対して強力なイテレータが登場すれば、一般化したコードが書きやすくなる。これも外見の美しさである。美しさを感じないが、どうしても避けられない機構もある。例えば、正規表現は文字列操作には欠かせない。これは非常に便利な機構であるが、方言があるのも事実だ。正規表現でしばしばイライラさせられるのは、そこに暗号めいた記法があるからである。それでも、酔っ払いには決まったいくつかの正規表現だけで、ほとんど事足りる。
また、美しさというよりも、むしろ一貫性と捉えるべきものがある。unix的な慣習で見られるようなドライバモデルには、その外見に一貫性が見られる。システムコールが提供するopen/read/write/closeパラダイムは、入出力装置の性能を最適化してバッファリング効果を助ける。そして、アプリケーション側でタイミングを制御するflush操作が実装される。
プログラミング言語の選択も、作業効率を求める手段となろう。連想記憶といった機構を使いたければ、それを実装した言語を使いたい。RubyやPythonのような動的な言語には、連想記憶を定義する構文が用意されている。テキストの行に、正規表現を当てはめたり構文を当てはめたりする思想は、awkのような言語から受け継がれる。ネットワークのプログラムでは、CレベルのAPIを使うところに鬱陶しさがある。また、しばしばOS間での互換性の問題も発生する。それでも、フレームワークや言語仕様などで、低レベルのAPIをカプセル化し、使いやすくはなっているのだろう。最近のことはよく知らんが。
こうしたものをすべて「美しい」という言葉で括れるのかどうかは、判断の難しいところである。ただ、技術者に思想や哲学の方向性を示してくれるのは、共通認識を与える効果がある。

経験上、一度やった仕事は、もう一度やればもっとスマートにやれると、必ず反省する。だが、同じような仕事を繰り返すことは滅多にない。反省も再利用と移植性に富んだものにしたいものだ。おいらの場合、仕事の美しさを上流工程に求めるところがある。上流工程の重要性は、かつて所属した企業で伝統的に先輩から叩き込まれたような気がする。おかげで、早い段階から仕様の疑問に飛びつく癖がある。システム構成の変更は、後の日程に大きな影響を与えるからである。事前検討がしっかりなされた仕様は、なによりも日程を正確に見積もれる。日程で一番厳しい状況にあるのは検証期間であろう。検証効率を上げるためにも上流工程は重要である。
また、プロジェクトには、哲学的意識をメンバーで共有するのも重要であろう。哲学的意識はプログラムの書き方にも影響を与える。手段であるプログラミング言語の選択も難しい問題である。より効果的な言語が存在するはずだが、スキルによって制限される。ちなみに、最近遊びで書くコードはRubyばかり。酔っ払いには、動的な記憶領域の管理などランタイム機構に任せるのが身のためである。そこで、スクリプト言語を多用することになる。とはいっても、だんだん保守的になって手段もワンパターン化してきた。新しいことも遊びでしか試さない。そんな時、「もう歳だねぇ!」と声をかけられると過剰に反応するので、周りはおもしろがる。もともとはデータの型が明確でない言語を嫌う傾向にあったが、だんがん面倒になってきた。Ruby開発者のまつもとゆきひろ氏によると、型も大切だが言語の本質ではないと語ってくれるのは心強い。

本書は、いろいろな専門分野における主観的観点が覗けるのがおもしろい。それほど美しいと感じないものもあり、感覚の違いが体感できる。また、プログラムに対する謙虚さの大切さを教えてくれる。クヌース先生は、2分探索が公表されてからバグのないコードが公表されるのに12年以上経っていると指摘したという。もっと驚くべきは、何千回も実装され作り変えられてきたベントリーの公式の証明済みアルゴリズムでさえ、配列が大きくアルゴリズムが固定小数点演算を使用した言語で実装されているときに出現する問題があるという。ちょっとおもしろいエピソードでは、スティーブン・レビーの歴史古典「ハッカーズ」にビル・ゴスパーの「データはバカバカしい種類のプログラムである」というのがあるらしい。逆に言うと、「コードはスマートな種類のデータである。」というのが導かれるわけだが、このことから、チャールズ・ペゾルド氏は、次のように語る。
「プログラムとは、CPUが何か役立ったり面白いことをする引き金となるようなデータというわけです。」

さて、あまりにも多くの中から、ちょっと興味を持ったものを摘んでおこう。

1. クィックソート
「私が書いたことのある、一番美しいコード」として紹介されるのがクィックソートである。ソート処理で重要なのは、いかに比較の回数を減らすかであろう。そこで注目すべきは、選択される比較の対象をランダムで選ぶところである。ネット検索など、ランダム性を利用したアルゴリズムで処理の効率化を図るシステムは多い。完璧な検索結果を時間をかけて得るよりも、だいたい正しいだろうとする結果を高速で得られる方が有用な場面では、確率論に持ち込むアルゴリズムが有効となる。
本書は、その実行時間の分析を、ソートプログラムを使って計測しているのは分かりやすい。ゲーテの言葉「建築は凍りついた音楽だ」をもじって、ジョン・ベントリー氏は、「データ構造は凍りついたアルゴリズムだ」と語っている。そして、クィックソートのアルゴリズムが凍りついたら、そのデータ構造は2分木になると。なるほど、クィックソートのデータは、2分探索木のような構造になる。クヌース先生も、2分木構造の処理時間はクィックソートと類似した漸化式になると言ったという。

2. BioPerl
BioPerlは、生物情報学向けのラピッド開発ツールキットだという。これはDNAとタンパク質の解析、系統木の建築と解析、遺伝子データの解釈、ゲノム配列の解析などのモジュールを提供するのだそうな。本書は、Bio::Graphicsの例を使って、そこで使われるオブジェクトクラスを紹介している。ただ、これがPerlで書かれていることに驚いた。暗号っぽい言語は、遺伝子暗号を解読できる道具となっているというわけか。簡単にカスタマイズや拡張される様子を眺めていると、つい読みいってしまう。Perlは、あまり好きな言語ではないのだが、時々要求されるので仕方なく使う。

3. 遺伝子ソータ
遺伝子ソータはCGIスクリプトで、web上でユーザとの対話管理で使われるという。CGIスクリプトは提供する側のマシンで動く言語ならなんでもいいわけだが、これはC言語で書かれているらしい。少々大きめのプログラムで構造上の美しさを語っているが、要するにオブジェクト指向設計になっている。

4. ガウス消去法(LU分解)
コンピュータの技術革新で、アーキテクチャの変化に応じて、アルゴリズムも変化する場合がある。その例として、連立一次方程式を解くためのガウス消去法を紹介している。これは線形代数では欠かせない行列に対する操作アルゴリズムである。LU分解は、数値演算言語で実装されている関数なので、おいらは無条件に利用している。線形代数のカーネルであるBLASやLAPACKは、気まぐれで中身を覗くこともあるが、何も知らずに使う方が幸せである。
ベクトルマシンが登場すれば、行列アルゴリズムをベクトル化することに、一層の意味があるだろう。マルチコア化が進めば、並列アルゴリズムが有効となる。マルチスレッドによってブロック分割アルゴリズムを、ベクトルや行列レベルの演算で高度にチューニングすることもできる。

5. MapReduce
MapReduceは自動的に並列実行されるもので、gさんが開発した。実行時にシステムが入力データの分割を担い、一群のプログラム実行をスケジュールし、マシン間の通信を管理する。つまり、並列分散システムの経験がない技術者でも、大規模な分散システムの資源を容易に利用できるというわけだ。これは、並列処理の抽象化とでも言おうか。MapとReduceに分離して並列処理する仕掛けは、Mapでレコード毎の処理を定義し、Reduceで繰り返し処理を定義し、それぞれMapとReduceのドライバが管理するといった具合。この分離というか抽象化をうまいことやれば、かなり精度のよい並列処理が実行できるようだ。これはなんとなく凄い!

6. Schemeのsyntax-case
コードとデータは、通常はっきりと区別される。ただ、コンパイラから見れば、コードも単なるデータである。合理的に眺めれば、コードとデータはバイト列に過ぎない。こうした感覚は、lispの哲学が内包されているように思える。プログラムとデータが同じ形式というか、プログラムにはそもそも構文らしきものがないというか。lispには、そうしたなんとなく謎めいたものを感じるからである。構文の抽象化という意味で、Lispには昔から興味を持っているのだが、いまだに手を出せないでいる。

7. Emacspeak
emacsがただのエディタではないことは分かる。メールを読んだり、webを閲覧したり、シェルコマンドを実行したり、多様な用途のプラットフォームもどきになっている。周りには、マウスに頼った操作が嫌いな人で、emacsを愛用する人も多い。emacs-lispを実行できるところに、即テストできるという馴染みもある。
本書は、音声デスクトップ環境としての、Emacspeakを紹介している。完全な音声のみのデスクトップ環境というところに、emacsの可能性を垣間見る思いである。そこには、障害者向けの環境構築といった可能性がある。

0 コメント:

コメントを投稿