FizzBuzz問題(Ruby)

FizzBuzz 問題」という有名な問題があることを知りました。
Fizz Buzz - Wikipedia

Wikipedia から引用すると、

プレイヤーは円状に座る。最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は 「Fizz」(Bizz Buzz の場合は 「Bizz」)、5で割り切れる場合は 「Buzz」、両者で割り切れる場合は 「Fizz Buzz」 (Bizz Buzz の場合は 「Bizz Buzz」)を数の代わりに発言しなければならない。発言を間違えた者や、ためらった者は脱落となる。

これをプログラミングするということですね。

別に問題はないでしょう。例えば

for i in 1..50
  if i % 15 == 0
    print "Fizz Buzz, "
  elsif i % 3 == 0
    print "Fizz, "
  elsif i % 5 == 0
    print "Buzz, "
  else
    print "#{i}, "
  end
end

でいいですね。結果は

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17,
Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32,
Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, Fizz Buzz, 46, 47,
Fizz, 49, Buzz,

となります。ただ、これも Wikipedia によると、「2分以内に解け」とか「%(剰余)演算子を使うな」とか「ワンライナーで」とか、色々条件をつけることがあるそうです。そうかあ。逆にできるだけ凝るとかね。プログラマーの数だけ解法があるとか何とか。

剰余演算子(%)を使わない場合

考えてみました。でも、ただ単に割り切れるかどうかを判定するメソッドを作っただけ。

class Integer
  def divisible?(x)
    (self - self.div(x) * x) == 0
  end
end

for i in 1..50
  if i.divisible?(15)
    print "Fizz Buzz, "
  elsif i.divisible?(3)
    print "Fizz, "
  elsif i.divisible?(5)
    print "Buzz, "
  else
    print "#{i}, "
  end
end


※追記 これ、おもしろかった。プロで書けないとか、何ですかそれは。そういうこともあるのかあ。


※再追記(2018/2/4)
Ruby 的にもっとも素直に書くとこんな感じ?

1.upto(50) do |i|
  st = if i % 15 == 0
    "FizzBuzz, "
  elsif i % 3 == 0
    "Fizz, "
  elsif i % 5 == 0
    "Buzz, "
  else
    "#{i} ,"
  end
  print st
end

 

※再々追記(2018/2/5)
この方が Ruby っぽいか。

def trans(i)
  if i % 15 == 0
    "FizzBuzz"
  elsif i % 3 == 0
    "Fizz"
  elsif i % 5 == 0
    "Buzz"
  else
    i.to_s
  end
end

p [*1..50].map{|i| trans(i)}

最後をこう書けたらいいのになあ…

p [*1..].map {|i| trans(i)}.take(50)

いや、しかしこう書けるのか。

p 1.step.lazy.map{|i| trans(i)}.first(50)

うーむ、lazy…。
 

※再再再追記(2019/12/27)
case ~ when を使って。

fizzbuzz =
(1..50).map do |i|
  case
  when (i % 15).zero?
    "FizzBuzz"
  when (i % 3).zero?
    "Fizz"
  when (i % 5).zero?
    "Buzz"
  else
    i.to_s
  end
end

p fizzbuzz

基本か。