プログラミング難しい

比較と状態遷移が難しい。


年をとるごとに、自分がプログラマとして能力が高まりつつあるのを感じる。


何故、年を取ると能力が高まっていくのかというと、将来への明るい希望が減ってくると、「自分は優秀だ」という勘違いぐらいしかすがるものが無くなるからなのだった。だから、多分俺は今後も年を取るにつれて能力が高まっていくと思う。


さて、そんな優秀な俺なので、処理内容を理解している場合は、プログラムの実装時に困る、ということはほぼ無くなっているという気がする。
(まあ、実装以外の面で、処理わからんとか、アルゴリズムわからんとか、論文全く読めないとかで停止することはよくあるので、別にプログラミング能力の高まりに対応して仕事がサクサク進むというわけでもないのだけど)


しかし、それでも、やっぱりプログラム難しいと思う点が、最後に二点残った。これは、

  • 比較が < なのか <= なのか?
  • 状態遷移ちゃんと書けたのか?

の、二点である。


まず、比較のほう。

例として、文字列から文字を探して、それが終端を超えてなければ、return 1 という関数があるとする。

// start_ptr 〜 end_ptrの間で'a'の文字が入っているなら1を返す
int find_a(char *start_ptr, char *end_ptr) {
    while (*(start_ptr++) != 'a')
        ;

    if (start_ptr ??? end_ptr) {
        return 1;
    }
    return 0;
}

さて、この時、??? に入る比較は、'<'か? '<='か? という問題である。


なお、どちらとも言えない、が正解だった。end_ptr は、終端の文字を指すか、C言語の雰囲気的に(end_ptr - start_ptr で長さが出るので)終端の次の文字を指すか、が決まっていないので、回答は一意には定まらない。


この問題は、いっっっっっつも、なんか、「えーとーーどっちだっけーーー」とか考えてる気がする。自分だけで書いてるときは習慣として、end_ptrは終端の次の文字、比較は '<' と心に決めているのだが、それでも、いっつも悩んでいるし、他人が書いた処理が返してきたポインタだった場合は、さらに悩んでいる。
そして、さんざん悩んだ割に、一定確率で間違いを挿入しているという気がする。(先週もやった)


同じような問題はあちこちにあって、2D画像に対して畳み込みフィルタ入れるとき、カーネルサイズが奇数だった場合に処理してよい範囲は、いっつも悩んでいるし、時々間違えるし、さらにそれをSIMDにする時に、間違いをもう二回ぐらい追加しているという気がする。




さて、次に、状態遷移だが、これは、もう、俺は覚悟を決めた。プログラミング学習を通じて理解した重要な事実のひとつは、「状態遷移をきれいに書く方法はこの世には存在しない」ということだった。
状態遷移は、どんなに面倒でも、それをプログラムに記述する以外の方法は無いし、新しいイベント、状態を追加するときは、新しいバグを一個追加するのだ、という気持ちで臨むことが大事である。

状態遷移は、処理が最悪の場合、イベント数M、状態数Nだと、処理の数は、O(MN)になる。本来は、状態を追加するときは、全てのイベントの組みあわせと動作確認する必要があるし、イベントを追加する時も同じように状態の数だけ動作確認する必要がある。

もちろん、いくらかは構造化できるから、実際に、O(MN)になることはほぼ無いけど、今度は、そういう構造化を入れてしまうと、それが本当に、全ての状態/イベントの組み合わせを網羅できているのか?という問題が出てくる。そういうのは、コードを見てもよくわからないし、ちょろっとテストするだけではよくわからん場合が多い(そもそもテストパターン思い付くなら対応してるはずだし)

というわけで結論としては、状態遷移処理は、どうやってもバグが入るから、解析とデバッグしやすいように、状態を一個の構造体に入れて、処理を一個の関数でまとめるとわかりやすいということになった。(もちろん、実際には共通部分分けたりぐらいはするが)



このふたつの問題の共通点は、間違っていても大体の場合動いてしまうというのがある。もちろん、正しくは、境界条件探して、テスト作って、動いているかどうか検証すべきなんだけど、関数への入力の整合性とらないといけない場合とか入力データ作るの面倒だし、そんな末端の条件分岐一個のために、全体が正しく動くデータ作るのとか面倒だし…

あと、問題がプリミティブなので、一般的な方法で対応をとるのが難しく、結局一個一個チェックする以外の方法が無いというのもありますね。



というわけで、プログラミング難しいという話だった。


状態遷移難しいという話に関連して、次回、MVCとかのMVなんとかフレームワークが何故クソなのか、という話について書こうと思う。