"i"禁止

昨晩、組み込み系で働いてる友人と話していて、「ループカウンタ変数として"i"を使うの禁止」というコーディング規約だという話を聞いた。
前提として、言語はCであった。


ループカウンタを使うのはどんなときだろうか。
最初に思いついたのは配列の内容を順次使用する場合。

for (i=0; i<N; ++i) {
  printf("%d\n", data[i]);
}

この場合、重要なのは配列の意味であろうと思う。この例では、"data"とかいう、何が入ってるんだかまるでわからない名前が問題なのであって、"i"が問題なのではない。"N"もどうかと思うが。
次に多重化する場合。

for (i=0; i<N; ++i) {
  for (j=0; j<M; ++j) {
    for (k=0; k<L; ++k) {
      printf("%d\n", data[i][j][k]);
    }
  }
}

3次元まで到達するようなプログラムは大抵物理学由来であろう。数式の上で添え字としてi,j,kを使うのはむしろ一般的なので、これは逆にわかりやすい命名だと言えると思う*1。ここでも"data"や"N","M","L"という命名には問題があるだろう。
一方、物理とは関係ないケースでは、2次元が限界ではないだろうか。世の中の様々な事象を分析するのには、ほとんどの場合、状態ベクトルを作って行列で処理するだろう。一変数高次多項式の方程式を作るよりも、多変数一次方程式の方がモデル化しやすいし、固有値や特異値、行列のランクからもわかることは多い。技術者といえど、3階以上のテンソルとなると、途端に難解に感じるのではないだろうか。


というような内容をその場では考えていた。
そのときは訳が分からんと答えたのだが、実際どういうことなのか、考えてみる。


まず、配列内容を回すだけの場合、ループカウンタを使う必要は無い。特に組み込み用途であれば、ポインタを使うだろう。

int * p = data;
while (*p != END) {
  printf("%d\n", *(p++));
}

この場合も、"p","data","END"といった変数名はあまり適切じゃなさそうだけど、とりあえずループカウンタの話ではない。これは、要するにC++でstd::vectorの添え字でループ回すくらいならイテレータ使えよ、という話と同じ。


つまり、Cでループカウンタを使う必要がある、ということは、ループ回数自体に意味があるケース、ということになるだろう。

for (i=0; i<N; ++i) {
  printf("%d\n", i);
}

そりゃ"i"は使用禁止ですってことにもなる。ループの終了条件を見れば、"i"が何を意味しているのかはわかるだろうが、局所的に見た時に、「このprintfは何を出力しているのか」がわからない。"i"ではなく、それなりに意味のある変数名にすべきだろう。

*1:本来は逆に、ループカウンタに"i"を使うという習慣自体が、FORTRANの仕様(変数名の先頭がI〜Nのものは暗黙的に整数型)から来ていて、恐らく物理・化学方面の利用を想定したものだと思う。