FizzBuzz で複数条件を組み立てる
このレッスンで分かること
- 剰余演算子
%は割り算の余りを返し、倍数の判定に使えます- 条件どうしが重なっているときは、狭いほうを先に書きます
- 数値をそのまま返すと、文字列を期待するテストは通りません
「割り切れたか」をどう聞くか
FizzBuzz は英語圏の数え上げゲームです。1 から順に数え、3 の倍数では数の代わりに Fizz、5 の倍数では Buzz、両方の倍数では FizzBuzz と言います。これをプログラムにする課題が入門の定番になったのは、条件分岐の順番という、初心者がいちばんつまずくところが凝縮されているからです。今回はプロフィールサイトを離れ、数を 1 つ受け取って、その 1 つぶんの答えを返す関数を書きます。
まず要るのは「割り切れたか」を聞く手段です。剰余演算子 % は、割り算の余りを返します。
JavaScript
console.log(9 % 4); // => 1
console.log(8 % 4); // => 0
console.log(0 % 4); // => 0余りが 0 なら割り切れた、つまり倍数だということです。条件式にすると n % 4 === 0 の形になります。
3 行目に注目してください。0 はどんな数で割っても余りが 0 です。つまり 0 は、あらゆる数の倍数として扱われます。テストに 0 が含まれているときは、必ず手で流して確かめてください。
先に書いた条件が、後ろの条件を飲み込む
ここからが本題です。倍数の判定には、条件どうしが重なるという厄介な性質があります。
JavaScript
// NG。8 を渡しても「2 の倍数」しか返らない
if (n % 2 === 0) {
return "2 の倍数";
} else if (n % 4 === 0) {
return "4 の倍数";
}8 は 4 の倍数ですが、同時に 2 の倍数でもあります。else if は最初に当たった枝で打ち止めなので、2 つ目の枝には永遠に届きません。
| 条件 | 当てはまる数 | 広さ |
|---|---|---|
n % 4 === 0 | 0, 4, 8, 12 | 狭い |
n % 2 === 0 | 0, 2, 4, 6, 8 | 広い |
4 の倍数は、2 の倍数の中にすっかり含まれています。含まれている側、つまり狭い条件を先に書かなければ、そこへはたどり着けません。逆に、重なりのない条件どうしなら順番は自由です。書き始める前に「この条件とこの条件は重なるか」を確かめる癖を付けてください。
なお、2 つの条件を同時に満たすことは && でも表せます。n % 2 === 0 && n % 3 === 0 は「6 の倍数」と同じ意味です。仕様書の言い回しに近いほうを選んでください。
この考え方は FizzBuzz だけの話ではありません。会員ランクの判定でも、割引の適用でも、エラーの分類でも、条件が重なる場面ではいつも同じことを考えます。FizzBuzz が入門課題として愛される理由はここにあります。
数値のまま返すとテストが赤くなる
倍数でないときに数そのものを返す仕様でも、返すのは数値ではなく文字列です。String() に通せば、数値を文字列に変えられます。
JavaScript
console.log(String(7)); // => 7
console.log(typeof String(7)); // => stringテンプレートリテラルで書いても結果は同じです。戻り値の型がそろっていないと、呼び出す側が毎回型を気にすることになります。
よくある間違い
- 重なりを見落として並べる — 書き終えたら、複数の条件を同時に満たす値を 1 つ選んで手で流してください。そこが合っていれば残りはほぼ通ります
elseを付けずにifを並べる —returnで返す形なら結果は変わりませんが、変数に代入して組み立てる形だと、後ろの代入で上書きされます
要件
- 関数名は fizzBuzz、引数は 0 以上の整数 n の 1 つ
- 15 の倍数なら "FizzBuzz"、3 の倍数なら "Fizz"、5 の倍数なら "Buzz" を返す
- どれにも当てはまらないときは String(n) のように数値を文字列にして返す
- 戻り値は必ず文字列にし、数値のまま返さない
入出力例
fizzBuzz(0) → "FizzBuzz"
fizzBuzz(1) → "1"
fizzBuzz(3) → "Fizz"
fizzBuzz(5) → "Buzz"
fizzBuzz(15) → "FizzBuzz"