Help us understand the problem. What is going on with this article?

メソッド内にループがある場合の戻り値(返り値)について

皆さんこんにちは! プログラマーを目指して学習している、久保雄貴と申します。

今回は「メソッド内にループがある場合の戻り値(返り値)について」自分が疑問に思ったこと、考えたこと、知ったことについて書いてみようと思います。

あるときrubyの勉強中にエラーが起きました。メソッドの戻り値を使って演算をしようとした時です。
「undefined method '+' for nil:nilclass」 のような表示がされました。
空のオブジェクトと足し算はできません。ということのようです。
頭の中に「?」がたくさん出てきて、自分が戻り値についてよく理解できていないことがわかりました。
そこでいろいろ考え、実験して、調べてみたわけです。

皆さんのお役に立てる内容であれば嬉しいです。

なお、間違っている点や「こういう風に書いたらいいよ」などアドバイスがありましたら、コメントをください。

では始めましょう!

1. 戻り値とは

戻り値とは、オブジェクトやメソッドが処理された後の最終的な値のことです。返り値とも言います。
Rubyのオブジェクト自体、またはメソッドを利用した「式」には、全て戻り値があります。

Rubyにおける「式」とは文字列や数値の他に、変数やメソッド呼び出し、演算子式などが含まれます。
すなわち、"あいうえお", 5296, (4 + 24)などはすべて式になります。
そしてそれらすべてに戻り値があるということです。(上の例でいうと、"あいうえお", 5296, 28)

2. メソッドの戻り値はどうなる?

では、メソッドの戻り値はどうなるのでしょうか。
Ruby Referenceで調べてみました。

http://ruby-for-beginners.rubymonstas.org/writing_methods/return_values.html

以下のように書かれています。

In Ruby, a method always return exactly one single thing (an object).
Also note that in Ruby we do not have to use the statement return, as in other languages. In fact, most Ruby code does not use the keyword return at all.

This is extremely convenient, but it is also something we need to learn:

If we don’t do anything else, then a method will return the value that was returned from the last evaluated statement. Most often, this is the last line in the method body.

端的に言えば、
「メソッドは一つの値を返が、他の言語と違って、returnと記述する必要はない。
もし記述しなかったら、最後の式の値が返される。」
という感じでしょうか。

例えば、

def test(number)
  number + 1      ←メソッドの中の最後の式
end

puts test(5)

これを実行すると、6と表示されますね。
"number + 1" の答えである "6" がtestメソッドの戻り値となっているわけです。

3. 実験: メソッドの戻り値の判定

冒頭で書きましたが、メソッドの戻り値を使って演算をしようとした時に、「戻り値は「nil」ですよ。」とエラーが出ました。実は、その時はメソッド内にwhile文(ループ)がありました。
条件を満たすまで、命令を実行し、条件を満たしたら、ループから抜け出して、メソッドの呼び出し元にもどる。
その戻り値で演算をしようとしてエラーが出たわけです。

そこでメソッドの戻り値が「nil」かそうでないかを判断してみたらどうかと思いつきました。

メソッドの戻り値が「nil」なのかそうでないのかを判断するコードを考えましたので、みていきたいと思います。
まずはwhile文なしのパターンです。

def test(count)
    puts "0より大きい数字を入力してください。"
    count = gets.to_i
end

count = test(count)

if count.nil?
  puts "test(count)の戻り値はnilです。"
else
  puts "test(count)の戻り値は#{count}です。"
end

「.nil?」はcount(レシーバ)の中身が「nil」の場合trueを返し、「nilでない」場合falseを返すメソッドです
例えば、ターミナルで実行して、24という数字を入れたとすると、

代替テキスト

と表示されます。

上のコードでは予想通り、returnの記述がなくても戻り値は「nil」ではありませんでした。


では次はどうでしょう。
testメソッドの中にwhile文を入れて、10より大きい数字が入るまで、入力させ続けます。
そして、その戻り値が「nil」かどうかを判断します。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
  end

end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

ターミナルで実行してみましょう。
今回は、「4→24」の順で入れてみます。(while文が機能しているか判断するため。)

代替テキスト

なんと今回は戻り値が「nil」になりました!




次は、while文の後に「return count」を入れてみます。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
  end
  return count   ←追加しました
end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

実行してみましょう。

代替テキスト

当然のことながら、戻り値は「24」ですね。

4. 結果

さて、もう一度まとめてみましょう。
メソッドの戻り値が「nil」かどうかを判断しました。

順番 条件 結果
while文なし 戻り値: 24
2 while文あり かつ returnメソッドなし 戻り値: nil
3 while文あり かつ returnメソッドあり 戻り値: 24

5. 考察

さて、上記の結果になりました。
ここからは、あくまで自分で考えた内容です。(信憑性はとても低いです)

Rubyは、メソッド内において式から得られる「値」をどこかに仮置きしているのではないか。
例えば、1の実験では

def test(count)
    puts "0より大きい数字を入力してください。"
    count = gets.to_i      ←ここで入力された値をどこかに仮置きしている。
end

count = test(count)

if count.nil?
  puts "test(count)の戻り値はnilです。"
else
  puts "test(count)の戻り値は#{count}です。"
end

1) 3行目で「値」をどこかに仮置きする。
2) もしメソッドの中に次の式があれば、仮置きしている「値」を破棄し、その次の「値」をまた仮置きする。(今回はなし)
3) メソッドが終わったら、一番最後に仮置きした「値」をメソッドの戻り値として返す。

と行った感じです。

ただ、メソッドの中にループ文がある場合は、ループから抜け出す時になぜかその仮置きしておいた「値」を破棄してしまうのではないか。
なぜかはわかりません。。。(考察になっていない?)

6. 結論

実は、このブログを書く前にいろいろと参考になるものはないかと探していたところ、答えになるものを見つけました。
Rubyのリファレンスマニュアルです。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fcontrol.html

この中のwhileに関する説明の一番下にこう書かれています。

代替テキスト

while は nil を返します。

はっきり書かれてますね(笑)

なぜそういうふうになっているのかは書いてありませんが、そうなっているもんは仕方ない。
今回はそう割り切りましょう。

ということで、
結論は、「Rubyのルールでそうなっているから」です。



7. 感想

なんか予想外な感じになってしまいました。
でも、まあ、すっきりできたかな?




さて、気を取り直して、
今後学習を進めていくにあたって、事情がない限り、returnメソッドを書いておこうと思いました。
実は、"return count" と書かずに直接 "count" と書いても同じ結果になります。
でもそれだとその記述が何をしているのかわかりません。

ループの有無に関わらず、他のプログラマーのためにも、コードを見やすくしたり、そのメソッドが何をしているのかがわかりやすいように書いた方が良さそうです。



いかがだったでしょうか。
今回二回目の投稿ですが、学んだことは、
1) 疑問に思ったことは、自分で仮説を立てて、実験して、結果から自分なりの答えを出すことがとても大事ということ。
2) その過程は実は楽しいこと。
3) その過程の中でいろいろと調べるので、途中で答えが見つかることもあるということ。
4) ブログという形でアウトプットすることは思った以上に緊張するし怖い部分があるが予想以上に学びがあること
です。

いいねをもらえたり、ストックしていただけるのもとても励みになります。
ありがとうございます。m(_ _)m

今回は以上です。ありがとうございました。




おまけ

リファレンスマニュアルに

また、引数を伴った break により while 式の戻り値をその値にすることもできます。

と書かれていたので調べてみました。

https://hydrocul.github.io/wiki/programming_languages_diff/control_flow/break.html#ruby

breakの後ろに数字あるいは変数を書くといいみたいです。

def test(count)
  while count <=10
    puts "10より大きい数字を入力してください。"
    count = gets.to_i
    break count    ←ここです
  end

end

count = 0
count = test(count)

if count.nil?
puts "test(count)の戻り値はnilです。"
else
puts "test(count)の戻り値は#{count}です。"
end

やってみましょう。

代替テキスト

「nil」になりませんでした!



もうお気づきかもしれませんが、これには問題が。。。

代替テキスト

そうです。ループされないんです。
ループの意味なし。
なんじゃこりゃ。。。。

ということで 本当におしまいです。ありがとうございました。



Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした