メソッド


メソッドとは

これまで作成したプログラムでは変数をたくさん使ってきた。 変数は自分で好きな値を代入すると、それを保持してくれて、あとで参照したときにその値を返してくれる。

変数が数値、文字列、などのある時点の「値」のみを返してくれるものである一方、メソッドは何かの「手順」を行ない、その結果を返してくれる。 これまでよく利用してきた 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 と同じ内容なので、だいたい意味は分かるだろう。

メソッドの定義を含むプログラム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行の式がある。

のどの行が g(x) の値になるのだろうか? たとえば5を与えたら

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

のように、引数リストの括弧内は空にする。


return文

メソッドの中途でその実行を中断し直ちに呼び主に制御を返すこともできる。

def is_bigger(num1, num2)
  if num1 > num2
    return true
  end
  return false # else を使っていないことに注意
end

上記の例では、num1num2 より大きい場合に3行目で直ちに true を返しメソッドを抜ける。そうなった場合は残りの部分(4行目以降)は実行されないので5行目の return falseelse に入れなくても問題ない。


仕事をするのみのメソッド

必ずしも数学の関数のように「結果の値」を返してもらう必要はない。 たとえば、次のようなメソッドも考えられる。

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

  • 提出先:S4の 「第2回の課題提出」に提出してください.
  • 提出期限:2023年10月12日(木)

 発展課題 

  1. 基本課題のプログラムで、「2 - 範囲を指定し点数を出力」が選ばれた場合の下限値と上限値の入力手順も2つのメソッドとして実装せよ。入力された数値が0~100の範囲内ではない場合や上限値が下限値より小さい場合にはもう一度入力させる仕組みにすること。
  2. 「基礎プログラミングI」でチームで開発したプログラムを分析し、その中で行われている手順(例:「データを読み込みする」、「メニューを表示する」、「カードを配る」)のリストを作る。そしてプログラムを修正し、それぞれの手順をメソッドとして実装する。

目次