これまで作成したプログラムでは変数をたくさん使ってきた。 変数は自分で好きな値を代入すると、それを保持してくれて、あとで参照したときにその値を返してくれる。
変数が数値、文字列、などのある時点の「値」のみを返してくれるものである一方、メソッドは何かの「手順」を行ない、その結果を返してくれる。
これまでよく利用してきた printf, gets, to_i
などはいずれもメソッドである。printf
はフォーマットを決めて文字列を出力してくれる、gets
は決められた場所からデータを読んでくれる、to_i
は元の値を整数に変換してくれる。このような働きをするのがメソッドである。
メソッドを理解する上で、数学で習った「関数」を思い浮かべると分かりやすい。
f(x) = 2x + 1
という定義をしておけば、f(4) のように呼び出すことで、2×4 + 1
を計算したことになる。
ここで、f に与えた 4 のことを引数(ひきすう) という。
関数は、引数に与えられた数値を x に代入して計算を行なう。
メソッドでもこの点は同様である。
メソッドを定義するには def
を利用する。
def メソッド名(引数リスト) 定義本体 end
簡単なメソッドを定義してみよう
def f(x)
2 * x + 1
end
これは、先ほどの f(x) = 2x + 1 と同じ内容なので、だいたい意味は分かるだろう。
def f(x)
メソッドとして f
という名前のメソッドを定義する。メソッドの名前は自由に選べるが、変数名の場合と同じような制限がある(使える文字は英字、数字とアンダースコアで最初の文字としては数字は使えない)。括弧 ( )
の中に書くのは引数リストという。そして、ここには x
とだけ書いてある。これは、
メソッドf
を呼ぶときには引数を1つだけ付けてね
ということを意味している。つまり、別の場所でf
を呼びたいときには f(5)
のように必ず一つの引数が必要となる。メソッド定義の引数リストに書く変数は 仮引数
といい、メソッドを呼んだときに与えた値が代入される。つまり、f(5)
と呼んだ場合 x
には5が代入される。
2 * x + 1
def
……end
の間に書くのがメソッドの定義本体である。ここには、Rubyの文を何でも書くことができる。
ここではたまたま、数学の関数のように数式を書いたが、
printf("2*%d + 1 は %dです\n", x, 2*x+1)
のようにRubyのメソッドを書いても良い。
end
end
でメソッド定義の終了を示す。
メソッドの定義を含むプログラムmethod-1.rb
を作ってみよう。
method-1.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def f(x)
2*x + 1
end
実際に実行してみよう。
% chmod +x method-1.rb
% ./method-1.rb
なにが起きるだろうか。
メソッドは定義しただけでは動かない。プログラムの別の箇所で呼び出ししなければなにもせずに終わってしまう。これは数学の答案で最初の方に
f(x) = 3x + 1 とする
と書いたのに、別のところで f(x) を使わなければ意味がないのと同じである。
メソッドを呼び出すには、呼び出したい位置で
メソッド名(引数)
と書くだけで良い。たとえば、先ほど定義した f
メソッドを呼ぶには、
f(3)
のようにすればよい。プログラムを改良してみよう。
練習問題method-2.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def f(x)
2*x + 1
end
puts "数値を入れて下さい。2倍して1足します。"
y = gets.to_i
printf("%d\n", f(y))
f(y)
の部分が、定義したメソッドの呼び出しである。ここでは、直前の行で読み込んだ数値をy
に入れていて、それをf
メソッドに渡している。f
メソッドでは、仮引数 x
に入力した値を代入して計算した結果を返す。
では、次のようなプログラムはどんな結果を返すだろうか。
練習問題method-3.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def g(x)
a = x*2
b = 1
c = a + b
end
puts "数値を入れて下さい。計算した結果を表示します。"
y = gets.to_i
printf("%d\n", g(y))
今度は g
メソッドに3行の式がある。
a = x*2
b = 1
c = a + b
のどの行が g(x)
の値になるのだろうか? たとえば5を与えたら
a = 5*2
→ 10
b = 1
→ 1
c = a + b
→ 11
10か1か11が返ると予想できる。実際に実行してみよう。
% ruby method-3.rb
数値を入れて下さい。計算した結果を表示します。
5
11
このように、メソッドでは、そのメソッドを実行するときに最後に実行した式(行)の値がメソッドの実行結果として返される。 これを戻り値または返却値という。
また、引数を2つ以上利用するときは
def foo(x, y) x + y*2 end
のように引数リストで仮引数をカンマ(,
)で区切って列挙する。この例の場合呼出し側では2個の値を渡す必要がある。
つまりfoo(2, 5)
のように呼ぶ。
引数が不要の場合は
def bar() ……なんらかの内容…… end
のように、引数リストの括弧内は空にする。
メソッドの中途でその実行を中断し直ちに呼び主に制御を返すこともできる。
def is_bigger(num1, num2)
if num1 > num2
return true
end
return false # else を使っていないことに注意
end
上記の例では、num1
が num2
より大きい場合に3行目で直ちに true
を返しメソッドを抜ける。そうなった場合は残りの部分(4行目以降)は実行されないので5行目の return false
を else
に入れなくても問題ない。
必ずしも数学の関数のように「結果の値」を返してもらう必要はない。 たとえば、次のようなメソッドも考えられる。
def hello(who) printf("%s さんこんにちは!\n", who) end
このメソッドでは、仮引数として who
をもらい、
それが文字列だとして 「さんこんにちは!」を後置したメッセージを出力するというだけのものである。実際に利用してみよう。
aisatsu.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def hello(who)
printf("%s さんこんにちは!\n", who)
end
puts "あなたの名前を入れて下さい。"
you = gets.chomp
hello(you)
puts "あなたの近くに座っている人の名前を入れて下さい。"
friend = gets.chomp
hello(friend)
実際に実行してみよう。
% chmod +x aisatsu.rb
% ./aisatsu.rb
あなたの名前を入れて下さい。
taro
taro さんこんにちは!
あなたの近くに座っている人の名前を入れて下さい。
hanako
hanako さんこんにちは!
メソッドは、決まった式の計算をしてもらうこともできるし、 決まった仕事をしてもらうこともできる。
ちなみに、printf
メソッドも仕事をした結果として値(nil
)を返している。
したがって、hello
メソッドの戻り値も nil
となる。ただし、呼出し側の方で、
hello
メソッドの返す値を全く利用していないのでこの値は捨てられることになる。
もう少し複雑なメソッドを定義してみよう。
金額を渡すと消費税を計算するメソッドを作成してみよう。 税率として「10%固定」というのでは安物電卓と変わらなくて悔しいので税率も選べるようにしよう。
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def tax(price, ratio) # priceに値段、ratioに税率をもらう
charge = price*ratio
charge.to_i
end
ここで定義したtax
メソッドを呼び出すときは、第1引数に金額、第2引数に税率を指定する。
tax(250, 1.10)
これでtax
メソッドで price=250, ratio=1.10
となり、税率10%で計算した結果を返してくれる。
配列をメソッドに渡すこともできる。次のメソッドは、 数値が複数入っている配列を受け取り、それらの値の平均値を求めるものである。
def average(scores) # scoresは数値のたくさん入っている配列 sum = 0.0 # 割り算する予定なので浮動小数点数にしておく for x in scores sum += x end sum/scores.length end
これを利用して、入力した数値全ての平均値を出力するプログラム average.rb
を作ると以下のようになる。
average.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
def average(scores) # scoresは数値のたくさん入っている配列
sum = 0.0 # 割り算する予定なので浮動小数点数にしておく
for x in scores
sum += x
end
sum/scores.length
end
points=[] # からっぽの配列を作る
i=0 # iがpointsの添字となる
while true
print "数値を入力して下さい: "
line=gets
if line == nil
break
end
points[i] = line.chomp.to_f
i += 1
end
printf("\n平均値は %6.2f です\n", average(points))
実際に実行してみよう。データの入力の最後には[C-d]をタイプする(「Ctrl」キーと「d」を同時に押す)。
% ./average.rb
数値を入力して下さい: 1
数値を入力して下さい: 2
数値を入力して下さい: 3
数値を入力して下さい: 4
数値を入力して下さい: [C-d]
平均値は 2.50 です
今回の課題では、メソッドを使って基礎プログラミングIの第7回の時に作ったプログラム(test_results.rb
)を書き直して、改良する。
以下のソースコードは、メソッドの呼び出しだけを含むが、メソッド自体がまだ記述されていない作りかけである。完成させよ。
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
# ここでメソッドを定義すること
data = "scores.txt"
while true do
puts "OPTIONS:"
puts "1 - 全ての点数を出力"
puts "2 - 範囲を指定し点数を出力"
puts "3 - 点数を追加"
puts "4 - 平均点数を計算"
puts "0 - 終了"
input = gets.to_i
if input == 0 then
break
elsif input == 1 then
show_scores(data, 0, 100)
elsif input == 2 then
puts "表示する点数の下限値を入力してください"
min = gets.to_f
puts "表示する点数の上限値を入力してください"
max = gets.to_f
show_scores(data, min, max)
elsif input == 3 then
add_score(data)
elsif input == 4 then
printf("平均点数: %5.2f\n\n", calc_avg_score(data))
else
print "1/2/3/4/0から選んでください\n\n"
end
end
データ:scores.txt
記述するメソッドについて:
show_scores
scores.txt
のようなフォーマットの点数データから、点数が指定された下限値(min
)以上で上限値(max
)以下である行を表示する。add_score
scores.txt
)に新しい項目を追加することができる。
入力された点数が0~100の範囲内でなければ、メッセージを表示しもう一度入力させる仕組みにすること。calc_avg_score
scores.txt
のようなフォーマットの点数データから、平均点数を計算して結果を返す。注意:このメソッドは平均点数を表示するのではなく、戻り値として返す。ヒント:学籍番号と点数を含むデータから点数だけを抽出するためには正規表現を使うことができる。結果は文字列になるので、計算などに使う前に必ず数値へ変換をすること。
実行結果の例:
{c11xxxx}% ruby test_results2.rb
OPTIONS:
1 - 全ての点数を出力
2 - 範囲を指定し点数を出力
3 - 点数を追加
4 - 平均点数を計算
0 - 終了
4
平均点数: 86.25
OPTIONS:
1 - 全ての点数を出力
2 - 範囲を指定し点数を出力
3 - 点数を追加
4 - 平均点数を計算
0 - 終了
3
学籍番号を入力してください
C199000005
点数を入力してください
105
0から100までの範囲で入力してください
100
点数が追加されました
OPTIONS:
1 - 全ての点数を出力
2 - 範囲を指定し点数を出力
3 - 点数を追加
4 - 平均点数を計算
0 - 終了
4
平均点数: 89.00
OPTIONS:
1 - 全ての点数を出力
2 - 範囲を指定し点数を出力
3 - 点数を追加
4 - 平均点数を計算
0 - 終了
2
表示する点数の下限値を入力してください
80
表示する点数の上限値を入力してください
90
C199000002 80
C199000003 81
C199000004 84
OPTIONS:
1 - 全ての点数を出力
2 - 範囲を指定し点数を出力
3 - 点数を追加
4 - 平均点数を計算
0 - 終了
0
発展課題