NnmnLog

Rubyでの繰り返し処理

作成: 2021-06-06
更新: 2021-06-06
タグ: Ruby Ruby入門

Rubyの繰り返し処理でよく使いそうなものをまとめました。

Rubyの繰り返し

構文 用途
while文 条件に指定した式が真の間、処理を繰り返し実行する構文
until文 条件に指定した式が偽の間、処理を繰り返し実行する構文
for文 inに指定したオブジェクトの各要素に対して、繰り返し処理を実行する構文
Kernel.#loop 無限ループを作るメソッド
Array#each オブジェクトのリストに対して繰り返し処理を適用するメソッド
Integer#times レシーバの整数の数だけループを実行するメソッド
Integer#upto, Integer#downto レシーバの整数から引数で指定した整数になるまで、カウンターを増加/減少させて繰り返し処理を実行するメソッド
String#upto レシーバの文字列から引数で指定した文字列になるまで、文字列を進めながら繰り返し処理を実行するメソッド
次の文字列の定義は、こちら

while文

while ... [do] 文は、条件に指定した式が真の間、処理を繰り返すことができます。

count = 0
while count < 10 do
  puts count
  count += 1
end

until文

until ... [do]文は、条件に指定した式が偽の間、処理を繰り返すことができます。

count = 0
until count >= 10 do
  puts count
  count += 1
end

for文

for文は、for ... in ... [do] ... endの形式で使用し、in句で指定したオブジェクトのリストに対して繰り返し処理を実行します。

for c in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] do
  puts c
end

for文自体は、in句で指定したオブジェクトのリストとして評価されるため、以下のようにfor文の戻り値を変数に代入すると、繰り返し処理の対象になったオブジェクトのリストが得られます。

return_from_for = for n in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] do
  (n + 1)
end
pp return_from_for
# => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] が出力される

loopメソッド

無限ループを作成できます。

loop do
  puts "Hello!"
  sleep 1
rescue Exception
  puts "\nおわり"
  raise StopIteration
end
# 止めるときは、「Ctrl + C」を入力します。

eachメソッド

for文と同様にオブジェクトのリストを対象に繰り返し処理を実行します。

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].each do |n|
  puts n
end

eachメソッドの評価結果も繰り返し処理をしたオブジェクトのリストになります。

return_from_each = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].each do |n|
  (n + 1)
end
pp return_from_each
# => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] が出力される

Integer#timesメソッド

レシーバに指定された、Integerクラスのオブジェクトの回数分処理を繰り返すメソッド。

10.times do |n|
  puts n
end

Integer#uptoメソッド, Integer#downtoメソッド

ある数字からある数字まで、繰り返しを行うときに使います。

# 0から10までカウンターをインクリメントしながら処理する(ちょうど0と10の時も実行される)
0.upto(10) { |n| puts n }
# 10から0まで、カウンターをデクリメントしながら処理する(ちょうど0と10の時も実行される)
10.downto(0) { |n| puts n}

String#uptoメソッド

ある文字列からある文字列まで連続で処理を実行したいときに使用します。

次の文字列は何かという定義は、こちらを参照してください。

# A, B, C, ..., 記号, ..., x, y, z まで順に処理を実行します。(間に記号が挟まる)
"A".upto("z") { |c| puts c }

小ネタ

文字列を1文字ずつループしたい場合は、splitでArrayにする

以下のように、文字列に対して直接forやeachを使うとエラーになる。

(for文に関しては、内部的にeachメソッドを使っているっぽい)

# for文の場合
for c in "Hello, World!"
  puts c
end
# => (irb):16:in `<main>': undefined method `each' for "Hello, World!":String (NoMethodError)
# =>         from /usr/local/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
# =>         from /usr/local/bin/irb:23:in `load'
# =>         from /usr/local/bin/irb:23:in `<main>'

# eachメソッドの場合
"Hello, World!".each { |c| puts c }
# => (irb):19:in `<main>': undefined method `each' for "Hello, World!":String (NoMethodError)
# =>         from /usr/local/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
# =>         from /usr/local/bin/irb:23:in `load'
# =>         from /usr/local/bin/irb:23:in `<main>'

以下のように、splitメソッドで文字列を1文字ずつに分割してArrayにしてからループする

# for文の場合
for c in "Hello, World!".split('')
  puts c
end
# => H
# => e
# => l
# => l
# => o
# => ,
# =>  
# => W
# => o
# => r
# => l
# => d
# => !


# eachメソッドの場合
"Hello, World!".split('').each do |c|
  puts c
end
# => H
# => e
# => l
# => l
# => o
# => ,
# =>  
# => W
# => o
# => r
# => l
# => d
# => !

原因としては、StringクラスがArrayクラスを継承していないためと思われる。 eachメソッドはArrayクラスのメソッドなので、Stringクラスから直接、eachメソッドを呼べない。

裏技的に、以下のようにStringクラスを拡張し、1文字ずつ処理をするメソッドeach_charを作ってみる。

class String
  def each_char &block
    # blockが渡されたら、文字列を1文字ずつに分割したリストにblockの処理を適用する
    self.split('').each { |c| block&.call(c) }

    # 戻り値は、自身を返しておく
    self
  end
end

これで、以下のような記述が可能になる。

"Hello, World!".each_char do |c|
  puts c
end
# => H
# => e
# => l
# => l
# => o
# => ,
# =>  
# => W
# => o
# => r
# => l
# => d
# => !