自分の書いたソースには過去の自分の見識しかなく、先人の書いたソースには先人の見識がある

このように数多くのプログラマが「車輪の再発明」の罠に悩まされているのですが,車輪の再発明(より正確に言うと,車輪の再実装)にはそれなりに意義もあります.

車輪の再実装 - Life like a clown

id:tt_clownさんの反応に対してですが、
まず、自分も「車輪の再発明」ではなく「車輪の再実装」はすべきと考えます。
既にある機能を実装する事を「車輪の再発明」といって嫌う話もありますが、
実際にそれは「既にあるものを発明(新たに考える)する必要はない」という意味で


「既にあるものを実装する必要がないわけではない」と考えています。


自分は「車輪の再発明」と「車輪の再実装」は完全に異なると考えていて、
車輪の再発明」で無駄に頭を悩ませる必要はないですが、
寧ろ「車輪の再実装」は非常に重要だと考えています。
ただ、「車輪の再実装」は習作でしかないことも多く、
習作は習作だということです。


書道でお手本を見ながら字を書くのに似ています。
お手本があるとき「新しい字」を考える必要はないですが、「既存の字」を見てそれがなぜそうなのか考える感じです。


実際、多くのプログラムは読み書きして研磨されることでより優れていきます。
全く書き直されないプログラムというのは進化しません。
なので、既にあるので、といって誰も何も書かなかったらプログラムが進化することはありえないですが、
そんなことはないですよね。世界を見てもどんどん新しいプログラムが書かれていきます。


そういった大きな領域ではなく、単に狭いコンテナという部分に目を向けても
効率的なコンテナの「車輪の再実装」の経験があればニッチな要求をするコンテナを書かねばならないときにその経験を生かすことができるでしょうし、
例えばSTLもどきを自分で実装しようと考えたときに、
STLのインターフェイスや内部実装を見るだけでもずいぶん意味があります。


自分が言う問題は「既存のものがよくわからないので、自分の知識だけで再実装する」と言っているケースです。
これは、多くの場合非常に狭い知識で作られるのでかなりうまくないものができます。


既にある(優れた)ものを自分で実装してみて解ることは、
・いかに自分の実装が至らないかを感じる
・既に使われている実装はどうなっているかを知りたくなる
・内部での挙動に対する理解が深まる(自分の書いたものは既にあるものと比べてどうか? という比較においても)


といったところです。
ここで「なぜ」が生まれると感じます。
「なぜ既存のものはこうなっているんだろう? なぜ自分のものと比べて挙動が異なるのだろう?」
すると「ソースを読む」ということに発展します。
id:tt_clownさんも言われるように

私も Boost のソースコードを読もうとすると物によっては挫折するのですが,既存のソースコードを読んで理解すると言う作業は,自分で(取りあえず)実装してみるよりもはるかに多様な知識を要求されます(その言語自身への理解,アルゴリズム,エラー,etc).

この要因の一つに,既存のコードには「核となるアルゴリズム」と「様々なケースに対応するためのエラーチェック」が同時に含まれているから,と言うものがあります.

車輪の再実装 - Life like a clown

先人のソースを読むためには自分の範囲にない知識が必要とされるので力がつきます。
そう、自分のソースには自分の知識と経験の範囲しか書かれませんが、
先人のソースには先人の知識と経験と熟考がある筈だからです。
そして、何が問題になるかを推し量ることで問題発見の力もつきます。


「コンテナ」一つとっても自分で実装をしていない場合には、
「可変長配列」「双方向リスト」なんて言葉一つで片付いてしまい、
的確にこれらがどのような挙動をするのかを人に話すことさえ難しいです。
実際に実装をしてみるとこれらはどんな仕組みで行われているのか知ることができます。


そうなんです。
「車輪の再実装」はドンドンした方が良いんです。
ただ、それが多くの場合既にあるものより優れていることは極々稀なことだ、というだけです。
(ニッチなニーズに合致することはあります)
なので、それを他人に押しつけたり、閉じられた世界で標準であるようにするのは気をつけるべきだ、
ということだけのことです。


そういう意味では、id:monjudohさんの「再実装した車輪を使わない方がよい理由」というのが非常にしっくりきます。

車輪を再実装しない方がよいというか、

再実装した車輪を使わない方がよい理由だけど、

テストや検証の量が全然違うからというのが一番大きいと思う。
...
車輪の再実装をする本当のコストっていうのは、

車輪を実装する分のコストだけじゃなくて、

テストや検証を既存の車輪と同じくらい充実させるコストも含んでるという訳。

車輪の再実装をしない方がよい理由 - 文殊堂

こうしたコストの問題もありますし、
既にあるソース、他人のソースには先人や他人の知識が詰まっているので自分のソースに閉じこもらない方が良いですよ、というだけのことなのです。

「俺のソースだから」というプログラマは死んだらいいのに

最近こんなやりとりがあった。

「Cって標準のコンテナ(双方向リストや可変長配列など)がなくて不便。
 Cのプロジェクトってコンテナ自体ないこともあるし、コンテナがないとプログラムって書きにくいよね。
 その点C++STLが(ry」
...
「コンテナ? STL“も”いいけど、自分で書きたい」

正直、自分は「え? 何を言っているんだ?」と思った。
STL“も”いいけど、“自分で書きたい”だって?


その人はプログラマとしては十年選手だが、C++に関して、特にテンプレートに関しては稚児に等しいレベル。
で、どうして「自分で書きたい」ということになるんだろう?
それを使わされる人の苦労はどうなる?
それともプロジェクトに同一の事をするための複数のコンテナが存在するのか?
俺俺コンテナを書きたい理由はなんだ?


要するにここにおいて「自分で書きたい」はSTLがよく解らないので、
機能や動きを隅々まで把握するために自分で書きたい、ということのようだ。


???
STLのソース読めば良いんじゃない?
超例外的なケースに関しては自前で書くこともあるだろうけど、(そんなケースはほぼ無いんだけど)
なぜ「殆どのケースで使われることになるコンテナのようなもの」を自前で書かなければならないのか?
標準で定められた名前、挙動、インターフェイスに逆らわなければならないのか?


加えて、STLの動作が気に入らない時に直せないから、というのもあるようだ。
しかし、そういう人の言う「動作が気に入らない」という理由は殆ど些末なことで正直「どうでもいい」レベルが多い。
STLに関しても挙動が不可思議である、と感じる部分もあるだろうが、それは「Effective STL」でも読んでくれれば良いレベル。


「読むより書いた方が早い」
「自分が書いたソースでないといざというとき何が起きているのかわからないから」


というのもよく聞く話ではあるのだが、
なぜソースを読んだり使ってみたりして理解しないのか?
自分には正直理解ができない。


これが真の達人であるなら自分も達人の書くコンテナというものを見てみたいし、興味がある。
しかし、テンプレートをよく理解していない人間が書いたソースを読むのも、それを使うのもまっぴら御免だ。
もし「俺が標準を超えてやるぜ」という心意気を持っているのなら立派だ。
だが死ね。


言語を触って僅かな期間しか知らないような一介のプログラマ
何年にも渡って使われ練り上げられた標準に勝てるものを書けるだなんていうことは
壮大なる驕りでしかないのだが、なぜ書こうという意識になるのか。


そういう人に共通する方向性を最近見いだした。
こういう人は「自分の書いたソースしか読まないし書かない」事が多い。
加えて「C++ってこういうところが糞だよね」っていう事が多く、
それは理由があってそうなっているんだよね、ということを理解しない。
多くの場合、全ての言語機能には「理由」があるのだが、それに対して「なぜ」ではなく「糞」と片付けてしまう。


あくまで「自分が使って不都合だと感じたらその機能は糞」なのだ。
他の理由があってそうなっているとしても。


要するにその考えの根本は「俺のソースは俺のもの。お前のソースはお前のもの」ということだ。
「俺が書いたものは俺にはわかる。他人が書いたものなんて知らない」という思考に近い。
圧倒的にソースコードリーディングの経験がなく、
他人のコードを読んで挙動を理解し、手を入れるという機会に恵まれていない。
なぜその機能がそうやって実装されているのかを考える機会を与えられていない。


自分のソースだけを変更する機会だけが豊富なので、
「他の人の書いたソースはその人が直すべき。書いた人がいない“標準”なんてソースは不可解な挙動をしたら誰が直すの?」
という負の思考にとらわれているのだ。
「この機能は使いにくい。俺だったらこう書くのに(書けるようにするのに)」
という自分主体の考えに寄り添っているのだ。


これは一概には言えないかもしれないが、人のソースに手をいれる経験をちゃんと持つと、
・読みにくいソースに出会ったとき、読みやすい形に直そうと思う。また、そう書こうと心がけるようになる
・変更の影響度を気にするようになるので影響度を最小に抑えようとする
という傾向を示すと経験から自分は感じている。


勿論、人が書いたソースをいじって(影響度を読み誤って)大失敗、ということもあるのだが、
そんな失敗は(リカバリ体制がちゃんとしていれば)些末なことで、必要な失敗と言える。
そういった失敗はどんどんすべきで、それでソースに対する考え方を身につけるべきだ。


「俺が俺が」のやり方は本当にうまくない。


「俺のソースは俺のもの」は害悪でしかない。


「俺のソースだから」っていう人はみんな死んだらいいのに。

恐れることなく設計を正せ

どんなに正しく設計をしたつもりでも、
どこかで必ず「これは設計を直した方がいいのじゃないか?」
と思うことがある。

そういうときに聳え立つ「既に書かれたソースたち」。
基本インターフェイスで対話していても
抜本的に手をいれなければならない事もある。


既に動作確認したモジュールよ、さようなら。


そういうときに、恐れを緩和し、バグを減らしてくれるのに活躍するのが
テストや、適切なアサート、あとはバグに対する心構え、なのかも。
などと思ったりして。
デバッグなどと同じで、影響度の切り分けができていれば、
この変更を行ったことで、どこがバグるのかある程度予想できたりもする。

なので、自分や周りを騙し騙しにするより、
正しい設計にただせるような姿勢でいたい。

まあ、たまに「あわー」ってなることもあるんですけど。

未だにとらドラフィーバーから脱出できないorz

自分はなかなかとらドラフィーバーから、脱出できずにもがいてます。
あれ、おかしい……、というくらいはまってます。
放送のときにリアルタイムにはまったわけではないのですが、
たまにエピソードを見なおしたり、
キャラクターソングを聴いてしまったり、
どうにもこうにもです。
minekoaさんのエントリを読んで、こういうのややはりあるんだなあと思った次第。orz...

とらドラ!の最終回あたりから、わたしのとらドラ!フィーバーが続いていて、毎日とらドラ!の小説を繰り返し繰り返し読んだり、そうでなければDVDを繰り返し繰り返しみたりと、ほとんど中毒の様相を呈していました。
とらドラ!の何がそんなにわたしの琴線に触れたのかというと、甘々の恋愛モノにとろんとしてしまう部分と、それと「何かを壊すことが戦うこと」みたいなメンタリティを持った(要するに喧嘩っ早い/喧嘩大好きな)わたしでは得られない結論と、なのに強く覚える共感がわたしをふにゃ〜んとさせるので、繰り返し繰り返しその感触を楽しみたくなってしまうのです。
とはいえ、無限の時間があった学生の頃と違い、何かと一日が早く過ぎてしまう老化したこの脳には(たぶんクロックが全然遅くなっているんでしょうねぇ、トホホ)、そんなことをやっていれば何も進まず時間だけが過ぎていくわけで。

http://d.hatena.ne.jp/minekoa/20090419/1240156268

そろそろ、いろんなものを
モ、モグ、モルグに葬り去りたい気分でいっぱいです。

最適なデバッグは可能性を潰していく事

minekoaさんのエントリを読んでいて、

「そうそう、コンパイラがこんなこと言うときは実際にはあんな事が起きてるんですよ」みたいな知識データベース。そしてコンパイラが検出出来ないタイプのバグについても、現象に「あれ?、どこかでみたぞ、これ」となる。そういう「良くあるパターン」の蓄積はデバッグのスピードアップに大きく貢献します。
正しい。けれど、それはそれとして。
根本的な問題として、「問題の切り分け」というのが出来ていないからデバッグ出来ないというのが、「デバッグができないこと」なのではないかと思います。

デバッグという基礎素養 - みねこあ

「最適なデバッグはまず適切で高い可能性から潰していく事、最後に残った事が真実」
だなあと思いました。
デバッグをするときに経験があると
「そうそう、こういうときはこれだよね」
というのが解ります。
ハングアップが一番簡単で、
スタックを見ても良いし、
とんでもないアドレスに飛んでいるなら、
呼び出し元アドレス(大抵レジスタにあります)を表示するだけでも
手がかりになります。


ただ、こういうことは小手先のワザであって、
本質ややはりminekoaさんのいうように「問題の切り分けができるか?」
なんだと思います。
たまにバグがでたとき「それはありえない」という人もいますが、
バグに「ありえない」はない。「ありえない」と考えても、つぶせる可能性は潰すべき、なんてことを思います。
SDKとかライブラリのバグだったよ、なんてことがよくあるわけだしね。


例えば、ゲームなどでも
「テクスチャがでない」という問題があった場合、

「テクスチャが確保できてない」
「テクスチャが指定されていない」
「テクスチャのファイルが壊れている」
「その他のメモリ破壊の影響」
「モデル側のテクスチャを指し示すアドレスが壊れている/おかしい」
「そもそもテクスチャのVRAMアロケータのバグでテクスチャが見えてない」
「テクスチャがでてないのではなく、モデルがでてない」
えとせとらえとせとら、と様々な原因があります。
以上は、現実的にありえた事象です。

基本的にバグたりうる原因は無限大にあるので、
これらを一つずつ潰したところで収束するとは限りません。
例えば「デバッガが物理的に壊れている」なんてのもあります。(ありました)
ただ、殆どのケースはこれらを効率よく潰していくことで正答にたどり着きます。

結局は、デバッグは様々な要因を突き詰めて潰していき、
最後に残った物が真実です。

そういう意味では、「マインスイーパ」というのは凄く適切なたとえで、
勘や経験が働く人ほど、素早く的確に白い領域を広げていくことができるので、
非常に簡単に素早くデバッグが終わります。
ただ、時には「思いこみ」が支配していることがあるので
他人の助言が救いの手になったりもします。

ただし、どんなにうまい人でも、
マインスイーパが三次元で展開されていたり、
ヒントの数字が嘘だったりすると途端に手こずります。
ということは、やはり設計をシンプルに見通しよく、正しく保つことはデバッグの期間や時間、機会を減少させ、
効率よい開発を行える事に直結します。

パターンを覚えるタイプのデバッグ方は即効性はあるのだけれど、本来の意味での「デバッグ力」ではないんじゃないかな
だってデバッグ力は「学習する力」だもん(from マインドストーム

デバッグという基礎素養 - みねこあ

自分もパターンを憶えるより実際に被害に遭い、
理解を深め、
勘をやしなう方がいいかなーと思います。
C++の罠なんかも顕著で、浅いところを触っていると出会わないのですが、
経験によって「あああああああああああああああああ」と叫ぶことでより堅牢になるものですし。orz...
「理解」こそが最大の武器だと思います。なぜこうなるのか、ならばこういったこともあるのではないか、という手探りがデバッグ力?を養うのではないかと。

そういえば、
ハードウェアブレークポイントを知らない人もいます。
CPUによってはちゃんとICEを内蔵しているので、
ハードウェアブレークポイントをはることもできます。
固有のアドレスに読み書きされた場合に検出できるので、
非常に便利です。
ダンプを見ても、解ることがあります。
デバッガによってはダンプが変更されたら、赤く表示してくれたりするんですのにねえ、と思うこともあります。
これら知識も必要なのかな、とも。


ただ、
テストのコストやプログラムの設計の技術も結局はデバッグの期間を短縮させ、
品質の高いものに仕上がることに直結するのですよねえ、と思った次第です。


そして、マインドストームはポチっとしましたよ。:-)

実績があるコードと製品に使った事があるコードは違う

よくあることなんだけれど、「社内の実績があるコードを使え」と言われて見てみると、
「これはひどい」というコードであることがある。
そういう人が言っている「実績がある」は「製品に使用した事がある」なんだよな。

例えばあるデバイスから、非同期で読み書きするようなモジュールがあったのだが、
書き込みに対する操作で、
HogeDeviceWriteInitialize
HogeDeviceWriteExecute
となってて、
Initializeの中で書き込み先アドレス、書き込み元アドレス、書き込みサイズを記憶し、
「なぜか」
Executeの中で、デバイスに対する非同期リクエストを発行し、以降はそれを待つなんていう仕組みになってた。

// 構造だけ
void HogeDeviceWriteInitialize(int addr, const void* data, size_t size)
{
    ... 変数に記憶
}

HogeResultCode HogeDeviceWriteExecute()
{
    switch (mode) {
    case INIT:
       SDKWriteAsync(...); // init の処理で記憶した値で非同期書き込みリクエスト
       break;
    case WAIT:
        result = IsSDKAsyncWaiting(); // 非同期処理が終わったか問い合わせ
        break;
    case SUCCESS:
        // なんか上の方での成功を見てる
       result = SDKResultCode();
       break;
    case FAILURE:
        // なんか上の方での失敗の詳細を見てる
       result = SDKResultCode();
       break;
   }
...
}

こんな感じ。
非同期処理というのに、Executeを一度呼び出さないと処理が開始されないわ、
Executeの中にリクエストと、非同期待ちと、成否を返す機能がごった煮にされていて「これはひどい」。
普通に、
writeAsync……非同期で書き込みをリクエストする機能
isAsyncWaiting……非同期処理を待っているか成否を返す機能
getResultCode……最終的な成否及び、失敗なら失敗の原因を返す機能
みたいに分けるだけでええやん?(もともとのSDKはそういう構造だし)
なのになぜ、ごった煮にしたがるのかなあ。
InitとExecしかないから使いやすいだろー!?
ってことなのかなあ。


そして、これが「実績があるので使え」と言われると、
そんなものは死んでも使いたくないという罠。