fizbuzz
という有名な問題があります。おさらいすると以下のような条件をみたすプログラムを準備するものです。
-
3
で割り切れるときはfizz
と出力する -
5
で割り切れるときはbuzz
と出力する - それ以外はその数字を出力する
以下では z
で割り切れるときはzzzz
のことを
z
=zzzz
とも表現しますので一時的に記憶をお願いします。
一般的に
このような成果物が一般的でしょう。
成果物案 1.
const fizzbuzz = (num: number): string => {
if (num % 15 === 0) {
return 'fizzbuzz';
}
if (num % 5 === 0) {
return 'buzz';
}
if (num % 3 === 0) {
return 'fizz';
}
return `${num}`;
};
成果物案 2.
const fizzbuzz = (num: number): string => {
if (num % 3 === 0 && num % 5 === 0) {
return 'fizzbuzz';
}
if (num % 5 === 0) {
return 'buzz';
}
if (num % 3 === 0) {
return 'fizz';
}
return `${num}`;
};
- は
fizzbuzz
の条件を計算しており、2. は計算していないだけの違いです。
疑問
ここでわたしが抱いた疑問です。
1. 6
= fizz
になったら ?
ビジネス要件が変更され6
= fizz
となったとします。すると上記成果物のコードは1. 2.ともに2箇所変更する必要があります。
2. fizz
=> hoge
になったら ?
一括置換すればいいのですが、若干面倒です。
3. 15
= fizzbuzz
?
成果物案1. の15
= fizzbuzz
という条件は上記条件にはなく、数学的に自明だからこそ導き出せたものです。
15
って ?
この15
は数学的には3
と5
の最小公倍数を意味します。最小公倍数は互いに素の値の組み合わせのときはその値同士を掛け合わせたものになりますが、互いに素でなければその限りではありません。
今回の値は3, 5
であり、これらは互いに素なので15
自体は正しいものです。
成立しない場合
4
= fizz
, 6
= buzz
のときは24
= fizzbuzz
とはなりません。これは4, 6
の最小公倍数が24
ではなく12
だからです。
問題: 数値がわかりにくい場合
20003
= fizz
, 30011
= buzz
の場合??
= fizzbuzz
は何になるでしょうか?このときも検証が必要になります。
これらを踏まえると成果物案2. のほうが変更に強そうです。
4. 新しい条件が追加されたら ?
4
= dazz
という新しい条件が追加されたらどうなるでしょうか?成果物案1. 2.ともにかなり辛い目に合うでしょう。
何も考えなければ成果物案1'. 2'.はこうなるでしょう。
成果物案 1'.
const fizzbuzz = (num: number): string => {
if (num % 60 === 0) {
return 'fizzdazzbuzz';
}
if (num % 20 === 0) {
return 'dazzbuzz';
}
if (num % 15 === 0) {
return 'fizzbuzz';
}
if (num % 12 === 0) {
return 'fizzdazz';
}
if (num % 5 === 0) {
return 'buzz';
}
if (num % 4 === 0) {
return 'dazz';
}
if (num % 3 === 0) {
return 'fizz';
}
return `${num}`;
};
成果物案 2'.
const fizzbuzz = (num: number): string => {
if (num % 3 === 0 && num % 4 === 0 && num % 5 === 0) {
return 'fizzdazzbuzz';
}
if (num % 4 === 0 && num % 5 === 0) {
return 'dazzbuzz';
}
if (num % 3 === 0 && num % 5 === 0) {
return 'fizzbuzz';
}
if (num % 3 === 0 && num % 4 === 0) {
return 'fizzdazz';
}
if (num % 5 === 0) {
return 'buzz';
}
if (num % 4 === 0) {
return 'dazz';
}
if (num % 3 === 0) {
return 'fizz';
}
return `${num}`;
};
まだ数値以外の出力の条件が3個しかないからこれだけで済みましたが、これ以上の場合わけを考えたくはなくなります。
回答案
わたしの回答案は以下です。
const fizzbuzz = (num: number): string => {
const arr: Array<string> = [];
if (num % 3 === 0) {
arr.push('fizz');
}
if (num % 5 === 0) {
arr.push('buzz');
}
if (arr.length === 0) {
return `${num}`;
}
return arr.join('');
};
これは、条件を満たしたときにその文字列を配列に保持し、配列にひとつでもそれらが入っていればstring
として連結して返す。そうでなければ数値自体をstring
にして返す。という作りになっています。以下の特徴をもっています。
1. fizz, buzz
の条件が変更されても修正箇所は1箇所
fizz, buzz
の値が変更されると成果物案は1. 2.ともに2箇所以上の変更が生じますが、これは1箇所です。
2. 20003
= fizz
, 30011
= buzz
、じゃあ??
= fizzbuzz
を考える必要がない
これは成果物案2.も同様に考える必要がないので、そこまで特筆する特徴ではありませんが、記します。
3. 4
= dazz
1箇所に差し込むだけです。
const fizzbuzz = (num: number): string => {
const arr: Array<string> = [];
if (num % 3 === 0) {
arr.push('fizz');
}
// -> HERE
if (num % 4 === 0) {
arr.push('dazz');
}
// <- HERE
if (num % 5 === 0) {
arr.push('buzz');
}
if (arr.length === 0) {
return `${num}`;
}
return arr.join('');
};
組み合わせの爆発を考える必要はありません。