ベスパリブ

プログラミングを主とした日記・備忘録です。ベスパ持ってないです。

0が重要な条件なら「i > 1」は「i-1 > 0」と書いて欲しいという提案

まずは数学の話から

高校生の頃は数学は好きな科目でしたが、必ずしも得意とは言えない科目でした。特に苦手だったのが教科書を読んでいて、

 a \ne -1のとき 」

のような条件を読み解くことが苦手でした。

たとえば、以下の数式を見てください。

 a \ne -1 のとき、 \lbrace f(x) \rbrace^{a} f'(x) = \frac{1}{a+1}( \lbrace f(x) \rbrace^{a+1})' が成り立つ。 」

この「 a \ne -1」 という条件はなぜ付いているのでしょうか? 数学に慣れている人は一瞬で気づくと思いますが、 \frac{1}{a+1} のゼロ除算を避けるためにあります。

こういうとき私は、「 a+1 \ne 0のとき」と書いてくれたほうがゼロ除算を避けるためなことが一目瞭然でわかりやすいのになあ、と思います。

ところでここで a \ne -1と書くモチベーションは何かというと、

  • 【動機1】こちらのほうが短くすっきりと書けるから
  • 【動機2】これ以降の展開で必要だから予め移項させている

だと思います。

これらのモチベーションは正しいと思います。そして私は数学を専門としているわけではなくこれ以上話を展開させる気はないので、数学の話はここで終わりにします。

プログラミングの話

ここからが本題なのですが、プログラミングにおいて「リーダブルな条件式の書き方とは何か?」を考えたとき、表題にあるように『0が重要な条件なら i > 1i-1 > 0 と書いたほうが読みやすい』という主張です。

例えば以下のコードを見てください。

for (int i=0; i<n; i++) {
  for (int w=0; w<=W; w++) {
    if (w >= weight[i]) {  // (★)
      dp[i+1][w] = max(dp[i][w], dp[i][w-weight[i]] + value[i]);
    }
    else {
      dp[i+1][w] = dp[i][w];
    }
  }
}

上記の if (w >= weight[i]) { // (★) という条件式はなんのためにあるのでしょうか?

上記のコードは典型的な動的計画法のコードなので、慣れている人は一瞬で気づくと思います。

もし読み解くことが難しいと思った人は、以下のようなコードならどうでしょうか? (★)の行しかコードを変えていません。

for (int i=0; i<n; i++) {
  for (int w=0; w<=W; w++) {
    if (w-weight[i] >= 0) {  // (★)
      dp[i+1][w] = max(dp[i][w], dp[i][w-weight[i]] + value[i]);
    }
    else {
      dp[i+1][w] = dp[i][w];
    }
  }
}

答えを書くと、(★)の直下の dp[i][w-weight[i]] の範囲外アクセスを避けるための条件式になります(配列の添字は0以上でなければならない)。

つまり上記の場合、条件式は if (w-weight[i] >= 0) と書いたほうが、直下の dp[i][w-weight[i]] と形が一致しているのでよりリーダブルになる、という主張です。

さて、「プログラミングにおけるリーダブル」という観点に重きを置いたとき、

  • 【動機1】こちらのほうが短くすっきりと書けるから

という動機においては、どちらの書き方も大差ないでしょう。

一方、

  • 【動機2】これ以降の展開で必要だから予め移項させている

に関しては、if (w-weight[i] >= 0) の形の方が、これ以降のコードで w-weight[i] の形が一致している箇所があるので軍配が上がります。

コードミスもこっちのほうが気づきやすいと思いませんか?