roy > naoya > 基礎プログラミングII > (2)様々な繰り返し表現

(2) 様々な繰り返し表現

[1]繰り返し表現の復習

前期にはwhile-endという繰り返し表現の構文を用いてきた。まずこの構文を確認し、その後、その他の繰り返し表現の構文を確認する。

繰り返し継続条件を具体的に指定するwhile-end

プログラムは、通常上から順に1行ずつ実行される。これを逐次処理と呼ぶ。これに対して、プログラムの流れを変えるのが制御構造であった。制御構造には前回復習した「条件判断」と今回確認する「繰り返し」があった。「繰り返し」は条件に応じて指定した処理を何度も繰り返し実施するというもので、前期はwhile-endを取り上げた。

while-endの基本構造は以下の通りである。

while-endの基本構造

while 繰り返し条件
  繰り返す処理
end

whileの横に書くのは x < 10 や y >= i のような繰り返しの継続条件である。この条件を満たす間はwhile-end間の処理を繰り返し実行する。具体例をあげながらもう一度確認してみよう。これは6回のテストの合計得点を求めるプログラムである(sum_test.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

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)

これを実行すると次の結果が得られる。

sime{c1xxxxx}% chmod +x test.rb[Return]
sime{c1xxxxx}% ./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 true(whileの横に具体的な継続条件を書かない用法)

while-endを用いる場合、whileの横に繰り返し条件を具体的に記述するかわりに「while true」と記述することもできた。trueは「常に条件を満たす」という意味であり、while trueと書くことで永久に繰り返しを継続することができた。while trueを使用する場合は、while-end内に別途、繰り返しから抜けるための「break」を記述しておく必要があった。

以下は簡易レジスターのプログラムであり、購入した商品の単価(price)を繰り返し入力することで、購入した商品数(item)と合計金額(sum)を算出する。ユーザがキーボードから「q」を入力した場合に繰り返しから抜ける。

ユーザがキーボードから入力した値が代入されるのはpriceであり、「price = gets.chomp!」の行で入力値の取得と代入が行われる。ここで「q」を入力すれば繰り返しから抜けることになる。しかし、この行はwhile-end内に記述されている。つまり繰り返しの継続有無は、whileの行では判断できず、while-end内の処理を実行して初めて決定する。このようなケースではとりあえず繰り返しは続けておき、繰り返し処理を行う中で終了するかどうかを判断するという、while trueが利用される。

checkstand.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
item = 0

while true
  print"金額を入力してください(終了はq): "
  price = gets.chomp!
    if price == "q" 
      break
    end
  sum += price.to_i
  item += 1
  printf("現時点の購入商品の個数は%d個、
  合計は %d 円です\n", item, sum)
end

printf("ありがとうございます。お会計は %d 円になります\n", sum)
sime{c1xxxxx}% chmod +x checkstand.rb[Return]
sime{c1xxxxx}% ./checkstand.rb[Return]
金額を入力してください(終了はq)
100[Return]
現時点の購入商品の個数は1個、合計は 100 円です
金額を入力してください(終了はq)
150[Return]
現時点の購入商品の個数は2個、合計は 250 円です
金額を入力してください(終了はq)
q[Return]
ありがとうございます。お会計は 250 円になります

プログラム内の強調部分(黄色)で繰り返しから抜けるか判定を行っている。右辺は"q"であり""がついている。文字列の場合は""をつける。

このプログラムでは、「price = gets.chomp!」としている。getsはキーボード入力を文字列として取得する。それゆえ、100などの金額(整数)を入力しても文字列として取得される。

sum += price.to_i」で、sumに入力値を加算する際にto_iメソッドを付けて整数への変換を行っている。price=gets.chomp!.to_iとしていないのは、金額(整数)以外に、終了するためのq(文字列)が入力されるからである。

price = gets.chomp!.to_i」としてqを入力した場合、文字列はto_iメソッドで整数変換をすると一律で0になるため、priceには0代入される。結果的に、price内の値がqである場合に繰り返しから抜けるという条件を満たすことがなくなってしまう。

[2]練習課題

以下は簡易版のレジのプログラムであるが、何箇所がエラーがあり、正しく実行することができない。どこをどのように修正すればよいか答えよ。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

while true
  print"金額を入力[終了はquit]:"
  money = gets.chomp!
  if money = "quit"
    break
  end
  sum += money
end

printf("合計は%d円です\n",sum)

[3]その他の繰り返し表現(1)-times:一定回数の繰り返し

繰り返し表現にはwhile-end以外にもいくつかの構文がある。様々な方法を理解して自在に扱えるようにしよう。

timesは単純に一定回数繰り返す場合に用いる。以下のどちらの方法で記述しても構わない(times.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

4.times {
  print"Hello\n"
}
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

4.times do
  print"Hello\n"
end
sime{c1xxxxx}% chmod +x times.rb[Return]
sime{c1xxxxx}% ./times.rb[Return]
Hello
Hello
Hello
Hello

[4]その他の繰り返し表現(2)-upto・donwto:1ずつ増やしながら・減らしながら

1から10まで足し算するプログラムをwhile-endで書いてみると以下のようになる。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
i = 1

while i <= 10
  sum += i
  i += 1
end

printf("1から10までたすと%dです\n",sum)

このプログラムではiの初期値を1とし、繰り返しを行うごとにiの値を1ずつ増加しながらsumに加えている。そしてiが10を超えたら終了になるため、結果的に1から10までの合計を出したことになる。

○から□まで1ずつ変化させながら処理を繰り返し行うという場合、while以外にuptoメソッド、downtoメソッドを使って表現することもできる。以下はuptoメソッドを用いて上記のプログラムを書き直したものとなる(sum-upto.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
1.upto(10) do |i| #from.upto(to)。1から10までiを1ずつ増加させる。
  sum += i
end

printf("1から10までたすと%dです\n",sum)

同様にdowntoメソッドを用いて書き直すと以下のようになる(sum-downto.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
10.downto(1) do |i| #from.downto(to)。10から1までiを1ずつ減少させる。
  sum += i
end

printf("1から10までたすと%dです\n",sum)

uptoメソッド、downtoメソッドの基本構造はそれぞれ以下の通りである。初期値と終了値をそれぞれfromとtoの位置に指定する。繰り返し処理を行う中でuptoメソッドはfromからtoまで変数の値を1ずつ増やしながら、downtoメソッドでは1ずつ減らしながら処理を継続する。

uptoメソッドの基本構造

  初期値.upto(終了値) do |変数|
    繰り返す処理
  end

初期値と終了値には整数もしくは整数が代入された変数が使用可能

初期値から終了値まで順番に1ずつ増やしながら変数に代入し、繰り返し処理が行われる

downtoメソッドの基本構造

  初期値.downto(終了値) do |変数|
    繰り返す処理
  end

初期値と終了値には整数もしくは整数が代入された変数が使用可能

初期値から終了値まで順番に1ずつ減らしながら変数に代入し、繰り返し処理が行われる

なお、3.upto(1)のようにuptoメソッドにおいて初期値よりも終了値に小さな値を指定した場合や、1.downto(10)のようにdowntoメソッドにおいて初期値よりも終了値に大きな値を指定した場合は1回も処理は行われない。

[5]その他の繰り返し表現(3)-step:飛び飛びに繰り返す

uptoやdowntoは1ずつ増加、減少させながら繰り返し処理を行うためのメソッドである。では、10+20+30+、、、+100のように10ずつ増加する場合や、2+4+6+、、、+10のように2ずつ増加する場合はどうすればよいだろうか。これについてもまずはwhileでの表現方法を見てみよう。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
i = 10

while i <= 100
  sum += i
  i += 10
end

printf("10+20+...+100は%dです\n",sum)

このようにiの初期値を10として、繰り返しを行う中でiの値を10ずつ増加させていけば書くことができる。なお、一定間隔で値を変える方法として、stepメソッドもある。stepメソッドの基本構造は下記の通りとなる。

stepメソッドの基本構造

   初期値.step(終了値, 増加値) do |変数|
    繰り返す処理
  end

初期値・終了値・増加値には整数もしくは整数が代入された変数が使用可能

初期値から終了値まで順番に増加値ずつ増やしながら変数に代入し、繰り返し処理が行われる(増加値がマイナスの値の場合は増加値ずつ減らしながら繰り返し処理が行われる)

stepメソッドでは3つの値を指定する。初期値と終了値はuptoメソッド、downtoメソッドと同様であるが、加えて増加値を指定する。これにより10ずつ増加させるということが可能になる。

増加値は省略可能であるが、この場合は増加値=1とみなされる。10から100まで100ずつ増加させて加算するプログラムを書くと以下のようになる(sum-step.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
10.step(100, 10) do |i|
  sum += i
end

printf("10+20+...+100は%dです\n",sum)

増加値には負の値を入れることもできる。10から初めて100まで10ずつ増加させながら足し算をするということは、100から初めて10まで10ずつ減らしながら足し算をすることと同義である。上記のプログラムの開始値、終了値、増加値をそれぞれ100、10、-10に変更しても同じ結果となる。

[6]その他の繰り返し表現(4)-for-end:様々な用途に使える繰り返し表現

1ずつ増やしながら処理を繰り返すには、for整数範囲を用いた表現も可能である。整数範囲には1..10のように初期値と終了値を指定する。そして初期値から終了値まで1ずつ増やしながら変数に代入し、繰り返し処理を行う。

for-endの用法[1]-整数範囲の指定

  for 変数 in 整数範囲
    繰り返す処理
  end

整数範囲は1..10のように初期値..終了値を記載する。初期値から終了値まで変数に順番に代入しながらfor-end間の処理を繰り返し行う。

これで1から10までの合計を記述すると以下のようになる(sum-for.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

sum = 0
for i in 1..10 #iに1から10まで順次代入する
 sum += i
end

printf("1から10までたすと%dです\n",sum)

整数範囲の代わりに配列を指定すると、その配列に代入された値を順番に取り出して使用することができる。

for-endの用法[2]-配列の指定

  for 変数 in 配列
    繰り返す処理
  end

配列内の0番目の要素から順番に変数に代入しながらfor-end間の処理を繰り返し行う。配列内の全ての要素を読み出したら終了となる。

これで配列から順番に値を取り出して表示すると以下のようになる(array-for.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

test = [80, 75, 90, 88, 60]

sum = 0
for i in test #iにtestの0番目の要素から順に代入する
 sum += i
end

printf("テストの合計は%d点です\n",sum)

[7]レポート課題

次の3問のうちいずれかを実施せよ(ruby2-1.rb)。いずれもwhile-end以外の繰り返しを必ず使用すること。プログラム内で複数回繰り返し表現を用いる場合は、while-end以外の構文を1回使用すれば、他ではwhile-endを使用しても良い。

問題1(7点満点):入力した正の整数が素数かどうかを調べるプログラム。素数とは1と自分自身でしか割り切れない整数とする。例えば、11と入力した場合、11が2から10で割り切れないのであれば素数、割り切れるのであれば素数ではないということになる。

問題2(8点満点):キーボードから10回実数データを入力し、平均値と標準偏差を算出するプログラム

標準偏差は次式で計算できる。

平方根は、Math.sqrt()で計算できる。

a = 9
b = Math.sqrt(a)

p b #=> 3.0

ExcelやCalcなどの表計算ソフトには標準偏差を求めるSTDEVPという関数が準備されている。プログラムを実行したら、表計算ソフトで結果を確認し、間違いがないことを確認しておくこと。

問題3(9点満点)speed.txtは30000台の車の地点Aにおける通過速度の架空データである。このデータを読み込み、平均値、標準偏差、95パーセンタイル値を求めなさい。なお95パーセンタイル値とは、100台に換算した場合の遅い方から数えて95番目(速い方から5番目)に相当する値である。例えば速度抑止対策を施した場合、速度の平均値の低下だけでなく、高速で走行する車の速度が低下したことを示すために95パーセンタイル値を参考にすることがある。

表計算ソフトには標準偏差を求めるSTDEVPという関数や、パーセンタイルを計算するPERCENTILEという関数がある。プログラムを実行するだけでなく、表計算ソフトを用いて計算を行い、間違いがないか確認しておくこと。

なお、95パーセンタイル値は、95パーセンタイル値=平均値+標準偏差×1.64で計算できる。1.64の意味を知りたい場合は標準正規分布表について勉強すること。

  • 提出先:課題提出用メールアドレス
  • 提出期限:第1提出期限、第2提出期限を設定
  • メールのSubject:課題1
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
  1. 作成したプログラム
  2. プログラムの実行結果
  3. プログラムの説明
  4. 感想
  5. ファイルの添付(プログラム。実施した場合は表計算ファイルも)

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(1番2点,2番2.5点、3番3点)、プログラムの説明(2点)、表計算ファイル(2番0.5点、3番1点)
  • プログラムの説明:使用した繰り返し表現について説明する。繰り返し表現が複数登場する場合にはそれぞれについて説明すること。なお単にWebページに記載された定義を丸写しにするのではなく、自分が作ったプログラムでは具体的にどのような値を指定し、繰り返し処理として何を実施しているのかを述べること。その他にもプログラムを作成して気づいた点があれば述べよ。
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点することがある。一度読み直してから提出すること。
  • 驚異的に良くできているレポートについては満点を超える得点をつけることがある。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。

Tips:emacsでの日本語入力のオンオフはCtrl+oです

Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする

Tips:Mewによるメールの送り方はMewコマンドを参照

speed.txt(タブ区切り形式のテキストファイル)の表計算ソフトでの読み込み方法

OpenOfficeを起動し、メニュー[ファイル]→[開く]からspeed.txtのように拡張子にtxtのついたテキスト形式のファイルを指定するとWriterで開かれてしまう。

データとデータの間をカンマで区切ったカンマ区切り(CSV)形式や、タブで区切ったタブ区切り形式、スペースで区切ったスペース区切り形式のテキストファイルをCalcで開く方法は以下の通り。

  1. [ファイル]→[開く]を指定し、ファイルの種類をテキストCSV(*.csv;*.txt;*.xls)にする
  2. speed.txtを選択し[開く](「全てのファイル(*.*)」のままでpower.txtを選択するとWriterで開かれてしまう)
  3. テキストのインポートメニューが表示されるので、区切りのオプションを「タブ」にしてOK
  4. これで完了。なお、データがカンマ区切りの場合は区切りのオプションで「コンマ」を選択し、スペースの場合は「スペース」を選択すればよい
  5. データ処理を行った場合は、このままの形式(txt形式)で上書き保存をすると計算式やグラフが保存できないので、ods形式やxls形式で保存する