本日はループカウンタのカクテルで。材料は
やまださまの0 ベースなんだけど……
επιστημηさまのどんだけ~?
囚人さまの0 ベース vs 1 ベース
3回の繰り返しを、1, 2, 3と数えるか、0, 1, 2と数えるか。
1から始まる1ベースと0から始まる0ベース。
日常生活では1ベースでモノを数えるのに、なぜプログラム言語では0ベースで数えるのでしょう?
以下は私が0ベースで数えたい理由です。
3回のループを0ベースと1ベースで書いて見ましょう。
// 0ベースでのforループ
for (int i=0; i<3; i++)
// 1ベースでのforループ
for (int i=1; i<=3; i++)
今、ループカウンタiはint型です。int型の2と3の間には何もないですから、"<"での比較と値を-1して"<="で比較しても結果は変わりません。
0, 1, 2 と数える0ベースよりも、1, 2, 3 と数える1ベースの方が人間の数え方に近い気がしますね。
1ベースが狂うとき
では、1ずつインクリメントするのではなく、10おきに30までカウントするという場合はどうでしょうか?
つまり、10個ずつモノを数えるようなシチュエーションです。
// 0ベースで10ずつ増えるforループ
for (int i=0; i<30; i+=10)
// 1ベースで10ずつ増えるforループ
for (int i=1; i<=30; i+=10)
0ベースの場合、ループカウンタは 0, 10, 20, 30と増え、30となった時点で"30 < 30"が満たされなくなるので終了します。
1ベースの場合、ループカウンタは 1, 11, 21, 31と増え、30となった時点で"31 <= 30"が満たされなくなるので終了します。
どうでしょうか?不気味に感じませんか?本当にそんな数え方をしていますか?
人間の脳内では実は以下のように数えているのではないでしょうか?
do {
...
} while ((i += 10) <= 30);
そう、何かを行ってからループカウンタを増やしているのです。
10, 20, 30 と数えて、"30 < 30"となったところで数え終わっているのです。
そして、このループカウンタ周りの処理が繰り返しブロックの前にくると
// 0ベースで10ずつ増えるforループ
for (int i=0; i<30; i+=10) {
...
}
と
0ベースの記述になるわけです。
これが私の考えるプログラム言語で0ベースで数える理由のひとつです。
もうひとつの理由
もうひとつは、やや数学的な話になるのですが、0ベースでカウントし"<"で比較する方が綺麗に網羅できるからです。
intは離散的な(連続ではない飛び飛びの)値をとります。ですから、0と1の間には何もありません。
ですから 0 <= x < 3 という範囲の定め方をしても、1 <= x <= 3 という範囲の定め方をしても範囲は3なのです。
しかし、有限の幅を持つ値を扱うと前者のような範囲のとり方と、後者の範囲のとり方では大きさが変わってきます。
2007/8/10 <= x < 2007/8/13 は 24h * 3 = 3日分の時間がありますが、
2007/8/11 <= x <= 2007/8/13 は 24h * 2 = 2日分と3日目の0:00だけの時間しかないのです!
(そしてこれは業務で日付演算をするときにやってしまいがちなバグなのです)
1 <= x <= 3といった範囲指定をしてもバグらないのはintなど整数型を用いた場合の例外なのですね。
数直線を引いたときに、常に左側(負の方)は境界値を含む(<=)、右側(正の方)は境界値を含まない(<)としておくと
連続的な値を持つ場合にも隙間を作ることなく処理できるのです。
いかがだったでしょうか。
納得がいかないという方は遠慮なくお申し出くださいませ。
投稿日時 : 2007年7月30日 23:08