繰り返し

概要

プログラムでは条件分岐と並んで重要な繰り返しについて説明します。 何か処理のかたまりを一定の条件に従って繰り返すことを繰り返しといいます。

繰り返しとは

繰り返しとは何かの処理を繰り返し行うことです。 永遠に繰り返してしまうとプログラムが永遠に終わらないので、普通は

  • ある回数だけ繰り返す
  • ある条件を満たす間だけ繰り返す
  • 配列の全要素に対して繰り返す

といった条件をもって繰り返しを行います。

Rubyで繰り返しは、繰り返し専用の構文を使う方法と、メソッドを使う方法があります。

最も単純な例で、1から100まで画面に出力するプログラムを考えます。 繰り返しを使わないと、こうなります。

puts 1
puts 2
puts 3
puts 4
# ...
puts 100

これをtimesメソッドを使って書き換えると次のようになります。

100.times { |count| puts count + 1 }

詳しくはあとで説明します。

別の例として、2.8条件分岐であった消費税計算のプログラムを考えてみます。 このプログラムは不正な値を入力するとメッセージを出力します。 このプログラムを次のように変更してみます。

  • コマンドライン引数ではなく標準入力(gets)から値段を受取るようにする
  • 繰り返しを使って正しい値が入力されるまで入力を求めるようにする
loop do
  print "税抜価格を入力してください > "
  price = gets.to_i

  if price <= 0
    puts "値段は正の整数で入力してください"
  else
    puts "税抜価格は#{price}円です。"
    puts "税込価格は#{(price * 1.08).to_i}円です。"

    break # 繰り返しを抜ける
  end
end

これを実行すると次のようになります。

$ ruby loop_tax.rb
税抜価格を入力してください > aaa
値段は正の整数で入力してください
税抜価格を入力してください > 0
値段は正の整数で入力してください
税抜価格を入力してください > -100
値段は正の整数で入力してください
税抜価格を入力してください > 50000
税抜価格は50000円です。
税込価格は54000円です。

構文による繰り返し

while文

while文は繰り返しを行うための専用の構文です。 ある条件を満たしている間だけ繰り返しを行います。

while 条件
  処理
end

例えば1〜100を出力するプログラムをwhileで書くと次のようになります。

count = 1

while count <= 100
  puts count
  count += 1
end

メソッドによる繰り返し

Rubyでは繰り返しを行うためのメソッドが多くあります。 その中でも代表的な3つのメソッドを紹介します。

メソッドを使うと、次のように繰り返しを書くことができます。

100.times { |count| puts count + 1 }

ブロック

個別のメソッドを紹介する前に、ブロックというものの説明をします。

ブロックとは処理のかたまりのことです。 上の例だと、

{ |count| puts count + 1 }

となっているところがブロックです。 繰り返しを行うメソッドには、繰り返す処理をブロックとして渡します。

ブロックには2種類の書き方があります。 1つ目は{}で囲う方法です。

オブジェクト.メソッド名(引数...) { |ブロック変数1, 変数2, ...| 処理 }

2つ目はdo〜endで囲う方法です。処理が複数行に渡る場合はこちらを使います。

オブジェクト.メソッド名(引数...) do |ブロック変数1, 変数2, ...|
  処
  理
end

ブロック変数とは、ブロックの中で使うことができる変数です。 メソッドによって何が入るかは変わります。 例えばtimesメソッドの場合は「今が何回目か」という数値が入ります。

ブロック変数が必要ない場合は省略することもできます。

obj.method { 処理 }

obj.method do
  処
  理
end

timesメソッド

timesは数値オブジェクトのメソッドです。 その数だけ繰り返しを行います。 ブロック変数には「今が何回目の繰り返しか」という数値がはりいます。

数値.times do |変数|
  繰り返したい処理
end

timesでは0回目から始まります。なので、1〜100を出力するというときは1を足して調整する必要があります。

100.times { |count| puts count + 1 }

eachメソッド

eachは範囲オブジェクトや配列オブジェクトに対して使うことができます。 その要素全てに対して繰り返し処理を行います。

例えば範囲オブジェクトを使って1~100を出力するプログラムは次のようになります。

(1..100).each { |count| puts count }

範囲オブジェクトは1, 2, 3, ..., 100といった要素を含んでいます。 そしれ、それぞれの要素に対してeachメソッドでputsするという処理を繰り返し行っています。 ブロック変数には要素が入ります。

配列のeachはもう少しわかりやすいです。 例えば、寿司ネタを順番に出力するプログラムは次のようになります。

["サバ", "イワシ", "コハダ"].each do |neta|
  puts "#{neta}はとても美味しい!!"
end

[要素1, 要素2, ..., 要素n]で配列オブジェクトになります。 配列オブジェクトのeachメソッドを呼ぶことで、全ての要素に対してブロックの処理を実行します。 配列の要素は左から順番に繰り返されます。

これを実行すると次のようになります。

$ ruby each_neta.rb
サバはとても美味しい!!
イワシはとても美味しい!!
コハダはとても美味しい!!

loopメソッド

loopKernelのメソッドで、処理を無限に繰り返します。 実際は無限ループになると困るので、どこかでbreakなどを使って終了する必要があります。 breakについてはあとで説明します。

1から100を出力するプログラムは次のようになります。

count = 1

loop do
  puts count
  count += 1

  # countが100を超えたらbreakで繰り返しを終了する
  if count > 100
    break
  end
end

繰り返し制御

繰り返しはただ繰り返すだけでなく、必要に応じてスキップしたり終了したりできます。

next

nextnext以降の繰り返し処理をスキップして、次の回の繰り返し処理に進む命令です。

例えば1から100までのうち、偶数だけ出力したい場合は次のように書きます。

(1..100).each do |count|
  # 奇数(2で割り切れない)ときはnextでスキップする
  if count % 2 != 0
    next
  end

  puts count
end

break

breakは繰り返しの途中でも強制的に繰り返しを終了させる命令です。

例えば寿司ネタの配列にガリが混ざってるとそこで繰り返しを終了するようなプログラムは次のようになります。

["サバ", "イワシ", "ガリ", "コハダ"].each do |neta|
  if neta == "ガリ"
    break
  end

  puts "#{neta}はとても美味しい!!"
end

ifnetaがガリの場合はbreakするようにしています。 これを実行するとこうなります。

$ ruby gari.rb
サバはとても美味しい!!
イワシはとても美味しい!!

breakで繰り返しが終了していることがわかります。

練習問題

(1) 1〜100を出力

1〜100を画面に出力するプログラムを次の4通りで作成してください。

  • while
  • timesメソッド
  • eachメソッド
  • loopメソッド

(2) 1〜100の偶数を出力

1〜100のうち偶数を画面に出力するプログラムを次の4通りで作成してください。

  • while
  • timesメソッド
  • eachメソッド
  • loopメソッド

(3) 1〜100の奇数を出力

1〜100のうち奇数を画面に出力するプログラムを次の4通りで作成してください。

  • while
  • timesメソッド
  • eachメソッド
  • loopメソッド

(4) 消費税計算プログラム

2.8条件分岐の練習問題(1)で作成した消費税計算プログラムを次のように変更してください。

  • コマンドライン引数ではなく標準入力(gets)から値段を受取るようにする
  • loopメソッドを使って正しい値が入力されるまで入力を求めるようにする