FizzBuzzの最短コードの解析

前の記事FizzBuzzの最短コードを紹介しました。
一見よくわからない変態コードですが、詳しく見るとそんなに難しい訳じゃなかったです。

問題のソースコード
http://d.hatena.ne.jp/y_tag/20110125/fizzbuzz

100.times{println'Fizz'*(it%3/2)+'Buzz'*(it%5/4)?:++it}

実行結果

1
2
Fizz
4
Buzz
Fizz
... 中略...
98
Fizz
Buzz

このままだとにくいので、変数作って三項演算子を普通のfor文にして、インデントしてから解析します。

100.times {
  def str = 'Fizz'*(it%3/2)+'Buzz'*(it%5/4)
  if (str) {
    println str
  } else {
    println (++it)
  }
}

1行目
「100.times」
0〜99を順に、後のクロージャーに渡す。
(実行結果は1から100までの結果が出力されている事に注意。)

2行目
長いので少しづつ。
「'Fizz'*(it%3/2)」
「*」は文字列の繰り返しなので、「it%3/2」の数だけ繰り返す。
「it%3」は余りなので今回の場合
「0%3/2, 1%3/2, ... 99%3/2, 100%3/2」 → 「0, 0.5, 1」の繰り返し → 「"","","Fizz"」の繰り返し

同様に、Buzzの方は
「0, 0.25, 0.5, 0.75, 1」 → 「"","","","","Buzz"」の繰り返し

ということで、2つを「+」で繋げると
「"","","Fizz","","Buzz"...」となる。

表にしてみるとこんな感じ。

文字列
0 ""
1 ""
2 "Fizz"
3 ""
4 "Buzz"
5 "Fizz"

3行目
「if (str)」
Groovyではifに文字列を渡すと空文字やnullの場合はfalseそれ以外はtrueを返す。

4行目
「println str」
という事なので、文字列がある場合はその文字列を表示

6行目
「println (++it)」
空文字の場合は渡された値に1をプラスして表示。

こんな感じみたいです。

1〜100を表示したいのに0〜99で処理を行っているのがポイント!
余りが0の時に表示するのではなく、余りが最大の時に表示するとかテクニックですねぇ。。。

ちなみに上記のGroovy最短コードは55バイトなんですが、rubyだと最短は56バイトらしいです。
なんかちょっと嬉しいのは私だけですか?

http://d.hatena.ne.jp/shinichiro_h/20070509#1178693484



他にも色々見つけたのでGroovy風にして書いてみました。

正規表現
http://jarp.does.notwork.org/diary/200705b.html#200705111

f={n,i,s->"#"*n=~/^(${"#"*i})+$/?s:""}
(1..100).each{println((s=f(it,3,"fizz")+f(it,5,"buzz"))?s:it)}

そんなこんなで、色々見た結果自力で頑張ってここまで縮めてみた。
わかりやすい、理解しやすいところだとこれが限界かな??><

(1..100).each{println((s=(it%3==0?"fizz":"")+(it%5==0?"buzz":""))?s:it)}

ちなみに、prinalnが冗長に感じたので削ってみたら長くなっちゃいましたw

(1..100).collect{((s=(it%3==0?"fizz":"")+(it%5==0?"buzz":""))?s:it)}.join("\n")