roy > naoya > 基礎プログラミングII > (7)メソッド
(7) 11/15の授業内容:メソッド
[1]導入
歌の歌詞について考えてみよう(本当の歌詞を出すと著作権の侵害となりJASRACに怒られるので架空の歌詞とする)。多くの歌ではサビの部分は共通している。このため歌詞カードを見ると、2回目や3回目に登場するときは「*繰り返し」とだけ書かれていることが多い。
1.うんたらかんたら うんたらかんたら たりらり〜〜〜 *ららららら〜 れれれれれ〜 2.なんたらかんたら なんたらかんたら てれてれ〜〜〜 *繰り返し
もちろん、サビの部分で同じことを繰り返し書いても良い。しかし、「*繰り返し」の方が効率的であるし、行数も短くて済む。プログラムにおいても効率化を考えて、同じことを繰り返し書くかわりに以下のような構造をとることがある。あらかじめ冒頭に繰り返し部を記載しておき、プログラムの本文中で、必要に応じて呼び出すというものである。
これにより、同じことを何度も書く必要がなくなる。冒頭で呼び出されるプログラムを記載しておき、必要に応じて呼び出すために用いるのがdef-endである。def-endを用いる場面として以下をあげることができる。
- プログラム内で同じ処理が何度も繰り返し出現する。
- 複数のプログラムで同じ処理をする。2回目以降は冒頭に書いてあるものをコピーすれば、その部分は毎回書かなくても良くなる。
- プログラムの流れをわかりやすくする。細かな処理はdef-endに委ねることで、プログラム本体の流れがわかりやすくなる。
[2]def-endの基本的な用法
def-endの基本構造は以下の通りとなる。
def-endの基本構造
def メソッド名(引数リスト) 定義本体 end
簡単な例をみてみよう。
#!/usr/koeki/bin/ruby def plus(x) return x + 10 end number = 20 printf("10に10を足すと%dになります\n", plus(10)) printf("20に10を足すと%dになります\n", plus(number))
def-endで定義されたplusをメソッドと呼ぶ。このプログラムでは冒頭でplusメソッドを定義し、その下で実際にplusメソッドを呼び出して活用している。
def plus(x)とあるが、このうち(x)は引数リストと呼ぶ。これはplusメソッドを実行するためには引数が1つ必要であるということを意味する。メソッドと引数の関係をまとめると以下のようになる。
メソッドの定義と引数の関係
- def xxx():xxxメソッドは引数なしで実行可能
- def yyy(a):yyyメソッドを実行するためには引数が1つ必要
- def zzz(j,k):zzzメソッドを実行するためには引数が2つ必要
- def sss(x,y,z):sssメソッドを実行するためには引数が3つ必要
引数として指定する変数名はxでもyでもaでもbでも何でも構わない。
このプログラムで使われているplusメソッドに話を戻そう。def-end内を見ると、次のように書かれている。
def plus(x) return x + 10 end
このメソッドは引数を1つ必要とし、具体的には2行目にあるreturn x + 10を実行する。returnは呼び出されたところに返すという意味なので、呼び出されたときに与えられた引数xに10を足した値を返すということになる。
今度は、このプログラムの下部を見てみよう。
printf("10に10を足すと%dになります\n", plus(10)) printf("20に10を足すと%dになります\n", plus(number))
printfを用い、%dに表示する変数として、plus(10)およびplus(number)と書いている。ここでplusメソッドを呼び出している。plusメソッドは引数を1つ必要とするメソッドであるため、引数として10やnumberを与えている。numberには20が代入されている。
plusメソッドが呼び出されるとdef-endの部分に移動する。引数として10を与えた場合はxに10が代入されplusメソッドが実行される。つまり10+10や20+10が行われ、その結果の20や30が呼び出した場所に返される。
def-end内ではplusメソッドの引数にxを使用しているが、def-endの下でplusメソッドを呼び出す際は引数として10やnumberを与えている。xはdef-end内でのみ使用される変数であり、呼び出す際に与えられた引数が代入される。よって、def-end内の変数とdef-endの下の変数名が一致していなくても問題ない。
続けて幾つかの例を見ていきながらdef-endの用法について確認してみよう。
消費税を計算するメソッド:shohizei(shohizei.rb)
#!/usr/koeki/bin/ruby def shohizei(a) a * 1.05 end print"金額を入力してください" item = gets.chomp!.to_i printf("税込み金額は%d円です\n", shohizei(item))
shohizeiメソッドは引数を1つ必要とするメソッドであり、1.05倍した値を返す。ここではa * 1.05となっており、returnがついていない。returnは呼び出し元に返すという意味だが、省略可能である。
shohizeiメソッドを呼び出しているのは最後のprintfの行であり、printf("税込み金額は%d円です\n", shohizei(item))という形で呼び出している。itemはユーザが入力した金額が代入されており、これを引数として指定することで、shohizeiメソッドは1.05倍した結果の値を返してくれる。
shohizeiメソッドが呼び出されると、aにitemが代入され1.05倍される。その結果がprintfの行に返される。
消費税を計算するメソッド:tax(tax.rb)
#!/usr/koeki/bin/ruby def tax(x) price = x * 1.05 printf("税込み価格は%d円です\n",price) end STDERR.print "金額を入力してください" item = gets.chomp!.to_i tax(item)
これも消費税を求めるメソッドを作成した例である。ただしtaxメソッドはshohizeiメソッドと比較して、def-end内の行数が1行増えている。shohizeiメソッドは単に与えられた引数を1.05倍して返すだけであったが、taxメソッドは与えられた引数を1.05倍し、printfで結果を表示してくれる。この場合、呼び出した場所へ返されるのは最終行のprintfの行となる。
さらに、taxメソッドを呼び出す際の方法も異なっている。上記のプログラムでは、単にtax(item)とだけ記している。shohizeiメソッドは計算をした結果の数字を返してくれるだけであるため、その数字を表示できるような形で(すなわちprintfを使って)呼び出す必要がある。一方、taxメソッドではメソッドの働き自体にprintfによる表示が含まれるため、単にtax(item)で呼び出せば、計算をして結果も表示してくれることとなる。
[3]出席課題
tax.rbを改良し、金額を入力すると1割引の価格を計算して表示するプログラムを作成しなさい。なお、1割引の価格の計算と金額の表示をdef-end内で行うこと。メソッドの名称はdiscountとする。
制限時間は10分。出席点は2点。提出要領は下記の通り。
- 提出先:課題提出用メールアドレス
- メールのSubject:attend07
- 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降に作成したプログラム、実行結果を貼り付ける。
Tips:emacsでの日本語入力のオンオフはCtrl+oです
Tips:Mewによるメールの送り方はMewコマンドを参照
[4]def-end利用上の注意
ここでは、def-endを利用する上での注意点をまとめる。一部はこれまでの説明で出てきたものであり、新たに登場するものもある。後者についてはこの後で説明するが、注意点については複数の場所に記載されるとわかりづらくなるため、ここで一括して示す。
def-end使用上の注意
- 呼び出す前に定義をする:def-endで定義をしたメソッドを呼び出す場合、呼び出す前に定義されている必要がある。定義が下に書かれていても呼び出せない。
- 返り値であることを示すreturnはあってもなくても良い:原則として返り値は最終行となるため、これが返り値であるということを示すreturnはあってもなくても良い。
- 呼び出し方と返り値の関係を考える:単に計算した結果の数値を返すだけのメソッドなのか(例:shohizeiメソッド)、計算をしてその結果を表示してくれるメソッドなのか(taxメソッド)など、メソッドの持つ働きに応じて呼び出し方を変える必要がある。
- メソッドに引数として配列を渡すことができる:メソッドの中に配列を用いた処理を記載しておけば、引数は通常の変数でなくても構わない。
- メソッドを呼び出す際の引数は0〜∞まで指定できる:以下でプログラムのサンプルを示す。
- 引数にはデフォルト値を指定することができる:引数を必要とするメソッドを呼び出す際に引数を与えなければエラーになるが、デフォルトの値を指定しておくとその値が自動的に代入される。
[5]メソッドのサンプル(引数が異なる場合)
引数が0の場合(単にこんにちはと表示するだけのメソッド:hello)
#!/usr/koeki/bin/ruby
def hello()
print"こんにちは!\n"
end
hello
引数が不要な場合は、引数リストのカッコ内は空にする。helloメソッドはprint文でメッセージを表示するので、呼び出す際も単にhelloをすれば結果が表示される。
引数が2の場合(三角形の面積を求めるメソッド:triangle)
#!/usr/koeki/bin/ruby
def triangle(x,y)
menseki = x * y / 2
printf("面積は%4.2f平方センチメートルです\n",menseki)
end
STDERR.print"底辺をcm単位で入力してください"
teihen = gets.chomp!.to_f
STDERR.print"高さをcm単位で入力してください"
takasa = gets.chomp!.to_f
triangle(teihen, takasa)
triangleメソッドは2つの値を引数として与えると、計算をするだけではなくprintfで結果を表示する。このため呼び出す際も単にtriangle(teihen, takasa)とすればよい。
引数が2の場合(三角形の面積を求めるメソッド:sankaku)
#!/usr/koeki/bin/ruby
def sankaku(x,y)
x * y / 2
end
STDERR.print"底辺をcm単位で入力してください"
teihen = gets.chomp!.to_f
STDERR.print"高さをcm単位で入力してください"
takasa = gets.chomp!.to_f
result = sankaku(teihen, takasa)
printf("面積は%4.2f平方センチメートルです\n",result)
sankakuメソッドも2つの値を引数として受け取り面積を求めるものである。ただし返り値は計算をした結果の数字であるため、呼び出し方もtriangleメソッドとは異なっている。sankakuメソッドを呼び出して得られた返り値をresultに代入し、printfでresultを表示している。
メソッドのサンプル(メソッド内にifがある場合·引数として配列を指定する場合)
返り値が最後行とならない場合(大小比較を行うメソッド:min)
#!/usr/koeki/bin/ruby
def min(x,y)
if x < y
return x #returnは省略可能
else
return y #returnは省略可能
end
end
print"好きな数字を入力してください"
no1 = gets.chomp!.to_i
print"もう1つ好きな数字を入力してください"
no2 = gets.chomp!.to_i
printf("%dの方が小さいです\n", min(no1,no2))
minメソッドは2つの値を引数として受け取り、小さい方の値を返す。if文を用い、条件に応じた処理が行われるが、この場合は最終行が返り値とならない。最終行が返り値にならない場合でもreturnは省略することができる。
メソッドが配列を受け取る場合(募金の合計額を出すメソッド:total)
#!/usr/koeki/bin/ruby def total(yen) #yenは数値の入っている配列 sum = 0 x = 0 while x < yen.length-1 #.lengthで配列の中のデータの個数を調べる sum += yen[x] x += 1 end sum #最終行が返り値となるので単にsumと書いている end bokin = [] i = 0 while true STDERR.print"募金お願いします(0で終了)" bokin[i] = gets.chomp!.to_i if bokin[i] == 0 then break end i += 1 end printf("募金の合計は%d円でした\n",total(bokin))
プログラム下部ではbokinを配列として定義し、while-endの繰り返しを行う中で次々にbokinに値を代入している。最後にtotal(bokin)で配列のbokinを引数としてtotalメソッドに引き渡している。defの横はtotal(yen)と書いており、yenはdef-end内では配列として扱われている。配列と引数とする場合でもtotal(yen)の部分に特別な書き方はない。yenを配列扱いとして以後処理すればよい。
[6]メソッドのサンプル(引数にデフォルト値を指定する場合)
引数が必要なメソッドにおいて、引数が省略された場合に適用される値(デフォルト値)を指定しておくことができる。
#!/usr/koeki/bin/ruby
def sankaku(x,y=10)
x * y / 2
end
printf("面積は%4.2f平方センチメートルです\n", sankaku(5))
printf("面積は%4.2f平方センチメートルです\n", sankaku(5,8))
sankakuメソッドは2つの引数を受け取り、面積を計算してその値を返すメソッドである。本来2つの引数が必要であり、sankaku(5,8)のように2つの引数を与えるのが通常の呼び出し方である。しかし、このプログラムではsankaku(5)で呼び出している。
一方、def-endではsankaku(x,y=10)としてyに最初から10というデフォルト値を与えている。このため、sankaku(5)で呼び出した場合にはxに5が代入され、yは10が使用される。sankaku(5,8)で呼び出すとxには5、yには8が代入される。
メソッドを呼び出す際に与える引数は、先頭から順番に与えられる。上記の例でsankaku(5)とすると、5は必ずxに代入される。このため2つの引数を用いるメソッドを定義する際に、先頭の引数にデフォルト値を与え、2つ目の引数にデフォルト値を与えない場合、sankaku(5)で呼び出すとxに5が代入され、yには値がないことになる。この場合は実行するとエラーとなる。引数が2つの場合デフォルト値の与え方は以下の3種類があるが、×の場合はうまく動かないことがある。
メソッドへのデフォルト値の与え方
○ def sankaku(x=10, y=8) ○ def sankaku(x, y=8) × def sankaku(x=10, y)
[7]メソッドのサンプル(プログラム内で複数回呼び出す)
単一のプログラムの中で複数のメソッドを定義することができるし、一つのメソッドをプログラムの中で何度も使用することもできる。
対戦型ゲーム
(fight.rb)
#!/usr/koeki/bin/ruby hp1 = 30 #自分のヒットポイント hp2 = 30 #敵のヒットポイント time = 1 #sleepの待ち時間 #wazaメソッド(引数不要) #damageメソッドから呼び出される。 #3つの技からランダムにどれかが選ばれ、呼び出し元に返す。 def waza() sound = ["難しい宿題!","賞味期限の過ぎた牛乳!","オラオラオラオラッ!"] srand x = rand(3) return sound[x] end #damageメソッド(引数不要) #wazaメソッドで選ばれた技の名称と、1〜5のダメージをprintfで表示し #1〜5のダメージ(a)を呼び出し元に返す def damage() srand a = rand(5)+1 #1〜5がランダムに選ばれaに代入 printf(" %s %dのダメージを与えた\n",waza,a) return a end #hanteiメソッド(引数2つ必要) #自分と敵のヒットポイントを引数として受け取り #勝敗や戦闘の継続の判定をする def hantei(a,b) if a <= 0 && b > 0 print"あなたの負け\n" return 1 elsif a > 0 && b <= 0 print"あなたの勝ち\n" return 1 elsif a <= 0 && b <= 0 print"相打ちだ\n" return 1 else return 0 end end while true print"\nあなたの攻撃:\n" sleep(time) hp2 -= damage sleep(time) print"敵の攻撃:\n" sleep(time) hp1 -= damage sleep(time) printf("\n YOU:%d ENEMY:%d\n",hp1, hp2) if hantei(hp1,hp2)==1 exit(0) end sleep(time) end
pan{c10xxxx}% ruby fight.rb[Return] あなたの攻撃: 難しい宿題! 1のダメージを与えた 敵の攻撃: オラオラオラオラッ! 3のダメージを与えた YOU:27 ENEMY:29 あなたの攻撃: オラオラオラオラッ! 1のダメージを与えた 敵の攻撃: オラオラオラオラッ! 2のダメージを与えた YOU:25 ENEMY:28 : (以下略)
反応時間測定ゲーム
このプログラムは、みどりのように、表示された文字色の名前と表示色が異なる場合に、色の名前(ここでは「みどり」)と解答する時間が、双方が一致する場合と比べて遅れること(これをストループ効果という)を体験するプログラムである(stroop.rb)。
#!/usr/koeki/bin/ruby name = ["あか","みどり","あお","きいろ","しろ"] number = [31, 32, 34, 33, 37] text = [] color = [] result = [] def randomize_color(n) #10試行分の文字色(表示色)をランダムに作成 array = [] 10.times do srand i = rand(n) #0〜(n-1)の値をランダムに返しiに代入 array << i end return array end def check(start, stop, a, b) #正答の場合は反応時間を返し、誤答の場合はerrorと返す reaction_time = stop - start if a == b - 1 return reaction_time else return "error" end end def cls() sleep(1) print"\e[2J" #画面消去 printf("\e[%d;%dH", 0, 1) #カーソルを左上に移動 end cls system 'banner STROOP EFFECT' cls print"実験を開始すると、ディスプレイ左上に「あか」や「みどり」など、 色の名前が表示されます。 \e[33mみどり \e[mのように色の名前と表示色が異なる場合もあれば \e[31mあか \e[mのように色の名前と表示色が一致する場合もあります。 いずれの場合も表示色ではなく、\e[4m文字で書かれたの色の名前を回答して下さい。\e[m 例えば \e[34mみどり \e[mの場合は、「みどり」が答えになります。 回答は、「みどり」のように色の名前を入力するのではなく、 それぞれの色に割り当てられたキーを押して反応していただきます。 キーの割り当ては、 あか:1、みどり:2、あお:3、きいろ:4、しろ:5 となります。\n この実験では出現する色の種類を2種類から5種類まで自由に選択することができます。 例えば、3種類にすると、色の名前、表示色ともに「あか」「みどり」「あお」の 3種類となります。4種類にすると「きいろ」が追加されます。\n\n" while true print"何色にしますか。2から5で選んで下さい:" choice = gets.chomp!.to_i if choice < 2 || choice >5 print"入力できるのは2から5の整数のみです\n" redo else break end end print"全部で10試行行います。頑張って下さい。\n" color = randomize_color(choice) text = randomize_color(choice) 0.upto(9) do |j| cls start = Time.now #タイマースタート printf("\e[%dm%s\e[m\n",number[color[j]], name[text[j]]) answer = gets.chomp!.to_i stop = Time.now #タイマーストップ result << check(start, stop, text[j], answer) end p result
このプログラムで使用されている画面消去や文字色の変更、大きな文字の出し方、乱数発生などについては前期の自由課題の際に説明をしたプログラムを楽しくするためのテクニックのページを参照すること。
[8]レポート課題
問題(7〜10点満点):def-endを用いて面白いプログラムを作ってみよう。randやsleep、Time.nowなどを使ってみても良い。fight.rbやstroop.rbレベルで10点満点、このWebページ内の他のサンプルプログラムレベルだと7点満点で採点する(ただしfight.rbやstroop.rbの中身を多少書き換える程度の場合も7点満点)。複数のメソッドを定義し、プログラムを実行する中で複数回呼び出して活用すると得点が高くなる。
- 提出先:課題提出用メールアドレス
- 提出期限:11/21(日)23:00
- メールのSubject:課題4
- 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
- 作成したプログラム
- プログラムの実行結果
- プログラムの説明
- 感想
- 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(2点)、プログラムの説明(2点)、独自性(0点〜)
- プログラムの説明:def-end内に記述したメソッドの働きについて。また、どのような形でメソッドを呼び出し、メソッドが返り値として何を返しているのかについて。
- 独自性:他の先生を含めてWebページに公開されているプログラムをベースとした場合、みんなで協力して全く同じプログラムを作成した場合は独自性なしとする。自分なりのテーマ設定をしてみよう。面白いものやすばらしいものは独自性の配点を加点する。
- わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点することがある。一度読み直してから提出すること。
- 驚異的に良くできているレポートについては満点を超える得点をつけることがある。
- よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。
Tips:emacsでの日本語入力のオンオフはCtrl+oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照