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

forもwhileもifも剰余算も使わないJavaScriptのFizzBuzz、byte数徹底して減らしました

javascript programming

先日のネタ的Pythonの解答
http://d.hatena.ne.jp/xorphitus/20120411/1334147796
とは趣を変えて、直球勝負してみたJavaScriptのコードです。

まずは問題をおさらい。

問題:下記1〜4の制約それぞれについてFizzBuzz問題を解け
JavaScript (ECMAScript 5th) および Python (2.6 or higher) のコードを提出すること

1. for/whileループ禁止
2. 剰余算禁止
3. if/switch分岐禁止 (三項演算子もダメ)
4. 文の数を徹底的に減らせ

徹底的に減らそうと努力したのは、文の数ではなくbyte数ですが。

(new Array(100+1)).join('0').split('').map(function(i,j){m=function(p,q){return p-Math.floor(p/q)*q;};console.log(!!(m(++j,3)*m(j,5))*j||((m(j,3)||'Fizz')+(m(j,5)||'Buzz')).replace(/\d/,''));});

Array + 無名関数、というのが常套手段な気がします。再帰でループしようものならコード量が増えてしまいますので。
ただJSの場合はArrayの作成が面倒です。例えばPythonならrangeで簡単に作れるのですが…。
最初に試みたコードは

(new Array(100)).forEach();

だったのですが、これは配列の中身がトラバースされません。
ところがjoinはできるんですね。

(new Array(100+1)).join('0');

あとは空文字列でsplitしてやれば、任意のサイズのArrayができあがります。なお、joinで文字数がArrayのサイズより1つ小さくなるので1を足しておきます。

次にArrayのトラバースについてですが、意味としてはforEachが最も適切ではありますが、正直、filterやmapでもいいので最も文字数が少ないmapを用います。無名関数の第二引数がインデックスとなります。

無名関数内ですが、まず剰余算をする関数を定義しておきます。

m = function(p, q){return p - Math.floor(p / q) * q;};

ささやかな可読性のためにホワイトスペースを入れました。
何度かコールするので、これによってbyte数が削減できます。なお、varの宣言はこのコードにおいて何の意味もなさないのでこれもカットします。

あとは数値と真偽値の型変換をうまくつかってゴリゴリします。!!で数値は真偽値になり、積算すると真偽値は0/1として扱われます。

ただ、最後のreplaceがちょっとカッコ悪いですね。

追記: 記事をsubmitしてから気づいた

byte数削減狙いだったら、変数の使い方を変えて

(new Array(100+1)).join('0').split('').map(function(i,j){r=++j-Math.floor(j/3)*3;s=j-Math.floor(j/5)*5;console.log(!!(r*s)*j||((r||'Fizz')+(s||'Buzz')).replace(/\d/,''));});

の方が短い!
タイトルがちょっと恥ずかしくなりました…。