プログラム中、何回も同じ処理を繰り返すときはこれまで
主に while
を利用してきた。そのほかにも用途に応じた
繰り返しの構文がある。次講から頻繁に登場する多数のデータの本格的処理
をスムーズに進めるためには、様々な繰り返し構文をしっかりと理解して自在に
操れるようにしておく必要がある。ここで「繰り返し」のための構文を
整理しておこう。
繰り返したい処理の回数だけが重要な場合は、整数に備わっている
times
メソッドで繰り返す。繰り返したい回数を
Nとすると、以下のように記述する。
N.times do ……繰り返し本体…… end
以下のプログラムは、times
を使って5回繰り返す例である。
#!/usr/koeki/bin/ruby # coding: utf-8 5.times do puts "ねえねえ" puts "なんだよ" end puts "んー、ねえねえ" puts "なんだっての、しつこいよ!"
このプログラムをshitsukoi.rb
というファイル名で保存して
実行してみよう。
% chmod +x shitsukoi.rb % ./shitsukoi.rb ねえねえ なんだよ ねえねえ なんだよ ねえねえ なんだよ ねえねえ なんだよ ねえねえ なんだよ んー、ねえねえ なんだっての、しつこいよ!
たとえば1…10までの和を計算したりするような場合には、 整数を1つずつ増やしていく必要がある。そのような場合には、 これまでのプログラムでは以下のようにしていた。
sum = 0 i = 1 while i <= 10 sum += i i += 1 end printf("合計は %d です\n", sum)
このループは、要するにi
を 1 から上限の数
まで変えながらsum
に足していきたいものである。
このような場合は、整数に備わっているメソッド upto
を
利用すると以下のようにすっきり書ける。
#!/usr/koeki/bin/ruby # coding: utf-8 sum = 0 1.upto(10) do |x| sum += x end printf("合計は %d です\n", sum)
upto
とは逆の、1ずつ値を減らしていく
downto
メソッドもある。
もうひとつ、連続した整数を集合とみなして繰り返すには for
と整数範囲を用いて以下のように繰り返しを表現する。
for 変数 in 整数範囲 …繰り返し本体… end
たとえば、1から10までの数を足すには以下のようにする。
sum = 0 for i in 1..10 sum += i end printf("1から10までの和は%dです\n", sum)
配列の各要素を1つ1つ取り出しながらの繰り返しには、
each
または for
を使う。
配列.each do |変数| ……上記の変数を使った処理…… end # または for 変数 in 配列 ……上記の変数を使った処理…… end
実際には以下のようになる。
#!/usr/koeki/bin/ruby
# coding: utf-8
point = [10, 20, 15, 40, 33]
sum = 0
point.each do |z|
sum += z
end
printf("合計は %d です\n", sum)
#!/usr/koeki/bin/ruby
# coding: utf-8
point = [10, 20, 15, 40, 33]
sum = 0
for z in point do
sum += z
end
printf("合計は %d です\n", sum)
この場合の変数 z
には、point
配列の
各要素の値が順次代入される。一回目は z=10
、
二回目は z=20
、三回目は z=15
、
四回目は z=40
、五回目は z=33
、
と代入された上でループ内部が実行される。
上記の例では10, 20, 15, 40, 33が全てsum
に足されるので
合計は 118 です
と出力される。
この場合、配列の先頭から 値が拾われて変数に代入されるが、別の種類の配列では順番はどうなるか わからない。取りだす順番を気にしなくてよい場合にこれを使う。 取りだす順番を確実にしたい場合は、
配列.sort.each do |変数| ……上記の変数を使った処理…… end
のように、配列をsort
した結果を与える。
問題: では取りだす順番を逆順に並べ替えたものにしたい場合は どうしたらよいか。
1, 3, 5, 7, 9, ... とか、10, 20, 30, ... のように、数値を
間隔一定で変えながら利用したい場合は、整数に備わっている
step
メソッドを利用する。書式は以下のとおり。
開始値.step(終了値[, 増加値]) do |変数| …変数を利用した処理 end
増加値 は省略できて、省略した場合は1とみなされる。 実際の利用例は以下のようになる。
step99.rb
)
#!/usr/koeki/bin/ruby # coding: utf-8 sum = 0 print "0" 1.step(99, 2) do |i| printf("+%d", i) sum += i end printf("は %d です\n", sum)
countdown.rb
)
#!/usr/koeki/bin/ruby # coding: utf-8 print "新年まで" 10.step(1, -1) do |sec| if sec <= 3 printf("%c", 0x7) # CTRL-G (ベル)のコードは7 end printf("%d秒前\n", sec) sleep(1) end puts "-" * 79 # 文字列に掛算 * を使うとその回数繰り返す system "banner 'A Happy' 'New Year!'"
この例で利用している system
メソッドは、
Rubyプログラムの中から、外部コマンドを起動するものである。
KTermで
% banner 'A Happy' 'New Year!'
く同じ結果が得られる。
次の二つのプログラムを実際に実行してみよう。
#!/usr/koeki/bin/ruby # coding: utf-8 c=10 while c != 0 printf("%d\n", c) c -= 1 sleep 0.7 end printf("おしまい\n")
こちらは予想どおりの結果となる。
10 9 8 7 6 5 4 3 2 1 おしまい
10から0までの整数の繰り返しを、1.0から0.0までの小数繰り返しに 変えたプログラムを動かしてみる。
#!/usr/koeki/bin/ruby # coding: utf-8 c=1.0 while c != 0.0 printf("%f\n", c) c -= 0.1 sleep 0.7 end printf("おしまい\n")
期待に反して、実行すると以下のようになる。
1.000000
0.900000
:
…中略…
:
0.100000
0.000000
-0.100000
-0.200000
-0.300000
-0.400000
^Ccountdown-float.rb:7:in `sleep': Interrupt
from countdown-float.rb:7
while
ループでは、「cが0.0と等しくないなら繰り返せ」
という条件を指定しているのに、実際にはc=0.000000のときにループが終了して
いない。
これは、計算機による小数計算の誤差に起因する。計算機では内部で2進数を 用いているので小数を表すときに限られた桁数では表し切れないことがある。 3分の1を10進数で表そうとすると 0.333333... となって、途中で切り捨てなけ ればならないのと同様、10進数の0.1を2進数で表すと循環小数になり途中で切り 捨てなければならなくなる。そのため0.1を10回積み重ねても、切り捨て誤差も 積み重なって正確な1.0にはならない。それゆえ、1.0から0.1を10回引いたものは 厳密な0.0にはならないのである。
数値を使ってループを作るときは、ループ変数に浮動小数点数を使うのは避 けなければならない。どうしても小数刻みのものが欲しければ、整数を割り算し て利用する。たとえば、1.0から0.0でカウントダウンしたければ
#!/usr/koeki/bin/ruby # coding: utf-8 c=10 while c != 0 printf("%f\n", c/10.0) c -= 1 sleep 0.7 end printf("おしまい\n")
のように、整数のループ変数を割って利用する。