roy > naoya > 基礎プログラミングI·情報検索 > (4)制御構造[2]
プログラムの流れを変える働きをするものを制御構造とよび、前回の授業では条件判断を取り上げた。制御構造にはもう1つ繰り返しがある。繰り返しについて考えるにあたり、まず次のプログラムを作ることを考えてみる。
バーコードリーダーを使ってバーコードを読み取り、商品名と金額を取得する。レジの「合計」ボタンを押すと合計金額が計算され、表示される。
お客さんが購入する商品の個数は一定ではないため、合計を出すために何回足し算をすればよいかは毎回変化する。プログラムを作る上では、合計金額を最初0円としておき、バーコードリーダーを介して取得される金額を随時合計金額に加算していくことがあげられる。
これをプログラムの流れをあらわすフローチャートにして表現してみよう。左側がプログラムを上から順番に実行していく方法、右側が今回登場する繰り返しを用いた表現方法である。
左側から見ると、まず合計金額の初期値を0円としている。次にバーコードを読み取って金額を取得し、その金額を合計金額に加算する。これで1つ目の商品の金額が合計に加算されたことになる。次に2つ目の商品のバーコードを読取り、合計金額に加算する。「バーコードを読み取る」「合計金額に加算する」という処理が繰り返し登場していることがわかる。ここでは2回分しか書いていないが、実際には3個以上の商品を購入する場合もありうるのでこれでは足りない。最大で何回分準備しておけばよいのかは難しい判断だが、ここでは念のため100個分準備しておけば安心と考え、100回同じことを書いている。
右側は繰り返しを用いたプログラムの考え方である。ここでは「バーコードを読み取る」「合計金額に加算する」の後で◇があり、ここで合計ボタンを押したかどうか判定が行われる。押した場合はお会計に進み、押していない場合は上に戻り、次の商品の金額を合計に加算する。このような書き方をすると、バーコードからの金額の取得と合計への加算をプログラム内で1回書いておけばよいことになり、シンプルになる。
制御構造における繰り返し
繰り返しを表現する際に主に用いられるのはwhile-end(while文)である。以下がwhile文の基本構造となる。
while 繰り返しを継続する条件 繰り返し行う処理 end
whileの横に繰り返しを継続する条件を記載する。その条件を満たす間はwhileとendの間に書かれた行を繰り返し実行する。最後には必ずendをつける。
簡単なプログラムを見てみよう(add.rb)。
#!/usr/koeki/bin/ruby sum = 0 i = 1 while i <= 5 sum += i i += 1 end printf ("1から5の合計は%dです。\n",sum)
これは1から5まで足した結果を求めるプログラムであり、実行すると以下の結果が得られる。
irsv{naoya}% ruby add.rb[Return] 1から5の合計は15です。
このプログラムでは冒頭で、sumに0、iに1を代入している。その後while-endが登場するが、whileの横に書かれた繰り返しを継続する条件は「i <= 5」、whileとendの間にある繰り返し行う処理は「sum += i」「i += 1」の2つとなっている。では、このプログラムでどのように1から5までの足し算をしているのだろうか。
以下は、add.rbを説明するにあたり左端に行番号を付加したものである。この行番号に基づいてプログラムの流れを示す。
#!/usr/koeki/bin/ruby
1:sum = 0
2:i = 1
3:while i <= 5 #=> iの値が5以下の間は以下の2行を繰り返し実施する
4: sum += i
5: i += 1
6:end
7:printf ("1から5の合計は%dです。\n",sum)
繰り返しを行う中で、iの値を1ずつ増加させている(5行目)。そしてiの値に基づいて繰り返し継続の可否を決定している(3行目)。
もう1つ例を見てみよう。今度は6回のテストの合計得点を求めるプログラムである(test.rb)。
#!/usr/koeki/bin/ruby total = 0 number = 1 print "6回のテストの合計得点を求めます。\n" while number <= 6 printf ("%d回目のテストの得点は?\n", number) score = gets.chomp!.to_i total += score number += 1 end printf ("6回のテストの合計は%dです。\n",total)
これを実行すると次の結果が得られる。
irsv{naoya}% ruby test.rb[Return] 6回のテストの合計得点を求めます。 1回目のテストの得点は? 30[Return] 2回目のテストの得点は? 45[Return] 3回目のテストの得点は? 25[Return] 4回目のテストの得点は? 30[Return] 5回目のテストの得点は? 50[Return] 6回目のテストの得点は? 40[Return] 6回のテストの合計は220点です。
ここではwhile-end内でnumberの値を1ずつ増加させ、whileの横に書かれた条件であるnumber <= 6を満たす間繰り返し処理を行わせている。
while-end間にはprintfメソッドやgetsメソッドが用いられており、繰り返しを行うたびに、メッセージの出力やキーボードからの入力の受け取りが行われている。
while文のまとめ
while 繰り返しを継続する条件 繰り返し行う処理 end
while文以外にもいくつかの繰り返し表現がある。詳細はその他の繰り返し表現のページを参照されたい。ここでは、whileに次いで利用頻度の高いfor-end(for文)のみ紹介する。
for文は様々な使い方ができ、もう少し後の回(具体的には配列を学んだ後)に役に立つ表現方法である。ここでは最も簡単なfor文の使い方を示す。
for 変数 in 開始時の数値..終了時の数値 do 繰り返したい処理 end
これがfor文の基本構造である。forの横に記載した変数にin以下の値を順次代入しながらその下に書かれた処理を繰り返し実行する。for-endを用いた足し算プログラムの例を示す。
#!/usr/koeki/bin/ruby sum = 0 for i in 1..5 sum += i end printf ("合計は%dです。\n",sum)
上記のプログラムでは、iに1から5まで1ずつ増やしながら順番に代入していき、その下にあるsum += iという処理を繰り返し実行することになる。iの値は1、2、3、4、5と変化していくため、これに合わせてsumの値は1、3、6、10、15と変化する。1から5までの足し算なのでwhile文で書くこともできる。
add.rbを参考に1から10までの積(1×2×・・・×10)を求めるプログラムを書いてみよう。最初から書くのは大変なのでadd.rbを修正してみよう。while文で書けば良いが、for文に挑戦しても良い。
制限時間は10分。うまくいかない場合は、できたところまででよいのでメールで解答を送信すること。出席点は2点。提出要領は下記の通り。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:Mewによるメールの送り方はMewコマンドを参照
本日の冒頭で話したバーコードを読み取るレジのプログラムを思い出そう。ここでは複数商品の会計をすることができるように、以下の部分を繰り返して実行していた。
これをwhile-endを使って書くことを考えよう。これまではwhile-end内で特定の変数の値を1ずつ増加させ、その変数の値が一定の値を超えた時点で繰り返しが終了するようにしていた。
しかし、今回はこのような方略を採用することはできない。合計ボタンを押したときに繰り返しを終了しなければならないからである。つまりユーザーからの入力によって繰り返しを終了する必要がある。
このような場合にはwhile trueを用いる。while trueを用いたプログラム(register.rb)を確認してみよう。
#!/usr/koeki/bin/ruby sum = 0 while true print "金額を入力してください(お会計は0と入力):\n " price = gets.chomp!.to_i if price == 0 then break end sum += price end printf ("ありがとうございます。お会計 %d 円になります。\n", sum)
irsv{naoya}% ruby register.rb[Return] 金額を入力してください(お会計は0と入力): 200[Return] 金額を入力してください(お会計は0と入力): 100[Return] 金額を入力してください(お会計は0と入力): 350[Return] 金額を入力してください(お会計は0と入力): 0[Return] ありがとうございます。お会計650円になります。
while-end内では、printメソッドでメッセージを表示し、getsメソッドを使ってユーザーからの金額の入力を受け取りpriceに代入している。そして、priceの値をsumに加算代入することで、繰り返しを行うたびにsumの値は増加する。
問題はプログラム中に黄色で示した、while trueとif-endである。
while trueについてのまとめ
register.rbを見ると、なぜか字下げ(インデント)されている箇所がある。while-end内やif-end内がインデントされている。インデントをすることでwhile文やif文がどこで始まりどこで終わっているかわかりやすくなる。以下には同一のプログラムを(1)インデントをした場合、(2)インデントをしない場合の2パターンで示す。(1)インデントをした場合の方が見やすいのは明らかである。
#!/usr/koeki/bin/ruby sum = 0 while true print "金額を入力してください(お会計は0と入力):\n " price = gets.chomp!.to_i if price == 0 then break end sum += price end printf ("ありがとうございます。お会計 %d 円になります。\n", sum)
#!/usr/koeki/bin/ruby sum = 0 while true print "金額を入力してください(お会計は0と入力):\n " price = gets.chomp!.to_i if price == 0 then break end sum += price end printf ("ありがとうございます。お会計 %d 円になります。\n", sum)
もう少し長いプログラムを書くようになると、while文の中にwhile文が登場したり、さらにif文が登場したりして、endを複数使用するようになる。endがないとプログラムはうまく動かない。インデントをする習慣をつけるとendの抜け落ちに気づきやすくなる。
インデントを行う場合、スペースキーを何回か押しても良いが、Tabキーを押すと自動的に適切な位置にインデントをしてくれる。便利なので確認しておこう。
問題はプログラム中に黄色で示した、while trueとif-endである。
プログラムのインデントについて
register.rbを改良し、以下のバイト代を計算するプログラムを作成せよ(kadai2.rb)。
時給を入力後、毎日のアルバイトの時間を順に入力していくと、最後に合計時間とアルバイト代を計算して出力する。
whileの横には繰り返しを継続する具体的な条件を書いても良いし、while trueを使っても良い。while trueの場合は何日分でも入力することができるが、繰り返し継続条件を具体的に指定する場合は、何回(何日分)入力できるのかをあらかじめ決めておく必要がある。この場合の日数は任意とするが、少なくとも2回以上繰り返しが行われるようにする。
いずれの場合も、printメソッドやprintfメソッドを使って、初めてそのプログラムを実行したユーザでも使い方がわかるような文章を提示するよう心がけよう。せっかく内部は良いプログラムであったとしても、ユーザが操作方法がわかりづらいと感じれば使ってもらえない。この点についても趣向を凝らしてみよう。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照