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
# => !