Ruby 入門

Ruby for ループとイテレーション

for ループをユースすることで、シーケンス内の各エレメントに対してコードブロックをリピートしてエグゼキュート(実行)することが可能になります。

本章では、これまでのコントロールフローに対する理解をベースに、Ruby における for ループの包括的なガイドを提供し、よりアドバンスドなループ・テクニックを学習するための準備を行います。

1. for ループの基礎知識を理解する

Ruby における for ループは、アレイ (Arrays) や レンジ (Ranges) などのアイテム・コレクションをトラバース(走査)するためのコンサイス(簡潔)なアプローチを提供します。そのベーシックなシンタックスは以下の通りです:

for variable in collection do
  # コレクション内の各アイテムに対してエグゼキュートするコード
end

各パートをブレイクダウンしてみましょう:

  • for:ループをスタートさせるためのキーワードです。
  • variable(バリアブル):コレクション内のカレント(現在)のアイテムの値を、各イテレーションのプロセスでホールドするバリアブルです。
  • in:このキーワードは、ループが後続のコレクションのエレメントをトラバースすることを指定します。
  • collection(コレクション):トラバースしたいアレイ、レンジ、またはその他のエンニュメラブル(Enumerable)オブジェクトです。
  • do:各アイテムに対してエグゼキュートされるコードブロックの開始をマークするキーワードです。これはオプショナル(任意)であり、省略してニューライン(改行)で代替することも可能です。
  • # エグゼキュートするコード:コレクション内の各アイテムに対してエグゼキュートされるコードブロックです。
  • end:ループの終了をマークするキーワードです。

以下は、ナンバーのアレイをトラバースするシンプルなサンプルです:

numbers = [1, 2, 3, 4, 5]

for number in numbers do
  puts number * 2
end
# アウトプット:
# 2
# 4
# 6
# 8
# 10

このサンプルでは、for ループが numbers アレイをトラバースします。各イテレーションにおいて、number バリアブルがアレイ内のカレント・エレメントの値を取得し、do...end ブロック内のコードをエグゼキュートして、そのナンバーに2を乗算したリザルトをプリント(出力)します。

2. レンジ (Range) のトラバース

for ループは、ナンバーのレンジをトラバースするためにもユースできます。レンジとは、スタート値とエンド値によってディファイン(定義)されるナンバーのシーケンスです。

for i in 1..5 do
  puts "イテレーション回数: #{i}"
end
# アウトプット:
# イテレーション回数: 1
# イテレーション回数: 2
# イテレーション回数: 3
# イテレーション回数: 4
# イテレーション回数: 5

このサンプルでは、for ループが 1..5 のレンジをトラバースします。このレンジには 1、2、3、4、および 5 のナンバーが含まれます。各イテレーションの間に、バリアブル i はレンジ内の各ナンバーを取得します。#{i} というシンタックスはストリング・インターポレーションと呼ばれ、ストリング内にバリアブルの値をエンベデッド(埋め込み)することを許可します。(これについてはデータ型とバリアブルのモジュールでカバーされています)。

1...5 をユースして、エンド値を含まないレンジ(エクスクルーシブ・レンジ)をクリエイトすることもできます。これは最後のエレメントをエクスクルード(除外)します。

for i in 1...5 do
  puts "イテレーション回数: #{i}"
end
# アウトプット:
# イテレーション回数: 1
# イテレーション回数: 2
# イテレーション回数: 3
# イテレーション回数: 4

3. do の省略とインライン・シンタックス

前述の通り、do キーワードはオプショナルです。これを省略し、直接ニューラインをユースすることができます:

numbers = [1, 2, 3, 4, 5]

for number in numbers
  puts number * 2
end

さらに、ループのボディがシングルステートメントしか含まない場合、極めてコンパクトなインライン・シンタックスをユースできます:

numbers = [1, 2, 3, 4, 5]
for number in numbers do puts number * 2 end

この記述はよりコンパクトですが、特に複雑なループのボディにおいてはリーダビリティ(可読性)が低下する傾向があるため、アビュース(乱用)は推奨されません。

4. ストリングにおける for ループのユース

for ループは、ストリング内の各キャラクターをトラバースするためにもユースできます。ただし、Ruby はストリングのオペレーションにおいてより便利なメソッドを提供しているため、このアプローチは一般的ではありません。以下は1つのサンプルです:

word = "hello"

for char in word.chars do
  puts char.upcase
end
# アウトプット:
# H
# E
# L
# L
# O

このサンプルでは、word.chars"hello" というストリングをキャラクターのアレイ ['h', 'e', 'l', 'l', 'o'] にコンバートしています。その後、for ループがこのアレイをトラバースし、char.upcase が各キャラクターをアッパーケース(大文字)にコンバートしてプリントします。

5. ネストされた for ループ

1つの for ループを別の for ループの内部に配置(ネスト)することで、マルチプルなコレクションをトラバースしたり、より複雑なパターンをクリエイトしたりすることができます。以下は、マルチプリケーション・テーブル(九九の表)をプリントするネストされた for ループのサンプルです:

for i in 1..3 do
  for j in 1..3 do
    puts "#{i} * #{j} = #{i * j}"
  end
end
# アウトプット:
# 1 * 1 = 1
# 1 * 2 = 2
# 1 * 3 = 3
# 2 * 1 = 2
# 2 * 2 = 4
# 2 * 3 = 6
# 3 * 1 = 3
# 3 * 2 = 6
# 3 * 3 = 9

このサンプルでは、アウター(外側)の for ループが 1 から 3 までのナンバーをトラバースし、インナー(内側)の for ループも 1 から 3 までのナンバーをトラバースします。ij の各コンビネーションに対して、インナーループ内のコードがエグゼキュートされ、乗算のリザルトがプリントされます。

6. for ループと他のイテレーション・メソッドの比較

for ループは Ruby における有効なイテレーション・アプローチの1つですが、一般的には他のメソッド(eachmapselectreject など)と比較して「Ruby らしい(Ruby-like/Idiomatic)」とは見なされません。後続のモジュールで紹介されるこれらのメソッドは、よりコンサイスでエクスプレッシブ(表現豊か)であるため、通常はこちらが好まれます。とはいえ、イテレーションの基礎をマスターし、初期の Ruby コードをリーディングするためには、for ループの理解が依然として非常に重要です。

以下は、前述のマルチプリケーション・テーブルのサンプルを、each メソッド(後の章で詳細に解説します)をユースしてリライトしたものです:

(1..3).each do |i|
  (1..3).each do |j|
    puts "#{i} * #{j} = #{i * j}"
  end
end

このコードは、ネストされた for ループのサンプルと完全に同一のリザルトを実現しますが、Ruby デベロッパーの視点からはよりリーダビリティが高く、Ruby のイディオムにも合致しています。

7. for ループ内での break と next のユース

break および next キーワードをユースして、for ループ内部のエグゼキュート・フローをコントロールすることができます。これら2つのキーワードは前の章ですでにイントロダクションされています。

  • break:このキーワードは、即座にループ全体をエグジット(脱出)します。
  • next:このキーワードは、カレントのイテレーションをスキップし、コレクション内の次のアイテムへと直接スキップします。

以下は、break をユースしたデモンストレーションのサンプルです:

numbers = [1, 2, 3, 4, 5]

for number in numbers do
  if number > 3
    break
  end
  puts number
end
# アウトプット:
# 1
# 2
# 3

このサンプルでは、ループが numbers アレイをトラバースします。number が 3 を超えた時点で break ステートメントがトリガーされ、ループは即座にターミネート(終了)します。

以下は、next をユースしたデモンストレーションのサンプルです:

numbers = [1, 2, 3, 4, 5]

for number in numbers do
  if number % 2 == 0
    next
  end
  puts number
end
# アウトプット:
# 1
# 3
# 5

このサンプルでは、ループが numbers アレイをトラバースします。number が偶数である(つまり、2 で除算した剰余が 0 である)場合、next ステートメントがトリガーされ、カレントのイテレーションがスキップされます。リザルトとして、奇数のみがプリントされます。