読者です 読者をやめる 読者になる 読者になる

めもりーあろけーと

技術的な話とか日常的な話とか。

PHPでFizzBuzzゴルフの話。

FizzBuzzは簡単に言うと1<=N<=100 (100は場合によってかわる)の時に、Nが3で割り切れる数はFizz、5で割り切れる数はBuzz, 15で割り切れる数はFizzBuzzと出力するベーシックなものです。

普通にやるとパッとコードが出てきます。例えば、下記が基本です。

<?php
for ($i = 1; $i <= 100; $i++) {
    if (($i % 15) == 0) {
        echo 'FizzBuzz' . "\n";
    } else if (($i % 3) == 0) {
        echo 'Fizz' . "\n";
    } else if (($i % 5) == 0) {
        echo 'Buzz' . "\n";
    } else {
        echo $i . "\n";
    }
}

だれでもできる、単純なロジックです。

さて、FizzBuzzゴルフですが、ゴルフはコードの量を極限までに減らすものとなります。 上記のコードの場合、単純に考えるとこうなるでしょう。

<?php
for ($i=1;$i<=100;$i++)echo(($i%15)==15)?'FizzBuzz':(($i%3) == 0)?'Fizz':(($i%5)==0)?'Buzz':$i)."\n";

これは通常のFizzBuzzゴルフとなりますが、いくつか縛りを交えて、ゴルフをするともっと人生豊かになります✨

よくあるのが, %(剰余演算子)の使用禁止, if-elseif-elseの禁止などです。 今回、私が挑戦したのはif-elseif-elseです。

FIzzBuzz

<?php
for($s=str_replace,$o=array_search;++$i<100;){$k=$s(0,Fizz,$o($i%3,[0])).$s(0,Buzz,$o($i%5,[0]));echo$s(0,$k,$s(1,$i,empty($k)*1))."\n";}

こうなります。ギリギリTweetできるサイズです🐱✋ このままだとわからないので、わかりやすいように整形してどういう風に動いているか説明します。

<?php
for($str_replace = str_replace, $array_search = array_search; ++$i<100;){
    $replaced = $str_replace(0, 'Fizz', $array_search($i % 3,[0])) . $str_replace(0, 'Buzz', $array_search($i % 5, [0]));
    echo $str_replace(0, $replaced, $array_search(1, $i, empty($replaced) * 1)) . "\n";
}

for文

まずfor文の箇所から。 $str_replace = str_replace, $array_search = array_search ですが、forの初期化段階の際に、多用するため、コールバック関数として、それぞれ変数に格納しています。

++$i<100

上記 は前置インクリメントですが、処理の前に$iを+1とします。PHPは初期化されていない変数にインクリメントを使用すると、0からの扱いになるので、それを利用しています。 この場合は 1<100 となります。後置インクリメントだと 0<100での比較となります。

Fizz, Buzzへの置換

次に

$replaced = $str_replace(0, 'Fizz', $array_search($i % 3,[0])) . $str_replace(0, 'Buzz', $array_search($i % 5, [0]));

行が長いので$str_replace(0, 'Fizz', $array_search($i % 3,[0])) を説明します。 これは、array_searchの特性を活かした処理にになります。array_searchは見つかればそのインデックス番号、見つからなければfalseが返ります。したがって、$iが3で割り切れれば0, それ以外はfalseとなります。そして、見つかった場合、str_replaceでインデックス番号0を'Fizz'に置換しています。見つからなければfalseのままです。$str_replace(0, 'Buzz', $array_search($i % 5, [0])) も3が5になっただけで同様です。上記の結果を結合し、$replacedに格納しています。

数字への置換

最後の

echo $str_replace(0, $replaced, $array_search(1, $i, (int) empty($replaced))) . "\n";

まずは、$array_search(1, $i, (int) empty($replaced)) の説明をします。 (int) empty($replaced)で、0か1かの判定を行っています。PHPのemptyの特性として、nullや0, falseはfalseが返ってきます。したがって上記で置換されなかったものはfalseとなり、見つかっている場合はtrueとなります。str_replaceの性質上どうしてもtrue/falseは変換できないので、intにキャストし、trueを1, falseを0としておいています。 つまり何も存在しない場合(1である場合), $iに変換する仕掛けになっています。

最後に

FizzBuzzゴルフは考え方の問題なので、もっと短くできるかもです。(前のブログにもっと短いのあった気がする…)

他のロジックとしてはdecbinで変換し、 0011, 1010, 1111のように2進数で考える方法などもありますね。

楽しいのでぜひやってみてください🐱✨