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の数字を出力するにはどう書けばいいでしょうか。
繰り返し処理するメソッドが使えそうです。繰り返しといえば、times
、each
あたりが思いつきます。
(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」を出力させるには条件によって処理を分ける必要がありそうです。条件式で分岐させるものといえば、if
、case
が有名です。
(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
としても同じです。
if
とcase
を使った書き方はそれほど大きく変わりませんね。どちらでもいいと思います。
条件式の順番に注意
条件分岐のポイントは、条件式の順番です。プログラムは基本的に上から順に実行されます。
たとえば、以下のように「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
)に書き換えれば完成です。
each
とif
を組み合わせてみると、以下のようになります。他の組み合わせでも問題ないので、やってみてください。
(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
他の書き方は?
繰り返し処理のメソッドとしては、for
やwhile
もありますが、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
を使った方がよい)そうです。