プログラムを作成すると、通常は上から順番に処理が実行される。しかし場合によっては順番に処理をされると都合が悪いことがある。いくつか例をあげてみよう。
例1:バーコードリーダーを使ってバーコードを読み取り、商品名と金額を取得する。レジの「合計」ボタンを押すと合計金額が計算され、表示される。
例2:銀行ATMで振り込み作業を行った。この際、口座の残高が振り込み金額以上であれば振込みを実施するが、以下の場合には金額が不足している旨を表示し、振込みは実施しない。
まずは、例1をプログラムで書くことを考えよう。お客さんが購入する商品の個数は一定ではないため、合計を出すために何回足し算をすればよいかは毎回変化する。プログラムを作る上での考え方としては、合計金額を最初0円としておき、バーコードリーダーから入力される金額を随時合計金額に足していくということになる。最高でも100個くらいしか購入しないだろうという予測をたてて100回分足し算ができるような準備をしておくこともできるが、これだとプログラム内に同じ内容が100回も登場することになり、なんとなく美しくない。こんな場合は、「合計」ボタンが押されるまで、新しく入力される金額を合計金額に繰り返し足していくとすれば、100回も同じことを書く必要がなくなる。これは前回の授業で用いたwhile-endである。
例2では場合わけが行われている。振り込み金額と口座の残高の大小比較を行い、振込み金額の方が大きければ振込みを実施しないということになる。これをプログラムで書き、上から順番に実施したらどういうことになるだろうか。振込みを実施しない行と実施する行のどちらも実施されてしまうことになり、口座の残高がいくらであっても結局振り込みは行われてしまう。この場合、条件1を満たす場合はAを実行してBは実行しない、条件2を満たす場合はBを実行してAは実行しないという構造が必要になる。
プログラムを上から順番に実行することを逐次処理というが、逐次処理だけでは表現できないことがある。
逐次処理のみでは表現できないという問題を解決するのが制御構造である。制御構造とはプログラムの実行される順番を変更するものであり、下記のように分類することができる。
whileの横に繰り返しを行う条件を記載する。その条件を満たす間はwhileとendの間に書かれた行を繰り返し実行する。while文と呼ぶことも多い。
#!/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 ("sumの値は最終的に%dになりました\n",sum)
このプログラムについて、プログラムの実行の流れを順を追ってみていこう。上記のプログラムでは左端に番号が振られているが、これは説明を行うために付与したものであり、実際のプログラムでは不要である。
while-endでは、whileの横に書かれた条件を満たす間は繰り返し処理を実行するが、until-endではuntilの横に書かれた条件を満たしていない場合に処理を繰り返し実行する。
#!/usr/koeki/bin/ruby sum = 0 i = 1 until sum >= 20 sum += i i += 1 end printf ("sumの値は最終的に%dになりました\n",sum)
上記の例では、untilの横に記載された条件はsum >= 20である。つまり「sumが20以上である」という条件を満たさない場合はその下の処理を繰り返し実施することになる。言い換えれば「sumが20以上になるまで」処理を繰り返すということになる。これはwhile-endを使用して書くことも可能である。
for-endは様々な使い方ができるため、重宝する記法である。6回目の授業で「配列」を学ぶが、それ以降頻繁に登場するようになる。現時点では1種類の使い方だけ示す。当面は繰り返し表現ではwhile-endしか使用しないので、今は無理に覚えなくともよい。
for 変数 in 開始時の数値..終了時の数値 do 繰り返したい処理 end
上記がfor-endの基本構造である。forの横に記載した変数にin以下の値を順次代入しながらその下に書かれた処理を繰り返し実行する。for-endを用いた足し算プログラムの例を示す。
#!/usr/koeki/bin/ruby sum = 0 for i in 1..5 sum += i end printf ("sumの値は最終的に%dになりました\n",sum)
上記のプログラムでは、iに1から5まで1ずつ増やしながら順番に代入していき、その下にあるsum += iという処理を繰り返し実行することになる。iの値は1、2、3、4、5と変化していくため、これに合わせてsumの値は1、3、6、10、15と変化する。
単純に一定回数繰り返す場合に用いる。以下のどちらの方法でもよい。
#!/usr/koeki/bin/ruby 4.times { print "Hello\n" }
#!/usr/koeki/bin/ruby 6.times do print "Hello\n" end
仮に下のプログラムをtimes.rbという名称で保存し実行すると、以下のようにHelloが6回表示される。。
irsv{naoya}%ruby times.rb Hello Hello Hello Hello Hello Hello
以下のプログラム(上記until-endの箇所で取り上げたものと同じ)をwhile-endを使用して書き直す。
#!/usr/koeki/bin/ruby sum = 0 i = 1 until sum >= 20 sum += i i += 1 end printf ("sumの値は最終的に%dになりました\n",sum)
制限時間は10分。うまくいかない場合は、修正途中でもよいので制限時間内にメールで解答を送信すること。出席点は2点。提出要領は下記の通り。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:Mewによるメールの送り方はMewコマンドを参照
if-then-elsif-else-endと長い名称になっているが、一般にはif文とよぶ。場合わけを行う際に使用するもので、基本的には以下の構造をとる。
if 条件A then #条件Aを満たせば(thenは省略しても良い) 処理A #処理Aを実施する elsif 条件B #条件Aを満たさないが条件Bを満たせば 処理B #処理Bを実施する else #条件ABともに満たさない場合は 処理C #処理Cを実施する end
この構造を把握するために、下記のプログラムをif.rbという名称で保存し、実行してみよう。
#!/usr/koeki/bin/ruby print "1〜100の間で好きな数字を入力してください\n" number = gets.chomp!.to_i if number >= 66 then print "結構大きいのが好きなんですね\n" elsif number >=33 print "真ん中あたりが無難ですよね\n" else print "小さいのが好みなんですか\n" end
ifやelsifの横の条件を変更すれば結果が変化することが確認できる。このプログラムには新しいメソッド(命令)が3つ出てきている。いずれもこれから頻繁に使用するので覚えよう。
gets:キーボードから入力された値を文字列として読み込むメソッド。ただし使い方によってはキーボードからの入力を読み込むのではなく、ファイルからデータを読み込ませることもできる(これは「ファイルの入出力」の回で扱う)。注意しなければならないのは、文字列として読み込むということである。100と入力すれば見かけ上は「ひゃく」に見えるが、文字列なので正確には「いちぜろぜろ」である。このためこのままでは足し算や引き算ができない。
chomp!:キーボードから入力する際は必ず最後に[Return]キーを押すが、これも\nというデータとして扱われてしまう(\nのことを改行文字という)。chomp!は末尾に改行文字がある場合に取り除くメソッドである。最後に!のつかないchompもあり、若干意味が異なる。ここでは混乱を避けるため改行文字を取り除くのはchomp!であると覚えよう。
to_i:文字列を整数に変換するメソッド。小数点以下は切捨てになる。
メソッドは.(ピリオド)でつなぐことができる。この3つのメソッドをつなげることで、上記のプログラムでは、キーボードから入力したデータを整数にしてnumberに代入することが可能になる。
つまり、「number = gets.chomp!.to_i」は、キーボードから文字列を読み込み、改行文字を取り除き、整数に変換した上でnumberに代入するという意味になる。
ifの場合はif以下の条件を満たす場合に処理を実行するが、unlessでは条件を満たさない場合に処理を実行する。until-endをwhile-endで書き直せるように、unless文もif文で書くことができる(ので無理に覚えなくて良い)。
#!/usr/koeki/bin/ruby print "1〜100の間で好きな数字を入力してください\n" a = gets.chomp!.to_i print "もう1回1〜100の間で好きな数字を入力してください\n" b = gets.chomp!.to_i unless a > b print "最初の方が2回目より大きくないわね\n" else print "最初の方が2回目より大きいわね\n" end
unless文で紹介したサンプルプログラムのように比較したい変数が2つではなく、1つであり、その値によって場合わけを行う場合、case文を使用することができる。case文ではなくif文やunless文でももちろん記述できるが、場合わけを行う上で条件が多い場合にはcase文を用いた方が簡単に書ける場合が多い。
#!/usr/koeki/bin/ruby print "1から10の好きな数字を入力してください\n" a = gets.chomp!.to_i case a when 1, 2, 3 print "small\n" when 4, 5, 6 print "medium\n" when 7, 8, 9, 10 print "big\n" else print "範囲を超えました\n" end
上記の例ではwhenを3つ使って条件を記述しているが、条件の数が増えた場合whenを無制限に増やすことができる。
まずは下記のプログラムをregister.rbという名前をつけて保存し実行してみよう。
#!/usr/koeki/bin/ruby sum = 0 item = 0 while true print "金額を入力してください(終了はq): " price = gets.chomp! if price == "q" then break end sum += price.to_i item += 1 printf ("現時点の購入商品の個数は%d個、合計は %d 円です\n", item, sum) end printf ("ありがとうございます。お会計は %d 円になります\n", sum)
このプログラムはキーボードから入力された金額を加算していき、購入商品の個数と現時点での合計金額を毎回表示するプログラムである。キーボードから金額を入力するかわりにqと入力すると購入終了で、お会計金額が表示される。以下では、このプログラムで新しく出てきた表現について示す。
while true:while-endで繰り返しを記述する場合、whileの横に繰り返しを続ける条件を記載できない場合がある。register.rbでは、繰り返しを続けるかどうかは、ユーザーがqを入力したかどうかによる。ユーザーの入力を受け付ける行はwhileの下にあるため、whileの行では繰り返しの判断ができない。この場合、whileの横にtrueをつけることができる。これは永久に繰り返すという意味になる。while trueとした場合は、while-end内に別途終了条件を設ける。具体的にはif文を使用し、if文の中にbreakを入れる。
break:繰り返しの途中で処理を中断する命令。このプログラムではキーボードからqが入力された場合にbreakが実行される。breakが実行されるとwhile-endの繰り返しから抜け出し、endの次の行に進む。
先ほどはgets.chomp!.to_iと3つのメソッドを並べて使用していたが、このプログラムではto_iの位置が異なる。なぜ上のプログラムと同じように3つを並べないのか考えてみよう。このプログラムではキーボードから入力されるのは数字のみではない。qが入力される可能性もある。price = gets.chomp!.to_iとすると、キーボードからの入力を整数に変換してpriceに代入してしまう。qを整数に変換すると0になってしまうので都合が悪い。このため、sum += price.to_iでsumに加算代入する際に整数に変換している。
register.rbを改良し、以下のいずれかのプログラムを作成する。(1)の方が簡単で、(2)は若干難しい。どちらを作成しても良い。
(2)は(1)の応用である。ランダムな数字を生成するためには、register.rbのプログラムの冒頭(sum=0の上)に、digit = rand(50)+1と書けばよい。randはランダムな数字を生成するメソッドで、rand(50)とすると0〜49のうちのどれかがランダムに選ばれる。digit = rand(50)+1のように+1をすることで1〜50となり、この値がdigitという変数に代入されていることになる。もちろんdigitの部分は自分で自由に変更しても良い。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照