FizzBuzzのしくみ

id:yoosaki:20070510#p1
いくら見ても意味がわからないので、irbで動かしてみた。
(コーディング環境はeclipseに移したけれど、こういうときに役に立つからEmacsが捨てられない)

肝はここ。

s=[[:Fizz][i%3],[:Buzz][i%5]]*''

:Fizz は 'Fizz' をシンボルで書いて一文字短縮したとわかる*1
わからんのが[:Fizz][i%3]と、最後の*''。
調べてみたら、RubyではArrayに文字列をかけるとArray#joinと同じ効果があるとわかった。
[1,2,3].join(';') = "1;2;3"
すなわち
[1,2,3]*';' = "1;2;3"
というわけ。

さて問題は[:Fizz][i%3]ですよ。
「配列と配列を並べて書くとどう動作する?」とか悩んでしまいましたがまったくの無駄で、[i%3]は[:Fizz]に対する添字です。

i%3はiのあまりなので、iが1,2,3,4,5,6,……と変化すると、i%3は1,2,0,1,2,0,……となる。
すなわち

[:Fizz][0] = :Fizz
[:Fizz][1] = nil
[:Fizz][2] = nil

というわけで、この式はiが3で割り切れるときのみ:Fizzを返すわけです。

Buzzについても同様で、肝の式の前半は

[nil, nil]
[:Fizz, nil]
[nil, :Buzz]
[:Fizz, :Buzz]

の4つの値をとりうることになる。
これを''でjoinするから、sの値は'', 'Fizz', 'Buzz', 'FizzBuzz'のいずれか。

てことはs[1]はnilか?iか?uになる。s[1]がnilのときだけiを返すため、最後はs[1]?s:iとなる寸法です。(s[0]を見ても可っぽい)

こういう遊びをコードゴルフというそうです。
プログラムの文字数を打数に見立て、文法知識と発想力を駆使して一文字でも短く書いて行くわけです。

ちなみにこれより短い56バイトの例がすでにここに。
id:yoosaki:20070516#p1
これはこれでまた読み込まないと意味がわからない!!

*1:厳密には文字列とシンボルは異なるけど、ここではその差異は無意味。さてASAくん、Rubyの文字列とシンボルの違いを説明できるかね?