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('');
};
組み合わせの爆発を考える必要はありません。