Rubyの{ }と戯れて

1.
「ブロックを表す { } と、ハッシュリテラルの { } が混同されることはあるか?」というのが最初に抱いた疑問でした。それについては自分で「tASSOC(つまり=>)の有無でわかるじゃん」と思いました。つまり、以下の二つのメソッドmethod1, method2の呼び出しで何が渡されるかは自明であると思いました。

method1 { puts 'alice@example.com' }        # ブロックが渡されるのだろう
method2 { :alice => 'alice@example.com' }   # ハッシュが渡されるのだろう

実際にmethod1, method2を書いて試そうとしたところ、以下のようなエラーになりました。

def method1(&block)
  block.call
end

def method2(arg)
  p arg
end

method1 { puts 'alice@example.com' }
method2 { :alice => 'alice@example.com' }   # syntax error, unexpected tASSOC, expecting '}'

2.
上でエラーになった理由は、きっとmethod2の引数をインタプリタが誤解するからだと思い、かっこ ( ) を付けてみました。すると以下のように期待通りの動きになりました。これでブロックとハッシュリテラルに関しては何となく納得。

def method1(&block)
  block.call
end

def method2(arg)
  p arg
end

method1 { puts 'alice@example.com' }        #=> alice@example.com
method2({ :alice => 'alice@example.com' })  #=> {:alice=>"alice@example.com"}

3.
「あれ?でも、メソッドの引数ってかっこがなくてもよかったはずだよな。だからこそDSL(Domain Specific Language)が楽に作れるわけだし」と思って、以下のようなプログラムで試してみました。あれれ?今度は括弧がなくてもちゃんと表示されます。

def print_args(*args)
  p args
end

print_args 1, 2, 3                      #=> [1, 2, 3]
print_args 1, 2, { :R => 'Rocket' }     #=> [1, 2, {:R=>"Rocket"}]

4.
今度は引数を1個だけにしてみました。するとやはりエラーになります。

def print_args(*args)
  p args
end

print_args 1
print_args { :R => 'Rocket' }       # syntax error, unexpected tASSOC, expecting '}'

5.
なるほど。メソッド名の直後が { だったら「これはブロック」と判断しているのか、と推測しました。以下実験。確かに { } のほうはブロックだと判断され、引数だとは思われていません。

def print_it(arg)
  p arg
end

print_it 1                  #=> 1
print_it { puts 'Rocket' }  #=> wrong number of arguments (0 for 1) (ArgumentError)

6.
ここまでで現象的には納得しました。また、RubyのFAQに4.6 p {}で何も表示されませんとあるのを見つけました(^_^;
7. 追記
bellbindさんから、{ }を取れば通りますねとコメントいただきました。ふむふむ。=> によってちゃんとハッシュにしてくれるんですね。

def print_args(*args)
  p args
end

print_args :R => 'Rocket'
print_args :R => 'Rocket', :S => 'Space'

実行結果です。ちなみにこの例はR is for RocketS is for Spaceより。

[{:R=>"Rocket"}]
[{:R=>"Rocket", :S=>"Space"}]