roy > naoya > 基礎プログラミングII・情報表現[月1] > (3)様々な繰り返し表現

(3) 10/22の授業内容:様々な繰り返し表現

繰り返し表現の復習

前期には繰り返しの記法としてwhile-endを用いてきた。まずはこの記法について確認し、その後その他の繰り返し表現の方法について確認していく。

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

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

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

while-endの基本構造

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

whileの横に書くのは x < 10 や y >= i のような繰り返しの継続条件である。この条件を満たす間はwhile-end間の処理を繰り返し実行する。具体例をあげながらもう一度確認してみよう(add10.rb)。

#!/usr/koeki/bin/ruby

sum = 0
goal = 10
i = 1

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

printf ("sumは%dです\n",sum)
irsv{naoya}% ruby add10.rb[Return]
sumは55です。

このプログラムでは、3つの変数を使用しており、それぞれsum=0、goal=10、i=1という初期値を与えている。その後、while-endがあるが、whileの横には「i <= goal」とある。これが繰り返しを継続する条件である。具体的に繰り返す内容は、while-end間に書かれている「sum += i」と「i += 1」の2行である。

繰り返しを継続する条件である「i <= goal」は「igoal以下」という意味になる。iは初期値が1であり、goalは初期値が10である。while-end間を見ると、「i += 1」があることから、iの値は変化する(より厳密にはwhile-endの繰り返しを実施するたびに1ずつ増加する)ことになる。それに対してgoalは初期値が与えられているのみで、その後値は変化しない。つまり、iが1から2、3、4、・・・と増加していき、最終的に10になるまで繰り返し処理を継続せよということになる。

具体的に繰り返し継続される処理は「sum += i」と「i += 1」であった。これは「sumに現在のiの値を加算せよ」および「iに1加算せよ」ということになる。

while-end部の繰り返しがどのように行われるのかを表にして示してみよう。

繰り返し回数

while i <= goal

sum += i

i += 1

1回目

iは1なので条件を満たす

sumは1に

iは2に

2回目

iは2なので条件を満たす

sumは3に

iは3に

3回目

iは3なので条件を満たす

sumは6に

iは4に

4回目

iは4なので条件を満たす

sumは10に

iは5に

5回目

iは5なので条件を満たす

sumは15に

iは6に

6回目

iは6なので条件を満たす

sumは21に

iは7に

7回目

iは7なので条件を満たす

sumは27に

iは8に

8回目

iは8なので条件を満たす

sumは35に

iは9に

9回目

iは9なので条件を満たす

sumは44に

iは10に

10回目

iは10なので条件を満たす

sumは55に

iは11に

11回目

iは11なので条件を満たさない(終了)

実行されない

実行されない

繰り返しを行うごとにsumおよびiの値が増加していき、11回目の時点でiが11となり「while i <= goal」の繰り返し条件を満たさなくなるためこれで終了となる。

while true

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

以下は前期のレジスターのプログラムであり、購入した商品の単価(price)を連続して入力することで、購入した商品数(item)と合計金額(sum)を算出するものである。そして、ユーザがキーボードから「q」を入力した場合に繰り返しから抜けよという指示がされている。

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

checkstand.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)
irsv{naoya}% ruby checkstand.rb[Return]
金額を入力してください(終了はq)
100[Return]
現時点の購入商品の個数は1個、合計は 100 円です
金額を入力してください(終了はq)
150[Return]
現時点の購入商品の個数は2個、合計は 250 円です
金額を入力してください(終了はq)
q[Return]
ありがとうございます。お会計は 250 円になります

このプログラムの強調部分(黄色の部分)が繰り返しから抜けるかどうかの判定部であるが、「if price == "q" 」となっている。右辺は"q"であるが、文字列の場合は""をつける。もしも数字で9999と入力したら終了としたければ「if price == 9999」となる。ただしこのプログラムに関しては「if price == "9999"」と書かなければならない。「price = gets.chomp!」の行で入力した値を取得してpriceに代入しているが、getsメソッドは文字列として取得する。つまり9999と入力したとしても整数の「きゅうせんきゅうひゃくきゅうじゅうきゅう」ではなく文字列の「きゅうきゅうきゅうきゅう」として扱われていることになる。整数に変換するためには.to_iメソッドを使用する必要があり、「price = gets.chomp!.to_i」としなければならない。このプログラムでは「sum += price.to_i」の行でpriceに.to_iをつけ整数に変換している。「price = gets.chomp!.to_i」としていないのは、ユーザが金額(整数)と終了のq(文字列)を入力する可能性があり、最初に全て整数に変換してしまうと都合が悪いからである(文字を.to_iメソッドで整数に変換すると0になる。)。このため「price = gets.chomp!.to_i」とするとqと入力しても0に変換されてpriceに代入されてしまい、price内の値がqである場合に繰り返しから抜けるという条件を満たすことがなくなってしまう。

出席課題

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

#!/usr/koeki/bin/ruby

money = 0

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

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

制限時間は10分。出席点は2点。提出要領は下記の通り。

  • 提出先:naoya@e.koeki-u.ac.jp
  • メールのSubject:attend03
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降で変更箇所を述べる。

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

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

その他の繰り返し表現について

情報検索の授業では、繰り返しを行う際、主にwhileを用いてきた。ただし繰り返し処理にはwhile以外にもいくつかの方法がある。様々な方法を理解して自在に扱えるようにしよう。

一定回数繰り返す(times)

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

#!/usr/koeki/bin/ruby

4.times {
  print "Hello\n"
}
#!/usr/koeki/bin/ruby

4.times do
  print "Hello\n"
end
irsv{学籍番号}%chmod +x times.rb
./times.rb
Hello
Hello
Hello
Hello

1ずつ増やしながら(upto)・減らしながら(downto)繰り返す

1から10までの合計を出すことを考えてみよう。while-endで記述した場合以下の通りとなる。

#!/usr/koeki/bin/ruby

goukei = 0
i = 1

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

printf ("goukeiの値は%dです\n",goukei)

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

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

#!/usr/koeki/bin/ruby

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

printf ("goukeiの値は%dです\n",goukei)

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

#!/usr/koeki/bin/ruby

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

printf ("goukeiの値は%dです\n",goukei)

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

uptoメソッドの基本構造

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

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

downtoメソッドの基本構造

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

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

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

飛び飛びに繰り返す(step)

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

#!/usr/koeki/bin/ruby

goukei = 0
i = 10

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

printf ("goukeiの値は%dです\n",goukei)

このように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

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

printf ("goukeiの値は%dです\n",goukei)

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

いろいろ使える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

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

printf ("goukeiの値は%dです\n",goukei)

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

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

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

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

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

#!/usr/koeki/bin/ruby

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

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

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

レポート課題

以下のうちいずれかを選んで解答する。プログラムはwhile-end以外の繰り返し表現を用いて記述すること。while-endを用いた場合や繰り返し表現を用いなかった場合は、配点を1点マイナスとする(例えば、9点満点の問題は8点満点で採点)。

問題1(7点満点):指定した任意の自然数(=正の整数)からはじめて、100までのその自然数の倍数を全て足した値を求めるaddsum.rbを作成せよ

問題2(9点満点):1から100までの素数の和を求めるprime.rbを作成せよ。ここで、素数とは1とその数以外に約数がない正の整数を指し、結果は例えば素数が1,2,3,5である場合「1+2+3+5=11」というように式形式で表示されるようにせよ。1から100までの値がそれぞれ素数であるかどうか判定した上で、素数の合計を求めること。

  • 提出先:naoya@e.koeki-u.ac.jp
  • 提出期限:10/28(日)23:59
  • メールのSubject:課題1
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
  1. 作成したプログラム
  2. プログラムの実行結果
  3. プログラムの説明
  4. 感想

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

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

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

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