RubyでFizzBuzz(いろいろな書き方)

FizzBuzz問題という有名なプログラムを書いてみたいと思います。いろいろな書き方があり全ては紹介できませんが、いくつかやってみます。

FizzBuzz

1から100までの数字を順番に出力するプログラムです。ただし、3の倍数のときは数字の代わりに「Fizz」、5の倍数のときは「Buzz」、3と5両方の倍数のときは「FizzBuzz」と出力してください。

プログラムの実行結果は以下のようになります。

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16

# 中略

98
Fizz
Buzz

プログラムをどこに書けばいいかわからないという人は、paiza.IOを使うのが簡単だと思います。

書き方の例

では順番にやっていきます。(1)1から100までの数字を繰り返し出力する部分 と、(2)条件によって出力を変える部分 の2つに分けて考えてみます。

(1)繰り返し処理

まず、単純に1から100の数字を出力するにはどう書けばいいでしょうか。

繰り返し処理するメソッドが使えそうです。繰り返しといえば、timeseachあたりが思いつきます。

(1 - 1) timesを使う
n = 1  # 変数nに1を代入
100.times do  # do〜endに書いた処理を100回繰り返す
  puts n  # nを出力
  n += 1  # n + 1をnに代入(nの値を1増やす)
end
(1 - 2) eachを使う
(1..100).each do |n|  # 1〜100の各数字に対して、do〜endに書いた処理を行う
  puts n  # nを出力
end

timesでもeachでも実行結果は以下のようになります。

1
2
3
4
5
# 以下略(〜100まで出力される)

ちなみに(1..100)は、Rangeオブジェクトといって「範囲」を表します。

(1..100)(ドットが2つ):1以上100以下

(1...100)(ドットが3つ):1以上100未満

(2) 条件によって処理を変える

Fizz」、「Buzz」、「FizzBuzz」を出力させるには条件によって処理を分ける必要がありそうです。条件式で分岐させるものといえば、ifcaseが有名です。

(2 - 1) ifを使う
if n % 3 == 0 && n % 5 == 0  # nを3で割った余りが0かつ5で割った余りが0のとき
  puts "FizzBuzz"  # FizzBuzzを出力
elsif n % 3 == 0  # nを3で割った余りが0のとき
  puts "Fizz"  # Fizzを出力
elsif n % 5 == 0  # nを5で割った余りが0のとき
  puts "Buzz"  # Buzzを出力
else    # それ以外のとき
  puts n  # nを出力
end
(2 - 2) caseを使う
case 
when n % 3 == 0 && n % 5 == 0  # nを3で割った余りが0かつ5で割った余りが0のとき
  puts "FizzBuzz"  # FizzBuzzを出力
when n % 3 == 0  # nを3で割った余りが0のとき
  puts "Fizz"  # Fizzを出力
when n % 5 == 0  # nを5で割った余りが0のとき
  puts "Buzz"  # Buzzを出力
else    # それ以外のとき
  puts n  # nを出力
end

一つ目の条件式では&&を使いました。これは「かつ」という意味です。「nを3で割った余りが0、かつnを5で割った余りが0」という意味になります。これはn % 15 == 0としても同じです。

ifcaseを使った書き方はそれほど大きく変わりませんね。どちらでもいいと思います。

条件式の順番に注意

条件分岐のポイントは、条件式の順番です。プログラムは基本的に上から順に実行されます。

たとえば、以下のように「3の倍数かつ5の倍数」の式を後ろに置いてしまうと、「3の倍数」は全て「Fizz」が出力され、「FizzBuzz」が出力されることはなくなってしまいます。その点caseなら安心…かと思いきや、caseでも同様でした!

if n % 3 == 0
  puts "Fizz"
elsif n % 5 == 0
  puts "Buzz"
elsif n % 3 == 0 && n % 5 == 0  # 実行されることはない
  puts "FizzBuzz"
else
  puts n
end
case 
when n % 3 == 0
  puts "Fizz"
when n % 5 == 0
  puts "Buzz"
when n % 3 == 0 && n % 5 == 0  # 実行されることはない
  puts "FizzBuzz"
else
  puts n
end

実行結果

1
2
Fizz
4
Buzz
Fizz 
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz  # 3の倍数という条件を満たすので「Fizz」が実行されてしまう
16
# 以下略

それから、ついうっかり===にするのもやりがちです(今回、自分がやりました…)。エラーになるので気をつけましょう。

繰り返しと条件分岐を組み合わせる

以上(1)、(2)を組み合わせれば、求めているプログラムを書くことができます。上のtimesまたはeachのコード中、puts n # nを出力の部分を、繰り返し処理のコード(ifまたはcase)に書き換えれば完成です。

eachifを組み合わせてみると、以下のようになります。他の組み合わせでも問題ないので、やってみてください。

(1..100).each do |n|
  if n % 3 == 0 && n % 5 == 0
    puts "FizzBuzz"
  elsif n % 3 == 0
    puts "Fizz"
  elsif n % 5 == 0
    puts "Buzz"
  else
    puts n
  end
end

他の書き方は?

繰り返し処理のメソッドとしては、forwhileもありますが、Rubyではあまり推奨されていないようです。

ちなみに私が初めてこのFizzBuzz問題Rubyで書いたときのコードは以下です。

n = 1
while n <= 100
  if n % 3 == 0 && n % 5 == 0
    puts "FizzBuzz"
  elsif n % 3 == 0
    puts "Fizz"
  elsif n % 5 == 0
    puts "Buzz"
  else
    puts n
  end
  n += 1
end

最初Rubyではない言語を勉強していたせいか、何の迷いもなくwhileを使いました(Rubyのfor文の書き方は知らないので)。これが悪いということではないですが、コードの見やすさや簡潔さなどから、eachの方がベターかなと思います。

for文は内部でeachを使って実装されているため、明確な理由がない限り使わない方がよい(eachを使った方がよい)そうです。