何度も繰り返し

def -- end は何度も繰り返して使うことができ、 代入される値は、method を呼び出した返り値を入れてもよかった。 method を使うと、プログラムが少し短くなる。

def method(x, y, ...)
  # method その 1
end

while 終了する条件
    :
   method(a, b, ...)
    :
end

論理演算子についてまとめておく。

#  #  条件 A || 条件 B              # 条件 A または条件 B
#  #  条件 A && 条件 B              # 条件 A かつ条件 B

method は必ず引数から得た返り値を返す。 最後の計算の値を返す部分を return 文で変更できる。

#  #  return 値           # 値を返す
#  #  return nil          # 計算を止めてしまう場合

電話番号サービス

間違った電話番号にかけると、 「こちらは .... (電話会社名)です。この電話は現在使われておりません」 を繰り返した後、突然切れてしまう。 5 回アナウンスしたあとに、「回線を切断します」と言うプログラムを作ってみよう。 times_phonecall.rb

times do -- end という method を使うと、同じことを繰り返す。 times の前に整数値をつけて、何回実行するか決める。

繰り返す整数.times do
  実行すること             # 繰り返す整数値分繰り返される
end

何度か同じことをしたあと、 かってに終了するようなしくみを考えよ。

カウントダウン

前期では数を数えるのには while -- end というループを用いた。 while_rocket.rb

method を使って短く作ることを考えよう。 繰り返し method の一つ、downto do -- end を使うこともできる。 downto_rocket.rb

downto do -- end の使い方は、

始める整数.downto(終りにする整数) do |変数|
    変数の処理
end

である。

アルファベットの ASCII コードを調べ、 downto を使って z から 順番に a まで出力するようにせよ。

ガウスくんとルビ緒

ガウスくんとルビ緒が足し算で競争をした。 問題は、1 から順に 100 まで足したら、答えはいくつか、 簡単に答を出したガウスくん。

ガウスくんの解答方法は次のようなものであった。 「1 から 100 まで足した答えを x とします。 足し算の順番を入れ替えたものも用意します。 それも和の答えは x で変わりありません。二つを加えます。」

実際に調べてみよう。

  1 +   2 +   3 + .... +  98 +  99 + 100 = x
100 +  99 +  98 + .... +   3 +   2 +   1 = x

101 + 101 + 101 + .... + 101 + 101 + 101 = 2 x

10100                                    = 2 x 

「各項は 101 で、それが 100 個並んだのが右辺のものとして計算されるので、 右辺は 10100 となります。一方左辺は 2 x なので、これを解いて、

x = 5050

です。すなわち 1 から 100 まで足した答えは 5050 になります。」

一方ルビ緒は、プログラムを作った。upto do -- end を使ってみた。 upto_addition.rb 実行してルビ緒の答えも 5050 であるかどうか調べよ。

upto do -- end の構造は

始める整数.upto(終りにする整数) do |変数|
    変数の処理
end

である。

数値を指定して何度も計算させるには

第二問が出された。「2 から 99 まで加えた場合はどうか。」 ガウスくんは

「さっきの計算から、1 と 100 を引けばいいので、5050 - 101 = 4949 です。」

と答えた。一方ルビ緒は、プログラムを作った。 for_addition.rb

実行してルビ緒の答えも 4949 であるかどうか調べよ。

for -- end の構造は

for 変数 始める整数 .. 終りにする整数
    変数の処理
end

である。

続・ガウスくんとルビ緒

第三問目である。「1 から 99 までの奇数を足した答えは何か?」

ガウスくんは 「1 から 100 まで足したものから、 2 から 100 までの偶数を引いたものですね。先程の計算から 1 から 100 までの和は 5050 で、1 から 50 までの和は 51 が 25 個あるものが 2 倍されていると数えることができますから、 2 かける 51 かける 25」 と黒板に書き出した。

    1 + 2 + 3 + 4 + .... + 97 + 98 + 99 + 100
 -(     2     + 4             + 98 +    + 100)

=   5050 
  -2 (  1     + 2 + .... +      49 +       50) 

=   5050 - 2 * 51 * 25 

言葉を続けるガウスくんは、

5050 - 2550 = 2500

「答えは 2500 です。」

一方ルビ緒は、プログラムを作った。method には step もある。飛び飛びに計算してくれるのである。

開始する値.step(終了したい値[, 増える間隔]) do |変数|
 変数の処理
end

これを用いて、step_addition_odd.rb を作成した。 ルビ緒の答えも 2500 と出るだろうか?

なわとびの数

班別対抗のなわとびを行なうことになった。 なわとびをした順に、配列 jump に値を入れておくことにする。 ary_jump.rb を作って、合計の数を求めよう。

この処理では、配列要素の 0 番目から値を取っていくが、 配列に sort をつけると、要素の中の値が小さな順から取っていく。

# 文を外したりつけたりして、取り出される方法を確認しよう。

for 変数 in 配列
  変数に関する処理
end

班ごとに一番飛んだ人が代表で、 班代表が飛んだ回数でも競うことも予定している。 回数の多い順に取り出すにはどのようにしたらよいだろうか。

映画の星づけランキング

投票により、映画の星の数を平均しているプログラム def_ary_movie.rb はどのようなものだろうか。

終らせるには、Ctrl-D をタイプして計算を止める。例:


% ./def_ary_movie.rb 
星を入力して下さい: 3
星を入力して下さい: 4
星を入力して下さい: 3
星を入力して下さい: 2
星を入力して下さい: (Ctrl-D を押す)^D
 みんなの平均は 3.0 です

== は比較演算子、nil は、空の情報、という意味であった。 構造については こちら

グラフ機能をつけ通信販売や映画評論の総評プログラムを書いてみよう。

method の中に method

星付けランキングの method を見てみよう。 def -- end の中で複数の動作を行なっている。関数 average(score) は、配列 score を持つ。

for -- in -- end が使われているので、score は配列であることが分かる。

# for A in B # 配列 B の中の成分を A と名付け、一つずつ取り出す : # end

def -- end の中には for -- in -- end が組み込まれていて、 配列の中にしまわれた数を取り出しては sum に次々と足して行く。

for 文実行後、合計された sum を score の配列の大きさで 割って、平均を求めている。score.length で配列の大きさが分かる。 method の中の構造は

def 関数(配列) 
 |    命令文 1
 |    for 要素 in 配列        # 仮の要素名と仮の配列名をつけてある 
 |     |
 |     | 命令文2
 |     |
 |    end
 |    命令文 3
end

である。

while 文で入力するとき

定義文のあとのプログラムの本体部分について見て行こう。 while true -- end で、line でキーボードから入力された数値を順に、 points の各要素に詰めていく。 if 文がはさまっており、line に入っているものが nil (無効) ならば (何も入力されなければ = Ctrl-D を押す) break (止めよ) である。 構造は

while true
 |    命令文 1
 |    命令文 2
 |    if 変数 == nil      # キーボード入力が何もなければ 
 |     | break            # == 比較演算子
 |    end
 |    命令文 3
 |    i += 1
end
命令文 4

となっている。

るびおっち

method を用いると、 育て系のゲームを簡潔に作ることができる。 キャラクタを育てる「るびおっち」 def_rubiocci.rb

ここでは、体重と、幸せ度で、while -- end を抜ける。体重は fat(perday, exercise) を呼び出すことにより増え、 成長度は鳴く回数を制御する cry(ct) を呼び出すことにより、 鳴いた回数が画面に表示され、キャラクタがどのくらい成長したのか分かる。 また、while -- end を抜けたあとは、体重でエンディングが異なる。

ここでの method のうち、 cry(ct) message(name) は、 ct, name に代入された値をそれ以降の実行文で使用しない。 このようなものは、値を捨てている、と表現する。

ふくろうになる場合、 すずめになる場合、からすになる場合を、 printf で変数を途中明らかにしながら、それぞれ割り出せ。

工夫しだいでいろいろ

step を用いてカウントダウンを作ってもよい。 step_rocket.rb を作って試そう。環境によっては音が鳴る。

system は、kterm で入力するコマンドをプログラムの中に使いたいときに便利である。

砂時計は止まらない

整数の場合には問題がないが、小数値でループを作る場合には注意が必要である。 while_sand.rb

printf("5 秒砂時計 \n")
c = 50
while c != 0
  printf("%3.1f\r", c/10.0)
  c -= 1
  sleep(0.1)
end
system("banner 'TIME UP!'")

小数計算の誤差を避けるために、 わざと整数を割り算して作る必要がある。 計算機の少数の誤差は 2 進法で 10 進法を計算していることに起因し、 0.1 を 10 回引いても厳密に 0 にならない; 1/3 を少数で表示すると 0.33333.. で、これを 3 倍しても 1 にならないのと似た状況である。

\r はその場所に printf をしつづけるという意味である。

コマンドライン入力と method をあわせてみよう

ARGV 配列を使うと、プログラムを起動したのち、 ユーザが入力した値に基いて計算をするようなプログラムを作ることができる。 1 から任意の自然数まで足し算をするプログラムの実行風景は

./def_sumup.rb 100

となる。この場合は 100 を入力した場合が示されている。 def_sumup.rb

#!/usr/koeki/bin/ruby
#coding: euc-jp

if ARGV[0] == nil
  STDERR.puts("指定した値までの自然数を順に加え、総和を求めます。")
  STDERR.puts("例: ./sumup.rb 100  --- 1 から 100 までの総和を求める。")
  exit(1)
end

sum = 0
goal = ARGV[0].to_i	# ARGV にしまわれているものは文字列なので
                        # 整数変換が必要
s0 = 1                  # 終了の数

goal.downto(s0) do |number|
  sum += number
end

printf("%d から %d までの自然数の総和は %d です\n", s0, goal, sum)