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

Ba.とWEBと梅干し太郎

WEB制作・プログラミング・音楽の学習したこと・つくったもののアウトプットブログです。あとは日記。

10秒で解読させないFizzBuzzを書いてみた

配列を制すための第一歩(のつもり)。

言語はPHPです。

まずは10秒で処理を読ませないことをテーマにfizzbuzzを書いてみました。

なお、あからさまに処理をぼかし過ぎて、先輩に意気揚々と見せたらすぐ処理バレしました。

<?php
function decision($num){
$text_1 = "fibu";
$text_2 = "zz";
$contents = str_split($text_1, 2);

$array_1 = $contents[0];
$array_2 = $contents[1];
preg_match("{(.{4})(.{4})}", "{$array_1}{$text_2}{$array_2}{$text_2}", $matches);

$result = array_sum(array_map("intval", str_split((string)$num, 1)));
if (in_array($result, [3,6,9,12,15,18], true) && preg_match("{\d*[05](?!\d)}", $num)) {
    $decision_result = 0;
  } elseif (in_array($result, [3,6,9,12,15,18], true)) {
    $decision_result = 1;
  } elseif (preg_match("{\d*[05](?!\d)}", $num)) {
    $decision_result = 2;
  }
  return @$matches[$decision_result]?:$num;
}

function fizzbuzz($curry, $num){
  echo decision($num), PHP_EOL;
}

array_reduce(range(1, 100), fizzbuzz);

解説です。
decisionメソッドではfizzbuzz判定をしています。
処理をぼかすために意識したのは、ずばりfizzbuzzの文字を見せないこと
というわけで、'fizzbuzz'文字列の共通項を切り貼りして出力したい3つの文字列(fizz, buzz, fizzbuzz)をそれぞれ作り、配列に格納する処理にしました。
無駄な処理はないけど、解読しづらいはずです。笑

str_splitを使って、text_1に代入している'fibu'を半分ずつ'fi'と'bu'に分けて配列にし、再度変数へ代入しています。
参考:PHP: str_split - Manual
そして、preg_matchで各パーツから,'fizz', 'buzz', 'fizzbuzz'という3パターンにマッチする正規表現を指定し、その結果を$matchesに格納しています。

array_sum以降で意識しているのは剰余算を使わないことです。
勉強した正規表現を使おう。と思いたった結果、こんな処理に。

<?php
$result = array_sum(array_map("intval", str_split((string)$num, 1)));
if (in_array($result, [3,6,9,12,15,18], true) && preg_match("{\d*[05](?!\d)}", $num)) {
    $decision_result = 0;
  } elseif (in_array($result, [3,6,9,12,15,18], true)) {
    $decision_result = 1;
  } elseif (preg_match("{\d*[05](?!\d)}", $num)) {
    $decision_result = 2;
  }
  return @$matches[$decision_result]?:$num;
}

見ての通り、5の倍数は正規表現を書けましたが、どうがんばっても3の倍数だけを弾く正規表現ができませんでした。 代替案として、各数字の各位の和が3の倍数に一致するものを弾く処理にしました。これは、3の倍数の簡単な判別方法として有名ですね。
各位の和の処理にはarray関数を使いました。
関数型プログラミングに憧れています。

<?php
$result = array_sum(array_map("intval", str_split((string)$num, 1)));

配列操作に超便利なarray関数の各処理の詳細は公式マニュアルを参照。
参考:PHP: array_map - Manual
array_mapに渡しているintvalはこちら。
参考:PHP: intval - Manual

上記の処理でfizzbuzzを判定し、結果を$decision_resultに代入します。if文の判定に応じて、$matchesに格納されているfizzbuzz達にアクセスしfizz、buzz、fizzbuzz、もしくは数字をそのまま返します。 後はそれを呼びたして完成!
呼び出しにもarray関数は多いに役立ちました。

<?php
function fizzbuzz($curry, $num){
  echo decision($num), PHP_EOL; //fizzbuzz判定をして出力
}

array_reduce(range(1, 100), fizzbuzz);

複雑なコードを書いた後は、、、。やっぱり最短チャレンジでしょ !!
はじめ、三項演算子では80byteを切れなくて悩んだので、配列を使うことにしました。
上同様に、計算結果で配列のアクセスを変えて出力できないかを模索した結果↓。

58byteワンライナーで処理書けました。
角括弧構文で配列を作成し、剰余算の計算結果に応じて文字列を出力しています。

この書き方でも処理できちゃう理由はこちら。 f:id:masayannuu:20160712002454p:plain
参考: PHP: 配列 - Manual

リファレンスにもありますが、角括弧による配列の作成はアクセス方法とかぶるので推奨されていません。
バージョンによっては、エラー制御演算子をつけないと「未定義の配列がいるよ!!」と怒られます、、(でもちゃっかり処理はしてくれる)。 また、phpの開始タグも省略していますが、xmlの記述と混同しエラーの原因になるので本来は省略しない方が良いです。

※開始タグ省略の設定方法メモ
phpの開始タグの設定はphp.ini内のshort_open_tagをonにすればOK。

short_open_tag = on

php.iniが見つからなければphp -iをgrepに渡して検索 。

php -i | grep php.ini

最短チャレンジで人と勝負するのも楽しかったです。
変わった処理を考えると勉強になるので、新しい言語を覚える度にやってみようかな 。