調子に乗って昨日の話の続き。
大筋を書いたら、些細な話を書きたくなったので。
主にプログラムの話。
プログラムは、現実に何かを作るという意味で工学系だ。
その意味では、すべてのプログラマは工学系に属している。
一方、プログラムの基になる「アルゴリズム」は、純粋に論理のみの存在で、理系の範疇だ。
アルゴリズムから考案してプログラムを行うプログラマは、理系の範疇でもある、といえる。
すべてのプログラマがアルゴリズムを考案するわけではない。
仕様書にしたがって、実装部分だけを担当するようなプログラマもいる。
#注:実際には、アルゴリズムを考案する人を「プログラマ」、実装を行う人を「コーダー」と呼ぶ。
しかし、現在ではこの2つの仕事を分割して考えることは少ないため、まとめてプログラマと呼ばれる。
たとえば、ソートを行うプログラムを考えるとする。
一番単純なのは、「バブルソート」と呼ばれるアルゴリズムだ。
バブルソートでは、「すでにソートが終了している」リストのなかに、新たなデータを挿入することでソートが行われる。
最初は、ソートが終了しているリストは存在しない。1つ目のデータは、必然的に「1番目」に置かれる。これでも、ソートが完了したリストとなる。
2番目のデータは、1番目のデータの、上か下に置かれるだろう。3番目のデータは、すでに存在する2つのデータの上か、中央か、下に置かれる。
仮に、「データの小さいもの」を1番上に置くことにして、新たなデータは「下から順に」既存データと比較を行うことで、挿入位置を探すものとする。
すると、アルゴリズムは
1. 一番下のデータを読み出す。
2. 読み出したデータと新たなデータと比較する。
3. 読み出したデータのほうが大きな場合、一つ上のデータを読み出し、2に戻る。
4. 読み出したデータのほうが小さな場合、比較したデータの下に新たなデータを置く。
「理系的には」これでよい。
しかし、実装するとなると問題が生じる。
すでにあるリストのどのデータよりも小さかった場合、3 の部分で「一つ上」を読み出すことが出来なくなる。
このような場合、「端の処理」を行わなくてはならない。
データの位置を検討し、1番上ならば終了する、というのは、良く使われる方法だ。
また、4 で「下に新たなデータを置く」と言っているが、これは実装時に問題が生じる。
一つ下にデータを置いてしまうと、それまで存在していたデータが上書きされてしまうのだ。
なので、事前に「下のデータを、順次ずらす」という処理が加わらなくてはならない。
これらを考慮すると、次のようになる。
1. 一番下のデータを読み出す。
2. 読み出したデータと新たなデータと比較する。
3. 読み出したデータのほうが大きな場合、
3a 読み出したデータが一番上なら、すべてのデータを順次ずらし、一番上に新たなデータを置き、終了。
3b 一番上でないなら、1つ上のデータを読み出し、2に戻る。
4. 3 で読み出したデータのほうが小さな場合、比較したデータの下を順次ずらし、一つ下に新たなデータを置く。
これでプログラムは動作する。工学系的にはこれでよい。
しかし、データを置く部分が 3a と 4 の2箇所に分かれていたり、「一番上」という端でしか意味のない処理を、毎回 3a で行っていたりと、無駄の多いアルゴリズムになっている。
ちょっと優れたプログラマなら、すぐに改良点を思いつくだろう。
「データの比較」と「データをずらす」が別に行われていることに無駄がある。
比較のためにデータを読み出しているのだから、条件によって「一つ下に」書き込んでしまえばよい。
そうすれば、データの比較と、ずらす処理を最小手順で行えるし、プログラムもすっきりする。
1. 一番下のデータを読み出す。
2. 読み出したデータと新たなデータと比較する。
3. 読み出したデータのほうが大きな場合、読み出したデータを、一つ下に書き込む。
3a 読み出したデータが一番上なら、一番上に新たなデータを置き、終了。
3b 一番上でないなら、1つ上のデータを読み出し、2に戻る。
4. 3 で読み出したデータのほうが小さな場合、一つ下に新たなデータを置く。
先のアルゴリズムを整理しただけで、それほど違うプログラムにはなっていない。
しかし、こうした細かな改良は、プログラムの動作速度を上げる。
しかし、これは実装上のテクニックの問題だ。
実は、アルゴリズムを見直せばもっとよくなる。
アルゴリズム上は、新たなデータを置く条件が「一番上に到達したとき」と「小さなデータを見つけたとき」の2つになっているのがよくない。これをまとめるとよりよくなる。
そこで、「すでにソートされたデータのリスト」を見直す。
これを、最初に空の状態から始めたのがよくなかった。ありえないほど小さなデータ… -999999 とかを最初にひとつだけ、入れておけばよかったのだ。
これだけの工夫で、「一番上に到達したとき」は、自然に「小さなデータを見つけたとき」の処理でカバーされることになる。
1. 一番下のデータを読み出す。
2. 読み出したデータと新たなデータと比較する。
3. 読み出したデータのほうが大きな場合、読み出したデータを、一つ下に書き込み、2に戻る
4. 読み出したデータのほうが小さな場合、一つ下に新たなデータを置く。
実装上のテクニックでごちゃごちゃと追加した処理が、すっきりと整理され、最初に考案したアルゴリズムに近くなった。
出来上がった結果には、最初に入れた「ありえないほど小さなデータ」が先頭に入っている。
なので、使うときには、最初のデータを取り除かないといけない。
工学系な思考をするプログラマは、細かなテクニックを駆使してプログラムを実装しようとするが、理系の思考をするプログラマは、アルゴリズムを根本から見直して問題を解決しようとする。
この違い、わかっていただけるだろうか?
ハッカーズ、という古典的なノンフィクションがある。
この中には、コンピュータープログラムが大好きで、プログラムばかりしている人々が登場する。
大抵は、驚くような優れたアルゴリズムを考案し、エレガントに問題を解決する人々だが、一人「風変わりな」ハッカーが登場する。
彼は、バグが生じたときに、バグの根本原因を突き止めようとはしない。
代わりに、バグがどのように生じているかを調べ、そのバグの影響を打ち消すようなプログラムを追加する。
「ハッカーズ」の著作者は理系の人間ではないため、彼のことを「風変わりだ」とは感じていても、なぜそう感じるのかを正しく説明できないような記述になっている。
つまり、他のハッカーは理系なのに、彼は工学系なのだ。
結局、プログラムというのは正しく結果を出せればそれでよい。…そう考えるならば、バグを打ち消すプログラムを作ったって問題はない。
しかし、それでは「端の処理」だらけになり、どこかでバグが噴出する可能性がある。
「ハッカーズ」の時代は、プログラマの多くが理系だった。
彼のように工学系が多少入っていたとしても、それは「風変わりな」ことだった。
現代では、プログラマの数は非常に増えている。
工学系のプログラマは山ほどいる。数式が全く理解できない、「自称文系」なプログラマだって珍しくない。
アルゴリズムの工夫が出来る、理系プログラマのほうが珍しいだろう。
さて、以前から「魔法使いの森」にリンクしてくれているページの中で、2ch の次のようなスレッドがある。
(リンク先はアーカイブ保存サイト)
ここで、岩田さんとナージャ・ジベリのどちらがすごいか、というような話も出ているのだが、岩田さんは「理系」プログラマで、ジベリは「工学系」プログラマのように思える。
#どちらの方のプログラムも、実際に読んだことはないので言い切れないけど。
岩田さんの言うところの「バグの出ないプログラム手法」というのも、出来るだけ「端」が生じないアルゴリズムを考案する、ということに尽きるように思う。
#自分もゲーム業界にいたのでわかるけど、定番になっている手法だけでもいくつもあります。
だから、「岩田さんがそんな手法を編み出した」のではなく、「知っている人は知っている手法を、ちゃんと知っている」ということ。
ゲーム業界にいても知らない人は少なくないので、ちゃんと知っているのは評価されるポイントです。
だから、アドベンチャーゲームには応用できない。アドベンチャーゲームは「条件判断」の塊で、つまるところ端だらけだからね。
開発が難航して、仕様変更を繰り返したためにつぎはぎだらけになったプログラムを「いちからつくり直していいのでしたら半年で」というのも、端だらけだとバグが出やすいため。
実際のところ、プログラム時間の大半は「バグの修正」に費やされる。
潜在的にバグが出やすいプログラムを使い続けるよりも、一から作り直したほうがはやく作れるのはそのため。
きわめてまっとうな主張しかしていないのだが、2ch で議論していたかたがたはプログラマでないか、プログラマでも「工学系」の方が多いようで、言葉を正しく理解できずに、過大評価してしまっているように見える。
オブジェクト指向の世界…だけでもないが、プログラム手法として、MVC 分離、という概念がある。
もとは SmallTalk で使われていた概念だ。
MVC は、Model View Control の頭文字。
Model は、数値モデル、内部的なアルゴリズムのこと。
View は表示、出力。Control は入力。
つまり、入出力と内部アルゴリズムは分離しておけよ、ってこと。
入力や出力は、環境に依存する。
また、使いやすさをもとめて、仕様変更が行われやすい部分でもある。
しかし、内部アルゴリズムは案外変わらない。
なので、これらを別々に考えてプログラムしていれば、プログラムの変更時にバグを生じにくい。
でも、この考え方自体が「理系」のものだ。
だって、考えの基本にあるのが、「アルゴリズムをシンプルに保ち続けよう」ということなのだもの。
岩田さんの言う、「バグの出にくい作り方」のひとつは、これではないかと思う。
#詳しく説明すると、それだけで長いので割愛。
勘違いされたくないが、理系の方法が良い、というのではない。
アルゴリズムを練り直すと良いものが出来る「可能性」はあるが、ダメになる可能性だってある。
すでにあるものは出来るだけいじらない、という、工学系の思考方法も重要なのだ。
結局は、バランスの問題。
Intel は、CPU の設計を「交互に」おこなうことでバランスを取っている。
ある世代では、全面的に設計を見直して、パフォーマンスを向上させる。その代わり、新機能はあまり追加しない。
次の世代では、細かな改良を施すとともに、新機能などを導入する。その代わりに、根本的な見直しは行わない。
全面的に改良を行った CPU は、数値上は素晴らしいパフォーマンスを出すのだが、深刻なバグが出たりもする。
しかし、こうしたうまいバランスの取り方が出来ないと、長い間トップランナーで居続けることは出来ないのだろう。
同じテーマの日記(最近の一覧)
関連ページ
別年同日の日記
申し訳ありませんが、現在意見投稿をできない状態にしています。 |