さらなる高みへ -higher ground -

プログラミング学習の定着を狙いとしたアウトプット(独り言)をしてみるブログ

メソッドについての復習

ここまでの「メソッド」「引数」「返り値」「スコープ」などの概念について、もう一度確認しておきたい。

以下にサンプルコードを用意した。

def rename(name)
  name = "Mr.#{name}"
end

name = "Tanaka"
rename(name)
puts name

名前の先頭に「Mr.」を追加する、renameメソッドを作成したはずなのだが、実際に出力されたのは「Tanaka」だけになってしっている。
なぜこのような動作になるのか。

メソッドの使い方

https://tech-master.s3.amazonaws.com/uploads/curriculums//535b466574db2490a657c3ae3b3c201c.png

 

  • メソッドとは

「メソッド」という考え方は、会社での「組織」と似ている。
小さな会社であれば、役割分担が明確でなくてもうまく回るかもしれない。
しかし、組織が大きくなれば、部署ごとに役割を分けないとうまく仕事を捌けないだろう。

プログラミングでも、コード量が増えるに従って、役割ごとの分割を行わないと見通しが悪くなる。
エラーの修正や機能の追加がしにくくなってしまうためである。
メソッドは、そのようなことが発生しないように機能ごとにコードを適切に分割する仕組みである。

  • メソッドの実行とは

メソッドを実行することは、他部署に仕事を依頼することに似ている。
依頼する内容は、毎回全く同じということは少ないだろう。

上図の例では、総務部に備品の購入を依頼している。
買って欲しい商品は、その時々で違うはずである。
「どの商品」を買って欲しいかを伝えるデータが、プログラミングでいう「引数」に当たる。

  • 引数とは

引数とは、上で見たように処理をお願いするときに渡すデータのことである。
メソッドが果たす大まかな機能はあらかじめ決まっているが、引数があることによって細かい作業の指示を行える。

同じ発注という作業でも、「ボールペンを買って欲しい」「ガムテープを買って欲しい」などの異なる内容でお願いをすることができる。

なお、メソッドの実行を依頼する側の引数を「本引数」、メソッドが受け取る引数を「仮引数」と呼ぶ。
中身は全く同じものだが、引数の名前は変えることができる。

  • 返り値とは

返り値とは、メソッドの呼び出し元に返す処理結果である。
会社の例でいうと、発注して届いたコピー用紙を担当者に渡すようなものである。

ここまで見てきたように、メソッドは一般的に以下の流れで使用する。

  1. 「引数」を渡してメソッドを実行する
  2. メソッド内で、受け取った引数を元に処理を行う
  3. メソッドは、処理が終わったらその結果である「返り値」を返す。

スコープとは

続いて、スコープという考え方についてである。

スコープとは、変数が有効な範囲のことである。
書いたプログラムの中に、見えないブロックがあるとイメージする。

上図のようにブロックがあって、人の目には同じ「name」という変数だったとしても、スコープが異なればプログラム的には別の変数として扱われる。
そのため、一番上の「name」に別の値を代入したとしても、他の「name」には全く影響がない。

スコープの意義

では、なぜスコープという考え方があるのだろうか。
値を変更したのに、中身が変わらない変数があることは一見不便に感じるかもしれない。
しかし、スコープはプログラミングの安全性を高めるための重要な機能である。
例えば、1万行のコードがあったとする。
その中でもともと「name」を使っていて、意図せず別の場所で「name」という変数を使ってしまったことをイメージしてみよう。
そうすると、本当は変更したくなかった別の「name」の中身も変わってしまう。

このように、本来変更したくなかった変数が、予期しない形で変わってしまうことを防ぐのがスコープの大きな役割である。

会社で例えると、自分の部署の書類は閲覧したり編集できて良いけれども、他部署の書類を勝手にいじれる仕組みになっていると困るということに似ている。

Rubyにおけるスコープ

上記では、プログラミング言語全般におけるスコープの考え方である。

次に、Rubyではスコープがどのように実装されているのか確認する。なお、プログラミング言語によってスコープの仕様は異なるようだ。

https://tech-master.s3.amazonaws.com/uploads/curriculums//7874a2ebde615b66c7aec8f556fc812a.png

Rubyでは、上図のようにそれぞれのメソッド内と外側ではスコープが異なる、という設計になっている。

ここまでの、メソッドの使い方とスコープが分かると冒頭で見たメソッドがうまく動かなかった理由がわかる。

def rename(name)
  name = "Mr.#{name}"
end

name = "Tanaka"
rename(name)
puts name

ここで、2行目の「name」はrenameメソッドの中で定義されている。
一方5行目の「name」はメソッドの外側で定義されている。

そのため、これまで見てきたように、同じ「name」という名称であったとしてもプログラミング的には別物として扱われる。

つまり、それぞれの変数のスコープが異なるということである。

よってメソッド内で「Tanaka」という文字列に対して「Mr.」と追加をしたとしても、もう一方の「name」は変化しないということになる。

これでは、renameメソッドを作ったとしても活用できない、ということになってしまう。

renameメソッドの修正

ではどうすれば意図したとおりの動きになるのか。

def rename(name)
  name = "Mr.#{name}"
end

name = "Tanaka"
name = rename(name)
puts name

 

6行目を修正してから実行してみると今度は「Mr.Tanaka」と表示された。

https://tech-master.s3.amazonaws.com/uploads/curriculums//8aa3675b3cb95eebc4a467f6e15d221f.png

renameメソッドの返り値はもともと「Mr.Tanaka」となっていた。
そのため、その結果を変数nameに再度代入することで変更することができる。

つまり、引数を渡してメソッドを実行し、その返り値を利用すれば良いということになる。

引数解説動画
https://www.youtube.com/watch?v=9xizkYCwzTY&feature=youtu.be