ES2015時代のFizzBuzzに挑戦する

  • 9
    Like
  • 0
    Comment
More than 1 year has passed since last update.

今更ながらFizz Buzzコードゴルフにハマってしまいました。

というのも、ECMAScript2015ならもしかしたら今までのJavaScriptより短く書けるのではないか?と思ったからです。

独自の調査で見つけた、一番短いJavaScriptのFizz Buzzは以下の62文字のコードです。

fizzbuzz_js5_shortest.js
for(i=0;++i<101;console.log(i%5?x||i:x+'Buzz'))x=i%3?'':'Fizz'

さて、ES2015はこの限界を越えられるのか!?

アロー関数、fill

従来の記法ですが、

Array(100)

で100個の要素を持つ空配列を生成できます。

この配列をforEachを回す作戦です。これで新機能アロー関数を使った

Array(100).forEach(elm=>console.log(elm))

のような書き方が可能となります。

しかし、このコード、実行しても反応がない…。どうやら、空要素はforEachに反応しないようです。

ここで新機能fillの登場です。

Array(100).fill(0).forEach(elm=>console.log(elm))

これで配列が0で初期化され、0が100個出力されます。

forEachのコールバックの第2引数にindexが入るので、それを利用して

fizzbuzz_fill_1.js
Array(100).fill(0).forEach((_,i)=>console.log((++i%3?'':'Fizz')+(i%5?'':'Buzz')||i))

でどうでしょう。

ただ、コールバック引数(_,i)が長いので、fillのところで意味のない数(0)を使ってるところを変数初期化に使ってみましょう!

fizzbuzz_fill_2.js
Array(100).fill(i=0).forEach(_=>console.log((++i%3?'':'Fizz')+(i%5?'':'Buzz')||i))

変化球で、別の方法で長い配列(中身はどうでもいい)を取得する方法を編み出しました。
windowオブジェクトのキーを配列として利用するのです。

keys(this).forEach((_,i)=>console.log((++i%3?'':'Fizz')+(i%5?'':'Buzz')||i))

グローバルオブジェクトがwindowなら、keys(this)windowのプロパティ名の配列になります。
(昨日Chromeでやったときとなぜか挙動が異なるので、正式なES2015ではどうなるか未検証です。)

新しいオブジェクトリテラル、テンプレート文字列

これは自分の中では努力賞です。

新機能でオブジェクトリテラルの省略表記が追加され、

var a = 5, b = 3, c = 8;
var obj = {
  a: a,
  b: b,
  c: c
}

のようにキーと値の変数名が同じ場合に

var a = 5, b = 3, c = 8;
var obj = {
  a,
  b,
  c
}

のように書けることに目をつけました。

もうひとつ、新機能テンプレートリテラルで

console.log(`hoge${1}`); // => 'hoge1'

のような式の参照ができるようになったのも何か使えそうだと思いました。

そこで、コンボ技を使ったのがこれです。

fizzbuzz_template.js
_1='Fizz';_2='Buzz';_3=_1+_2;for(i=0;++i<101;)console.log({_1,_2,_3}[`_${!(i%3)+!(i%5)*2}`]||i)

_1, _2, _3と名付けた3つの変数をオブジェクトリテラルでオブジェクト生成し、
ループ内で`_${1}`,`_${2}`,`_${3}`のどれかがキーとなったときは
'Fizz', 'Buzz', 'FizzBuzz'のどれかが選ばれ、
それ以外はオブジェクトのプロパティ参照でundefinedが返されるので||i(index)の方に逃がしてやります。

我ながら秀逸ですが、宣言部分が長すぎて対していまいち短縮化できませんでした。

他にも頑張った

文字列をevalしてみるとか。

fizzbuzz_eval.js
f='Fizz';b='Buzz';fb=f+b;for(i=0;++i<101;)console.log(eval('f  '[i%3]+'b    '[i%5])||i)

3で割った余りと5で割った余りの除算(0/1 === 0, 1/0 === Infinity, isNaN(0/0) === true)を分岐させるとか。

fizzbuzz_number.js
o={0:'Fizz',Infinity:'Buzz',NaN:'FizzBuzz'};for(i=0;i<100;)console.log(o[(++i%3)/(i%5)]||i)

自己ベスト

結局ES2015記法ではないのですが、以下のコードが最終的な自己ベストでした。

fizzbuzz_my_best.js
for(i=0;++i<101;)console.log((['Fizz'][i%3]||'')+(['Buzz'][i%5]||'')||i)

配列マジックを駆使して、最短記録を10文字オーバーした72文字。

まあ、最短記録のコードゴルフを見れば見るほど無駄の無さが際立つので、思いっきり裏をかけない限りは更新不可能でしょうね…。

for(i=0;++i<101;console.log(i%5?x||i:x+'Buzz'))x=i%3?'':'Fizz'

ほら、無理。