Ruby
プロを目指す人のためのRuby入門

Rubyでメソッド呼び出しのかっこの手前にスペースが入るとエラーになる理由

はじめに

Rubyではメソッド呼び出しのかっこの手前にスペース(空白)が入るとエラーになる場合があります。この記事ではその理由について説明します。

確認環境

この記事の内容は以下の環境で確認しています。

ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]

おことわり

この記事では概念的なレベル(イメージ的なもの)でエラーになる理由を説明します。
Rubyパーサーの動作を正確に反映した説明ではないので、予めご了承ください。

エラーになるケース、ならないケースを確認してみる

標準出力に引数で渡された値を表示するputsメソッドでは、複数の引数を渡すことができます。
2つ以上の引数を渡した場合は、引数の内容と改行が交互に出力されます。

puts 1, 2, 3
#=> 1
#   2
#   3

もちろん、メソッド呼び出しのかっこを付けてもかまいません。

puts(1, 2, 3)
#=> 1
#   2
#   3

しかし、次のようにかっこの手前にスペースが入るとエラーになります。

puts (1, 2, 3)
#=> Traceback (most recent call last):
#           1: from /Users/jnito/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
#   SyntaxError ((irb):3: syntax error, unexpected ',', expecting ')')
#   puts (1, 2, 3)
#          ^

では、かっこの手前にスペースが入ると必ずエラーになるのかというと、そうでもありません。
次のコードは問題なく動作します。

puts (1 + 2 + 3)
#=> 6

puts (1, 2, 3)はエラーになり、puts (1 + 2 + 3)にならないこの理由は何なのでしょうか?

かっこの手前にスペースが入ると、「式をグループ化するかっこである」と解釈される

この理由は簡単にいうと、Rubyではかっこの手前にスペースが入ると、メソッド呼び出しのかっこではなく、「式をグループ化するかっこである」と解釈されるからです。

さらにいうと、puts (1, 2, 3)puts (1 + 2 + 3)は、メソッドの呼び出しのかっこが省略されているのと同じ状態になっています。
省略されているメソッド呼び出しのかっこを補うと、上のコードは次のようになります。

# putsの第1引数に(1, 2, 3)を渡す
puts((1, 2, 3))

# putsの第1引数に(1 + 2 + 3)を渡す
puts((1 + 2 + 3))

このとき、かっこの中身が値を返す「式」になっていれば、構文エラーにならず正常に実行できます(なぜなら、このかっこは「式をグループ化するかっこ」だから)。

ところが、1, 2, 3は式ではありません。irb上で1, 2, 3だけ入力しても構文エラーになるはずです。

1, 2, 3
#=> Traceback (most recent call last):
#           1: from /Users/jnito/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
#   SyntaxError ((irb):1: syntax error, unexpected ',', expecting end-of-input)
#   1, 2, 3
#    ^

よって、puts (1, 2, 3)はかっこの中身が式ではないため、構文エラーになります。

一方、1 + 2 + 3は式なので値を返します。

1 + 2 + 3
#=> 6

よって、puts (1 + 2 + 3)はかっこの中身が式なので構文上エラーにならず、putsメソッドの引数として6が渡されます。

発展その1

それでは次の2つのコードはどのような結果になるかわかるでしょうか?

puts (1 + 2 + 3) * 4
puts (1 + 2 + 3), 4

puts (1 + 2 + 3) * 4(1 + 2 + 3) * 4で1つの式となり、これが第1引数となります。
省略されているかっこを補うと次のようになります。

puts((1 + 2 + 3) * 4)

この場合は(1 + 2 + 3) * 4の結果だけがputsの引数になるので、次のように24が出力されます。

puts (1 + 2 + 3) * 4
#=> 24

一方、puts (1 + 2 + 3), 4(1 + 2 + 3)が第1引数で、4が第2引数になります。
つまり、puts((1 + 2 + 3), 4)と書いたことと同じです。
よって、次のように64が出力されます。

puts (1 + 2 + 3), 4
#=> 6
#   4

発展その2

では次に、先ほどのコードからかっこの手前のスペースをなくしたらどうなるでしょうか?

puts(1 + 2 + 3) * 4
puts(1 + 2 + 3), 4

このコードは実はどちらも正常に実行できません。
その理由を以下で説明します。

puts(1 + 2 + 3) * 4は次のように実行時エラーが発生します(構文上のエラーはないが、実際に実行すると最後まで正常に実行できない)。

puts(1 + 2 + 3) * 4
#=> 6
#   Traceback (most recent call last):
#           2: from /Users/jnito/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
#           1: from (irb):8
#   NoMethodError (undefined method `*' for nil:NilClass)

これはまず、puts(1 + 2 + 3)が実行され、続いてputsメソッドの戻り値であるnilに対して* 4を実行しようとしたために、「nil * 4は実行不能(nilには*メソッドは定義されていない)」とRubyに怒られてエラーが発生します。

一方、puts(1 + 2 + 3), 4は以下のような構文エラーが発生します。

puts(1 + 2 + 3), 4
#=> Traceback (most recent call last):
#           1: from /Users/jnito/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
#   SyntaxError ((irb):9: syntax error, unexpected ',', expecting end-of-input)
#   puts(1 + 2 + 3), 4
#                  ^

これは、puts(1 + 2 + 3)でいったん式が終わっているにもかかわらず、その直後に, 4が出てきたためです。
, 4の部分がRubyの構文上解釈できないために、構文エラーが発生します。

まとめ

というわけで、この記事ではRubyでメソッド呼び出しのかっこの手前にが入るとエラーになる理由を説明しました。
僕が執筆した「プロを目指す人のためのRuby入門」では、この内容をほとんど説明していなかったため、予期せず発生したエラーで困っている方がときどきおられたようです(説明不足でどうもすいません :bow: )。

この記事で説明したとおり、Rubyではかっこの手前にスペースが入るかどうかで、プログラムの解釈の仕方が大きく変わってきます。
「ちゃんと本の通りにコードを打ち込んだはずなのに、なぜかエラーが出る!」という場合は、かっこの前にスペースが入っていないかどうかチェックしてみてください。

参考文献

この記事の説明では書籍「プログラミング言語 Ruby」の6.3.2項「必須のかっこ」の内容を参考にさせてもらいました。