要約
被除数が負のときに困ったら、/
演算子と %
演算子の代わりに、floorDiv
関数と mod
関数 を使うことを検討してみてください。
困りごと
曜日を表す列挙クラス DayOfWeek
があります。
enum class DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
currentDayOfWeek
は今日の曜日を返すプロパティです。
今日は土曜日です。
println(
currentDayOfWeek
) // > SATURDAY
明日は何曜日でしょうか。
println(
DayOfWeek.values()[
currentDayOfWeek.ordinal + 1
]
) // > SUNDAY
日曜日ですね。
では明後日は何曜日でしょうか。
println(
DayOfWeek.values()[
currentDayOfWeek.ordinal + 2
] // ArrayIndexOutOfBoundsException がスローされる!
)
おっと、これではだめですね。
配列のサイズを超えてしまっています。
println(
currentDayOfWeek.ordinal + 2
) // > 7
修正しましょう。
println(
DayOfWeek.values()[
(currentDayOfWeek.ordinal + 2) % DayOfWeek.values().size
]
) // > MONDAY
明後日は月曜日でした。
7日後は?
println(
DayOfWeek.values()[
(currentDayOfWeek.ordinal + 7) % DayOfWeek.values().size
]
) // > SATURDAY
14日後は?
println(
DayOfWeek.values()[
(currentDayOfWeek.ordinal + 14) % DayOfWeek.values().size
]
) // > SATURDAY
どちらも土曜日ですね。
では…7日前は?
println(
DayOfWeek.values()[
(currentDayOfWeek.ordinal - 7) % DayOfWeek.values().size
] // ArrayIndexOutOfBoundsException がスローされる!
)
あれ、なぜでしょう、配列の範囲外になってしまいました。
指定したインデックスの値を確認してみましょう。
println(
(currentDayOfWeek.ordinal - 7) % DayOfWeek.values().size
) // > -2
なんと、剰余が負になっています!
原因
多くのプログラミング言語では truncated division という方法で除算を行います。
Kotlin の標準の数値型の /
演算子および %
演算子もこの方法を使います。
この方法では -6 〜 +6 を +3 で割ったときの商と剰余は次のようになります。
被除数 | 除数 | 商 | 剰余 |
---|---|---|---|
+6 | +3 | +2 | 0 |
+5 | +3 | +1 | +2 |
+4 | +3 | +1 | +1 |
+3 | +3 | +1 | 0 |
+2 | +3 | 0 | +2 |
+1 | +3 | 0 | +1 |
0 | +3 | 0 | 0 |
-1 | +3 | 0 | -1 |
-2 | +3 | 0 | -2 |
-3 | +3 | -1 | 0 |
-4 | +3 | -1 | -1 |
-5 | +3 | -1 | -2 |
-6 | +3 | -2 | 0 |
また、-3 で割ったときは次のようになります。
被除数 | 除数 | 商 | 剰余 |
---|---|---|---|
+6 | -3 | -2 | 0 |
+5 | -3 | -1 | +2 |
+4 | -3 | -1 | +1 |
+3 | -3 | -1 | 0 |
+2 | -3 | 0 | +2 |
+1 | -3 | 0 | +1 |
0 | -3 | 0 | 0 |
-1 | -3 | 0 | -1 |
-2 | -3 | 0 | -2 |
-3 | -3 | +1 | 0 |
-4 | -3 | +1 | -1 |
-5 | -3 | +1 | -2 |
-6 | -3 | +2 | 0 |
グラフにすると次のようになります。
Quotient and remainder as functions of dividend, using truncated division / CC BY-SA 3.0
グラフの読み方:
- 左が除数が正の場合、右が負の場合
- 赤が商、緑が剰余
- 横軸が被除数
- 縦軸が商および剰余の値
このように、被除数の正負が変わると剰余の繰り返しパターンが変わってしまうことが、今回の困りごとの原因です。
解決方法
/
演算子と %
演算子の代わりに、floorDiv
関数と mod
関数 を使いましょう。
これらは floored division という方法を使います。
この方法では -6 〜 +6 を +3 で割ったときの商と剰余は次のようになります。
被除数 | 除数 | 商 | 剰余 |
---|---|---|---|
+6 | +3 | +2 | 0 |
+5 | +3 | +1 | +2 |
+4 | +3 | +1 | +1 |
+3 | +3 | +1 | 0 |
+2 | +3 | 0 | +2 |
+1 | +3 | 0 | +1 |
0 | +3 | 0 | 0 |
-1 | +3 | -1 | +2 |
-2 | +3 | -1 | +1 |
-3 | +3 | -1 | 0 |
-4 | +3 | -2 | +2 |
-5 | +3 | -2 | +1 |
-6 | +3 | -2 | 0 |
また、-3 で割ったときは次のようになります。
被除数 | 除数 | 商 | 剰余 |
---|---|---|---|
+6 | -3 | -2 | 0 |
+5 | -3 | -2 | -1 |
+4 | -3 | -2 | -2 |
+3 | -3 | -1 | 0 |
+2 | -3 | -1 | -1 |
+1 | -3 | -1 | -2 |
0 | -3 | 0 | 0 |
-1 | -3 | 0 | -1 |
-2 | -3 | 0 | -2 |
-3 | -3 | +1 | 0 |
-4 | -3 | +1 | -1 |
-5 | -3 | +1 | -2 |
-6 | -3 | +2 | 0 |
グラフにすると次のようになります。
Quotient and remainder as functions of dividend, using floored division / CC BY-SA 3.0
グラフの読み方:
- 左が除数が正の場合、右が負の場合
- 赤が商、緑が剰余
- 横軸が被除数
- 縦軸が商および剰余の値
このように、被除数の正負によらず剰余の繰り返しパターンが一定です。
なお、当然ですが、次の式が成り立つことは変わりません。
被除数 = 除数 × 商 + 剰余
これを今回の困りごとに適用すると…
println(
DayOfWeek.values()[
(currentDayOfWeek.ordinal - 7).mod(DayOfWeek.values().size)
]
) // > SATURDAY
期待通り土曜日になりましたね!
参考
/以上