プログラミングによる作業で,人間が最大の恩恵を被るのが,今回から学習する
「繰り返し」です.人間は同じ様な単調な作業を繰り返し行うような仕事が好き
ではありません.ところが,コンピュータはそのようなことが無いので,いくら
でも処理を続けます.そのため,繰り返しを覚えると,プログラミングにより一
気に人間が楽できるようになります.実例を挙げながら見て行くことにしましょ
う.
- 基本
基本的な考え方としては,次の要素をしっかり確認します.
何について,いつから,いつまで,繰り返すのか?という当然のように見えるこ
とです.これらには,いくつかのパターンがあります.
- 境界条件の内,開始条件と繰り返し回数が完全に分かっている場合
初期値と繰り返しの回数が決まっていると,考え方は非常に簡単になります.す
なわち,ある数を1から1ずつ増やして行って100回繰り返す,というような場合
です.このときには,for 文を使います.
- 終了条件の方が明確な場合
買いものをして行って,合計金額が手持ちの額を越えたとき,もしくは,残金が
無くなったときに買いものができなくなりますが,それと同じ様に途中の状態は
いろいろあっても,こうなれば作業が終わる,ということが明確な場合には別の
繰り返しを使います.条件が真である限り繰り返しを続ける構文に while
文があります.次週以降で学習します.
- 同様に,条件が偽である限り繰り返しを実行する場合には until
文を使います.これも次週以降で学習します.
- for ループの基礎
繰り返しを実行すると,処理を記述するフローチャートがループ構造になるので,
繰り返しを一般に「ループ」と言います.for 文のことを for
ループとも言います.まずは,基本的な考え方を確認して見ましょ
う.例として三角関数 sin x の値を表示してみます.
x の範囲を0から2πまでとし,0.01きざみで行ってみます.
include Math
delta = 0.01
min = 0
max = 2 * PI
maxnum = ((max - min) / delta).to_i
for xx in 0..maxnum do
x = delta * xx + min
y = sin(x)
printf "sin(%1.2f) = %1.2f\n", x, y
end
|
do は省略可能です.
データが629個も表示されるので,画面をスクロールしても最初の方の値は表示
されません.きちんと計算できているかを確かめるためには,
$ ruby sin.rb > data.txt
のようにリダイレクトを利用して一度結果をファイルに格納した後,
$ less data.txt
のようにして見るのが良いでしょう.ここでは,ループが629回回ったことにな
ります.では,先ほどの処理をフローチャートで見てみましょう.
図1 sin x の値を表示するスクリプトの流れ
このスクリプトはxの範囲が変わっても対応できるように,とりあえず,
最小値と最大値を変数として用意しています.その次に書いてある式が最初は分
かりづらいかも知れませんが,for 文の制約として繰り返しの際に変
化させる増分が1である必要から来ている変換式です.すなわち,
表1 実際の変域とスクリプト上の変域の対応関係
\ | 実際の値 | ループ用換算値 |
変数名 | x | xx |
開始の値 | 0 | 0 |
終了の値 | 2π | 628 |
増分 | 0.01 | 1 |
表1のように,ループの開始,終了条件,増分を整数に直すために処理が必要に
なっているわけです.計算する範囲を変更してみても,すなわち,変数 min
と max の値を変更してみても計算可能であることが分
かります.また,増分も自分で変更してみると良く分かるでしょう.
- 規則にしたがって数値を表示する
今見たように,for 文は図2に示すような構文を用います.
図2 for ループの構文
いきなり,少し難しい例から始めましたが,ループ自体は簡単な例もいろいろ作
れます.昨年度の期末試験に出したところ,異常にできの悪かったものに次のよ
うなものがあります.
n = 20
for i in 1..n
print i, "\n"
end
|
これは,1から20までの数字を順に表示するだけのものです.変数 i
が1から n まで,print 文で出力しているだけであることが
分かるでしょう.フローチャートは図3に示します.
図3 数字を順番に表示する流れ
20位までの数字ならば,自分で1行ずつ print 文を
書いても何とかなるかも知れません.これが,100まで,1000まで,10000までと
なるともうお手上げです.でもスクリプトは n の値を変更するだけで
いくらでも可能です.さらに,
n = ARGV[0].to_i
for i in 1..n
print i, "\n"
end
|
のようにしてしまうと,コマンドラインから最大値を入力できますので,スクリ
プトの変更が不要になります.これが,ループの特徴です.
このようにただ連続する数値を出すだけのものが何の役に立つのでしょうか.最
後に学習する配列と組み合わせることにより,数値計算が楽になります.すなわ
ち,この授業の最初で行ったように変数の値をさまざまに変化させて用意するこ
とが可能になるのです.
では,次の例は自分で実習しましょう.cの場合に鍵となる点は,
自己代入との組合せです.
- 0から20までの偶数を1行に一つずつ表示するスクリプト
- 1から99までの奇数を1行に一つずつ表示するスクリプト
- a1 = 1, an - an-1 = n となる数列
の要素を n が10まで.
- これまでに学習したスクリプトの書き直し
先週まで,いくつかスクリプトを学習して来ましたが,繰り返しを覚えたことに
より非常に簡単になるものがあります.試してみましょう.
連続する整数の和
n = ARGV[0].to_i
sum = 0
for i in 1..n
sum += i
end
|
連続する奇数や偶数の和をループにより求める場合にはちょっと工夫が必要になります.
n = ARGV[0].to_i
maxeven = n / 2
maxodd = (n + 1) / 2
sumeven = 0
sumodd = 0
#even number sum
for i in 0..maxeven
sumeven += 2 * i
end
#odd number sum
for i in 1..maxodd
sumodd += 2 * i - 1
end
|
ポイントは,コマンドライン引数で指定する数が偶数と奇数のどちらでも正確に
求める偶数もしくは奇数の個数を算出することです.
5/27の宿題
このときの宿題はループを使うと行数がずっと短縮できる良い例として実は用意
していました.書き直したものと自分で作ったものを比較してみましょう.
include Math
x0 = 9.9
str ="*"
for i in 0..10
print x0+0.2*i, "\t", str*(40*(sin(x0+0.2*i)**2)), "\n"
end
|
for 文の中を複数行に分けてより分かりやすくすると,次のようにも
書けます.
include Math
x0 = 9.9
str ="*"
for i in 0..10
x = x0 + 0.2 * i
y = sin(x)
yy = 40 * y * y
printf "%1.1f\t %s\n", x, str*yy
end
|
同じ様な処理を少しずつ数を変えて実行する際には,このようにループは非常に
効率的にスクリプトを作成することが可能になります.以後は,必須の機能です
ので,確実にマスターしてください.
- その他の例
階乗の計算を行う例を示します.0の階乗は定義から1と決められていますので,
場合分けも必要になっています.
n = ARGV[0].to_i
fact = 1
if n != 0
for i in 1..n
fact *= i
end
end
printf "%d! = %d\n", n, fact
|
ここでも,自己代入がポイントです.