🐕
Array#to_a と Array#entries の違い
Array#to_a と Array#entries で軽微な違いがあると聞いたので調べてみました。
Enumerable#to_a と Enumerable#entries
その前に Enumerable の話をします。
Enumerable では #to_a と #entries の2つのメソッドが定義されています。
これらはいずれも #each から配列を生成するような挙動になっています。
class X
include Enumerable
def each
yield("homu")
yield("mami")
yield("mado")
end
end
x = X.new
# x.each の結果を元にして配列を返す
pp x.to_a # => ["homu", "mami", "mado"]
pp x.entries # => ["homu", "mami", "mado"]
Array#to_a と Array#entries
Enumerable を include している Array でも Enumerable#to_a / entries は利用できるんですが『 #to_a は Array クラスで再定義されている』という違いがあります。
# #to_a は Array で再定義されている
pp Array.instance_method(:to_a)
# => #<UnboundMethod: Array#to_a()>
# #entries は Enumerable で定義されている
pp Array.instance_method(:entries)
# => #<UnboundMethod: Enumerable#entries(*)>
Array#to_a の挙動がどう変わっているのかというと『 self の値をそのまま返す』という挙動になっています。
なので
-
Array#to_a=> レシーバと同じオブジェクトを返す -
Array#entries=> レシーバと異なるオブジェクトを返す
という大きな違いがあります。
ary = [1, 2, 3]
pp ary.__id__ # => 16
# これは ary と同じオブジェクトを返す
pp ary.to_a.__id__ # => 16
# これは ary とは異なるオブジェクトを返す
pp ary.entries.__id__ # => 24
これによりどういうことが起きる?
Array#to_a の結果に対しては破壊的変更を行うと元々のオブジェクトまで破壊されてしまいます。
ary = %w(mado homu mami)
# #entries の場合は元の順番が維持される
ary.entries.sort!
pp ary
# => ["mado", "homu", "mami"]
# #to_a では ary の並び順まで変わってしまう
ary.to_a.sort!
pp ary
# => ["homu", "mado", "mami"]
もし、メソッドの引数に対して to_a.sort! のような破壊的変更を行った場合に元の引数の値まで変わってしまうので注意して使用する必要があります。
Discussion