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
これはこれでまた読み込まないと意味がわからない!!
もっともぬるいFizzBuzz
FizzBuzzというのは、「どうしてプログラマに・・・プログラムが書けないのか?*1」の記事に出てくる、プログラマ面接のテスト問題です。
1〜100の数字に対して、3で割り切れる数なら"Fizz"、5で割り切れる数なら"Buzz"、3と5の両方で割り切れる数なら"FizzBuzz"、どれにもあてはまらなければ単にその数字をプリントアウトするというもの。
これが書けないのは本当にプログラムを書いたことがないのだろう、という程度の問題なので、腕におぼえのある人は「いかに短いコードで書くか」を競う、コードゴルフのゲームに走るわけです。
しかし上のエントリにも書いたように、文法知識と発想力を駆使したコードは本物の呪文と化します(笑)
正攻法で書いたFizzBuzzのコードがあれば、コードゴルフの面白みが少しは伝わるでしょうか?
for i in 1..100 if i%3 == 0 print "Fizz" end if i%5 == 0 print "Buzz" end unless i%3 == 0 or i%5 == 0 print i end puts end
12行162バイト。たとえば1行目のfor i in 1..100を、(1..100).each とか、1.upto(100) とか書き換えるとちょっと短くなるわけです。
if文とunless文の連続はif ... elsif ... else と直したくなりますが、そうすると15の倍数でFizzBuzzが出ませんね。
この節を2行にまとめるのはわりと簡単です。
for i in 1..100 s = "#{'Fizz' if i%3 == 0}#{'Buzz' if i%5 == 0}" puts (s == "")? i : s end
埋め込み文字列と三項演算子でぐっと短くなりましたが、なにをやってるかはかなりわかりにくくなりました(笑)
もう一工夫するとこの節は1行にできます。
for i in 1..100 puts ( (s = "#{'Fizz' if i%3 == 0}#{'Buzz' if i%5 == 0}") == "")? i : s end
sを代入しながら空文字列判定しています。カッコが多くてわけわかりません。
がんばって全部1行にまとめてみました。
1.upto(100){|i|puts(((s="#{i%3==0&&:Fizz}#{i%5==0&&:Buzz}")=="")?i:s)}
動きそうですがダメです。Fizz(Buzz)にならない部分が全部Falseになってしまいます。
そろそろ眠いのでおしまい。これをやるにはRDEの方が便利かも。