- while ループの基礎
for 文のように一定条件の増分で繰り返しを行うのではなく,より自
由な条件判断で繰り返しを行う方法がこの while になります.
英語の意味から分かるように「〜の間」という意味ですから,ある条件
式が成り立っている間中処理を行います.以下に for の時に
も使用した整数の和の while 文バージョンを示します.
n = ARGV[0].to_i
sum = 0 #和の値の初期化
i = 1 #ループ内で使用する序数の初期化
while i <= n #iがnになるまで繰り返す
sum += i #和を自己代入で計算
i += 1 #iの値を1増やす
end
puts sum
|
違いは, while の行にある終了条件と,変数 i の初期値や増分を自
分で指定するところです.フローチャートは図1のようになります.
図1 while ループのフローチャート
基本構文は図2のようになり,for 文と同じです.
図2 while ループの構成
while ループでは増分が自分で指定できるので,for ループ
で苦労した奇数の和のような演算
は楽になります.
n = ARGV[0].to_i
sum = 0
i = 1
while i <= n
sum += i
i += 2
end
puts sum
|
偶数の例でも同じ様に書けることを確認してください.
C言語の開発者である本人が書いたC入門の名著「プログラミング言語C」(共立出
版,1989)のループの最初に紹介されているのも実は for 文ではなく
while 文なのですが,その例をRubyに翻訳
すると次のようなものです.
lower = 0
upper = 300
step = 20
fahr = lower
while fahr <= upper
celsius = 5 * (fahr - 32) / 9
printf("%d\t%d\n", fahr, celsius)
fahr += step
end
|
これは、アメリカで通常用いられている温度単位の華氏(Fahrenheit)により表
示された温度を摂氏(Celsius)に変換するプログラムです.構造をわかりやす
くするためにいろいろと変数を定義したり用意したりしていますが,基本的な動
作はわかるでしょう.華氏について0度から300度まで20度おきに華氏と摂氏の
対応表を出力します.整数型で演算を行うためにちょっとした工夫がなされているのに気づくでしょう
か?
次の例は,コマンドライン引数として与えた数の平均値を求めるものです.引数
の個数が何個でも構いません.これが while ループの便利なところで
す.
sum = 0
i = 0
while value = ARGV.shift
sum += value.to_f
i += 1
end
printf "Averaged value is %1.2f.\n", sum / i
|
shift という新しいメソッドが登場しています.これは,コマンドラ
イン引数が納められている ARGV という配列の要素を一つず
つ順番に取り出すことを行います.値をひとつずつずらして行くので
shift という名前になっています.取り出した要素は次の行
で浮動小数に変換されて自己代入により加算され,同時に要素の個数
i も自己代入により計算されて行きます.最後は平均値の算
出です.
相乗平均を求めるスクリプトに改良するとどうなるか,試してみて下さい.
- 文字列とファイルの処理
ターミナルで利用できるコマンドに cat がありました.これは,引数
として指定したファイルを画面に表示するものでしたが,これと同じ働
きをするスクリプトを作ってみましょう.
while line = ARGF.gets
print line
end
|
コマンドライン引数として与えた「値」を格納する定数は ARGV でし
たが,「中身」を参照する定数が ARGF です.そして,gets
というメソッドは改行文字 \n を行の区切りとして1行
ずつ読み込む動作を行います.ということは,このスクリプトはコマン
ドライン引数として与えたファイルの中身を1行ずつ読み込んで
line という変数に代入し,表示するものです.引数として与えた
ファイルに中身がある限り,while は動作し続けます.最後
まで読み込んだら,ファイルが終了するのでループも終了します.
次の例も終了条件が明確に決まっていない例です.コマンドライン引数を与えな
いで実行すると,キーボードからの入力待ちになります.そこで,数字を入れて
行くと,入れた数字の和が50を越えたところで,ループから抜け出して終わりま
す.
sum = 0
while value = gets
sum += value.to_i
if sum >=50
printf "Sum comes to %d\n", sum
puts "Stop inputting!\n"
exit
end
end
|
これを応用してもらうために昨年出した宿題が遠足のおやつスクリプトでした.
これは,300円以内でお菓子を買うための計算スクリプトでした.
remain = 300
tax = 1.05
while value = gets
if (value.to_i*tax).floor > remain
puts "No way."
elsif (value.to_i*tax).floor == remain
print "使った金額: ", (value.to_i*tax).floor, ", 買い物終了\n"
exit
else
remain -= (value.to_i*tax).floor
print "使った金額: ", (value.to_i*tax).floor, ", 残額: ", remain, "\n"
end
end
|
消費税は1円未満の端数を切り捨てるので,そのためのメソッドである
floor が新たに登場していますが,動作は難しくは無いでしょう.
ちなみに四捨五入は round というメソッドにより実現できます.
- 応用例
次の例は単純にループを回すだけの処理を含む逆算タイマーです.動作環境に応
じて calibration の値を修正しないと正確に動作しないのが難点です
が,それなりに動くはずです.
totalmin = ARGV[0].to_i
totalsec = totalmin * 60
calibration = 0.475
printf "残り時間\n"
for t in 1..totalsec
while Time.times.utime.to_f < t * calibration
end
minvalue = totalmin - 1 - (t - 1) / 60
secvalue = (totalsec - t) % 60
printf "0" if minvalue < 10
printf "%d 分 ", minvalue
printf "0" if secvalue < 10
printf "%d 秒\n", secvalue
end
printf "Time up!\n"
|
新しく出てきた時間に関するクラスですが,今は意味を知らなくても大丈夫です.
動作としては,コマンドライン引数として全体の時間を分で与えます.すると,
1秒毎に残り時間を表示して,残り時間が無くなったところで終了します.
最後にちょっと難しい例を挙げておきましょう.次のスクリプトは三角関数 cos
x の級数展開を行うスクリプトです.微積分で習ったマクロー
リン展開が用いられているのが分かるでしょうか.
include Math
x = 0.5
cosx = cos(x)
errorlimit = 0.0001
def fact(x)
f = 1
if x == 0
return 1
else
for i in 1..x
f *= i
end
return f
end
end
n = 0
sum = 0
error = (cosx - sum) / cosx
while error**2 > errorlimit**2
sum += (-1)**n * x**(2*n) / fact(2*n)
error = (cosx - sum) / cosx
n += 1
end
printf "xの%d乗の項まで計算すると相対誤差が%1.3f%になります\n", 2*n, error*100
|
動作は,実際の関数の値と展開して求めた値が,最初に設定した相対誤差0.01%
以内に収まるのは何次の項まで計算したときかを求めています.この計算では,
階乗を利用するので,特別に階乗を関数化して呼び出せるようにしています.こ
れについては,今は理解できなくて構いません.