fizzbuzzについて思うこと

`fizbuzz`という有名な問題があります。おさらいすると以下のような条件をみたすプログラムを準備するものです。

* `3`で割り切れるときは`fizz`と出力する
* `5`で割り切れるときは`buzz`と出力する
* それ以外はその数字を出力する

以下では **`z`で割り切れるときは`zzzz`** のことを

> `z` = `zzzz`

とも表現しますので一時的に記憶をお願いします。

## 一般的に

このような成果物が一般的でしょう。

### 成果物案 1.

```typescript
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.

```typescript
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}`;
};
```

1. は`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'.

```typescript
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'.

```typescript
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個しかないからこれだけで済みましたが、これ以上の場合わけを考えたくはなくなります。

## 回答案

わたしの回答案は以下です。

```typescript
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箇所に差し込むだけです。

```typescript
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('');
};
```

組み合わせの爆発を考える必要はありません。