読者です 読者をやめる 読者になる 読者になる

Rubyの多重代入におけるto_aとto_aryの挙動

先日のエントリRubyの多重代入についてふれたのだが、教科書によってto_aryが呼ばれると書いてあったりto_aが呼ばれると書いてあったりで、なにが本当なのか分からなくなった。

回答らしきもの

調べてみたら、こちらに回答らしきものがあった。

OK, to_a means an explicit array conversion, so it should not be used
for implicit conversion a in "*y = x". On the other hand, I consider
"*x" as a form of explicit conversion, i.e. shorthand for "*(x.to_a)",
so that the current 1.9 behavior is intentional.


matz.

http://bugs.ruby-lang.org/issues/show/1393

どうやら、Ruby1.9においては、暗黙的なコンテキストではto_aryが呼ばれ、明示的なコンテキストではto_aが呼ばれるようだ。

1.9.2で試してみた

class C                                                                                                                                                                                              
  def to_ary; [1,2]; end
  def to_a; [3,4]; end
end
c = C.new
 
a, b = c # 暗黙的
d, e = *c # 明示的
 
puts a, b # 1,2
puts d, e # 3,4

確かに暗黙的なコンテキスト(a,b = c)ではto_aryが呼ばれており、明示的なコンテキスト(d,e = *c)ではto_aが呼ばれていることが分かる。ただ、これはto_aryとto_aの両方が定義されている場合の挙動であり、片方の場合には挙動が違った。上記においてto_aryのみを定義した場合、明示的コンテキストにおいてもto_aryが呼ばれる(to_aの代わりに)。しかしto_aのみを定義した場合は、暗黙的コンテキストにおいては通常の多重代入が行われる(to_aryの代わりにto_aが呼ばれることはない)。結構ややこしい。

1.8.7でも試してみた。

class C                                                                                                                                                                                              
  def to_ary; [1,2]; end
  def to_a; [3,4]; end
end
c = C.new
 
a, b = c # 暗黙的
d, e = *c # 明示的
 
puts a, b # 1,2
puts d, e # 1,2

さらにややこしいことに、1.8.7の場合、1.9.2とは挙動が違った。to_aryとto_a両方が定義されていると、暗黙的でも明示的でもto_aryが呼ばれる。片方だけ定義された場合でいうと、to_aryのみが定義されている場合、暗黙的でも明示的でもto_aryが呼ばれる。一方to_aのみが定義されている場合は、暗黙的コンテキストでは通常の多重代入が行われ、明示的コンテキストにおいてはto_aが呼ばれる。


まとめ

すごく混乱したので、まとめておく。あまりこの違いで悩むことは無いだろうけど。。。

Ruby バージョン 暗黙的コンテキスト 明示的コンテキスト
1.9.2 to_ary to_a(なければto_ary)
1.8.7 to_ary to_ary(なければto_a)