hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

引数

違うスコープの変数を利用する

 下記のソースコードではエラーが出る

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
 def post_review
    # 変数の定義
    post = {}
    puts "ジャンルを入力してください:"
    post[:genre]  = gets.chomp
    puts "タイトルを入力してください:"
    post[:title]  = gets.chomp
    puts "感想を入力してください:"
    post[:review] = gets.chomp
    line   = "---------------------------"

    # レビューの描画
    puts "ジャンル : #{post[:genre]}\n#{line}"
    puts "タイトル : #{post[:title]}\n#{line}"
    puts "感想 :\n#{post[:review]}\n#{line}"

    # 配列オブジェクトに追加
    posts << post
  end

  ## 以下省略

18行目の配列オブジェクトpostsは、post_reviewメソッドの外で定義した変数なのでpost_reviewメソッド内では使えない。
「メソッドの外で定義した変数」を「メソッドの中」で使えるようにするには引数を使う。

引数(ひきすう)

引数(ひきすう)はプログラムでメソッドなどに渡すことのできる値のこと。引数(ひきすう)を使うことによって、メソッドの外にある変数(スコープ外の変数)をメソッドの中で使うことができる。

引数でメソッドに変数を渡す

メソッドは「何か材料を渡したら共通の処理を施した後に何か値を返してくれるもの」と表現することができる。ジューサーはジュースを作るものだが、リンゴを材料とするとリンゴジュースが、オレンジを材料とすればオレンジジュースができる。材料とするものによって出来上がるジュース(結果)が変わる。引数は、この時のリンゴやオレンジのような材料にあたる。引数によってメソッドの実行結果は変わるということになる。

引数のポイント

引数には以下の2つのポイントがある。

  • 引数は「メソッドを呼び出す部分」と「メソッドを定義している部分」両方に書く
  • 「メソッドを呼び出す部分」に書く引数と「メソッドを定義している部分」に書く引数の名前は、一致している必要はない

 

1. 引数は、「メソッドを呼び出す部分」と「メソッドを定義している部分」両方に書く

メソッドを呼び出す際に書く引数のことを本引数と言う。メソッドの呼び出し時は、以下のように本引数を書く。

1
  メソッド名(本引数)

本引数に対して、メソッドを定義している部分に書く引数のことを仮引数と言う。メソッドを定義している部分には、以下のように仮引数を書く。メソッドの処理の中では、引数として渡されたものを仮引数としてつけた名前で扱う。

1
2
3
  def メソッド名(仮引数)
    実行する処理
  end

【例】引数を用いたメソッドの実行

1
2
3
4
5
6
7
8
  def multi(input)
    puts input * input
  end

  puts "何か数字を入力してください"
  input = gets.to_i

  multi(input)

メソッドmultiは、引数として渡した数を2乗した数字を出力してくれるメソッド。

1 ~ 3行目でメソッドmultiを定義する。5行目のputsメソッドで「何か数字を入力してください」と出力されたあと、6行目でユーザーが入力した数字が変数inputに代入される。

そして、8行目でメソッドmultiを実行している。その際、メソッドの名の後ろの()の中にinputが書かれていることに注目する。この記述により、メソッドmultiが実行される際にinputを引数として利用することが出来る。

1行目、メソッドの定義側を見ると、仮引数はinputとなっている。なので、メソッドmultiの処理中は、本引数inputinputという変数として利用することになる。

2. 「メソッドを呼び出す部分」に書く引数と「メソッドを定義している部分」に書く引数の名前は同じである必要はない

どのような名前の本引数が渡されても、メソッドの中では仮引数の名前で扱うため、本引数と仮引数の名前が同じである必要はない。
引数が渡されたメソッドの性質によって名前を変えた方がわかりやすい場合がある。例えば、先ほどのメソッドの仮引数を以下のようにしても問題なく動作する。

【例】仮引数の名前を変更

1
2
3
4
5
6
7
8
  def multi(number)
    puts number * number
  end

  puts "何か数字を入力してください"
  input = gets.to_i

  multi(input)

仮引数の変数名をnumberに変更。inputとしてメソッドmultiに引き渡された引数の値は、メソッドmultiの中では変数numberに代入された状態で利用される。

仮引数の名前について

仮引数の名前は何でも良いのですが、どんな人が読んでもそれが何なのかわかるものにするべき。例えば、メソッドの仮引数がabでは、それが何なのかわかりづらくなる。

【例】わかりづらい仮引数

1
2
3
  def multi(a)
    puts a * a
  end

メソッドmultiの中で掛け合わされるのは必ず数字なので、ここはnumberにするべき。

メソッド自体の答えを明示的に示すreturn文

現在メソッドmultiは引数として渡された数字の2乗の結果を出力するだけ。この結果をメソッド自体の値とする場合は、以下のように3行目を追記する。

1
2
3
4
5
6
7
8
9
  def multi(number)
    puts number * number
    return number * number
  end

  puts "何か数字を入力してください"
  value = gets.to_i

  multi(value)

上記のようにすることで、9行目のメソッドmultiの式の答えはnumber * numberの答えになる。

 return

全てのメソッドには返り値があると、紹介した。メソッド内でreturn ◯◯とすると、return(リターン)のあとに続けた式がそのメソッド自体の返り値になる。returnを利用した時点で返り値が決まるため、メソッドはその行までで強制的に終了する。

【例】returnを利用した場合

 return文でメソッドは返り値を返してそのまま処理を終了する。return文のあとにコードがあっても無視される。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  def sample
    "1"
    "2"
    "3"

    return "4"           # ここで処理は終わり

    "5"                # 呼ばれない
    "6"                # 呼ばれない
  end

  puts sample            # => 4

【例】returnを利用しない場合

Rubyではreturn文を省略することが可能。 returnを省略した場合、メソッドの返り値はそのメソッドの中で最後に実行された式の値となる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  def sample
    "1"
    "2"
    "3"
    "4"
    "5"
    "6"                # 最後に実行されている
  end

  puts sample            # => 6

Rubyでは、どんな記述も式と捉え、その返り値を持つ。上記のように文字列を書いておくだけの場合、返り値はその文字列の値となる。

 

要点チェック

 

本引数と仮引数の関係①

 

sample.rb
1
2
3
4
5
6
  def mixer(fruit)
    puts "#{fruit}を細かく砕く"
    return "#{fruit}ジュース"
  end

  puts mixer("リンゴ") # => リンゴジュース

メソッドの呼び出し側で渡された引数(リンゴ)は引数を入れる変数(fruit)としてメソッド内で使うことができる

6
  puts mixer("リンゴ")           # この"リンゴ"という値がメソッドmixerに渡される。

puts 〇〇 とすればputsに引数として渡した値が出力される。今、〇〇の部分がmixer("リンゴ")となっている。そこでまずメソッドmixer自体の返り値(メソッド自体の答え)を確定させるために、メソッドmixerが実行される。

1
2
3
  def mixer(fruit)                # 渡された"リンゴ"という値が引数を入れる変数fruitに代入される。
    puts "#{fruit}を細かく砕く"     # 今、fruitには"リンゴ"という値が入っている。
    return "#{fruit}ジュース"      # "リンゴジュース" を返す。

以上より、mixer("リンゴ")は、"リンゴを細かく砕く"を出力した後、"リンゴジュース"を返り値とする。

1
2
3
4
5
6
  puts mixer("リンゴ")    # mixer("リンゴ")を出力するとその返り値である"リンゴジュース"が出力される
リンゴジュース

  juice = mixer("ブドウ") # 変数に代入もできる
  puts juice
=> ブドウジュース # puts juiceの実行結果

引数を使うと、渡す引数によって結果を変えることができるので非常に便利。6行目の「puts mixer("リンゴ") 」の"リンゴ"をいろんな文字列に変えると結果の出力も変わる。

 

要点チェック

 

本引数と仮引数の関係②

sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  rename(name)
  puts name

ここで定義しているメソッドrenameは引数で渡した変数nameにMr.をつけるという処理を行っている。しかし、実行してみるとわかるように7行目のputsではMr.TanakaではなくTanakaと表示される。

1
2
$ ruby sample.rb
Tanaka

引数で渡した値はメソッドで変数に入れられてメソッド内で使用される。このとき実は、メソッドの変数には引数で渡した値のコピーが代入されている。


引数のnameとrenameメソッドで受け取る変数nameを以下のように色分けして考える。

値渡し①

ここで重要なのは引数のnamerenameメソッドで受け取る変数nameは名前は同じだが、別物であるということ

  • まず、5行目でnameに文字列"Tanaka"が代入される。
sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  rename(name)
  puts name

値渡し②

  • 次にこのnameを引数としてメソッドrenameを呼び出す。このときnameの中に入っている値がコピーされる。この場合、文字列"Tanaka"の値がコピーされる。
sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  rename(name)
  puts name

値渡し③

  • コピーされた文字列"Tanaka"はメソッドrenameの変数nameに代入される。よって、引数nameとメソッドで使われる変数nameの中身の値は同じになり、メソッドに値を渡すことができる。
sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  rename(name)
  puts name

値渡し④

  • メソッド内の変数nameの値を変えても、この変数と引数の変数nameは別物であるため引数の変数nameには影響を与えない。
sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  rename(name)
  puts name

値渡し⑤


よって、変数nameにMr.をつけるメソッドrenameは以下のようにすれば期待通りに動くようになる。

sample.rb
1
2
3
4
5
6
7
  def rename(name)
    name = "Mr.#{name}"
  end

  name = "Tanaka"
  name = rename(name)
  puts name
1
2
$ ruby sample.rb
Mr.Tanaka

 

メソッドrenameでは引数から受け取ったnameにMr.をつけた文字列を返り値としてメソッドの呼び出し元に返す。

sample.rb
1
2
3
  def rename(name)
    name = "Mr.#{name}" # nameにMr.をつけた文字列をname自身に代入して、nameを返り値として返す
  end

以前はrename(name)と、単にメソッドrenameを呼び出しただけだったが、今回はname = rename(name)とメソッドからの返り値をnameに代入している。

sample.rb
7
  name = rename(name) # nameにrenameメソッドの返り値を代入

これで、nameの値が、メソッドrenameによってMr.をつけた文字列になります。