PHPでFizzBuzzってみる(59Byte)

今日はPHPの入門ハンズオン勉強会を主催してまいりました(と言っても仲間うちですけど)。
結構使ってるよ、という方から、全くPHP使ったことないよ(但し別言語での開発経験はある)、という方まで5人相手に「FuelPHP入門ハンズオンのためのPHP5.3基礎講座」といった内容でハンズオン形式の勉強会にしてみました。
Googleドキュメントで解説しながらリアルタイムコーディングしつつ、参加者の方が各々タイプしつつ学ぶ形式でやりました。
初心者の方も最後まで脱落せずついてこられたようで良かったです。

あ、FuelPHP入門ハンズオン、ただいま参加者募集中です。興味ある方は是非。


…で、その後なぜか一人、PHPFizzBuzz問題を突き詰めるトレーニングを開始。
おかしい。もっと他にやるべきことがあったはずなのに… orz

FizzBuzz問題についてはこちらを参考に。

という事で、最初に書いたコード。はじめは無名関数の動作確認をしていたはずなのですが…

<?php
$fizzbuzz = function($val) {
  $return = '';
  if ($val % 3 == 0) $return .= 'Fizz';
  if ($val % 5 == 0) $return .= 'Buzz';
  return $return ? : $val;
};
PHPでは空文字でない場合はtrueとして評価されることを利用し、「Fizz」「Buzz」「FizzBuzz」いずれかがセットされていれば文字列を、空文字の場合は引数の数値を返り値とするように記述。

for($i;$i<=100;$i++) {
  echo $fizzbuzz($i) . ' ';
}

まず、変数名を削ってみる。

<?php
$f = function($v) {
  $return = '';
  if ($v % 3 == 0) $r .= 'Fizz';
  if ($v % 5 == 0) $r .= 'Buzz';
  return $r ? : $v;
};

for($i;$i<=100;$i++) {
  echo $f($i) . ' ';
}

別に無名関数いらないか…と、何故か文字数を切り詰める方向へ暴走。

<?php
for($i=1;$i<=100;$i++) { echo (((($i % 3 == 0) ? 'Fizz' : '') . (($i % 5 == 0) ? 'Buzz' : '')) ? : $i) . ' '; }

スペースも切り詰め始める。

<?php
while($i<100){$i++;echo((($i%3==0)?'Fizz':'').(($i%5==0)?'Buzz':''))?:$i,' ';}

自分の今の知識ではここが限界だなー、ということで他サイトを参考にしてみる。
そうか…56byteまで切り詰められるのか、と思ったら別言語でのハナシじゃないですか。
実際には59byteなのね、ということで参考にして切り詰める。
そういえばショートタグって手があったな…。

<?for(;$i++<100;)echo(($i%3==0?'Fizz':'').($i%5==0?'Buzz':''))?:$i,' ';


途中の三項演算子の評価対象をひっくり返す。PHPでは0でない数値はtrueとして扱われることを利用。
さらに「未定義の定数は値がその文字列自身となる」というバッドノウハウを利用しての最終結果。

<?for(;$i++<100;)echo($i%3?'':Fizz).($i%5?'':Buzz)?:$i,' ';

whileにしても、文字数は変わらず。

<?while($i++<100)echo($i%3?'':Fizz).($i%5?'':Buzz)?:$i,' ';

三項演算子に絡むカッコが外せないのが悔しいですが、三項演算子の無茶な入れ子がない分、参考にしたサイトよりも若干動作が予測しやすくなっているのではないでしょうか。
アプローチが変わると最終結果も変わってくることが分かって面白かったです。
byte数は参考元のid:milieuさんと同じく、59byteとなりました。
「分かりやすく」の観点だったらダントツだと信じたいです。都市伝説のように散見される56byteってどんなコードなんだろ。

動作についてですが、PHP5.3環境ではショートタグの問題で動作が怪しいです。PHP5.4だと問題なく動作します。
# いかんせんバッドノウハウの塊なのであまり胸は張れない…