Ruby のEnumerable
のメソッドはミックスイン先のeach
メソッドを使用するが、 Array
の each
を再定義したとき、 Array
のインスタンスメソッドは再定義した each
を使わない場合がある。
Array#each
を再定義する
再定義する
class Array
def each
count = 0
while self[count]
puts 'here!'
yield self[count]
count += 1
end
self
end
end
ar = [1, 2, 3, 4, 5]
ar.each{|v| puts v}
=begin
出力は以下の通り
here!
1
here!
2
here!
3
here!
4
here!
5
=end
m = ar.map{|v| v * 2} # => "here!"が出力されない
p m # => [2, 4, 6, 8, 10]
特異メソッドで上書きする
ar = [5,2,3,4,1]
def ar.each
puts 'here!'
dupped = self.dup
while item = dupped.shift
yield item
end
self
end
# 再定義した時と同様に"here!"は出力されない
puts ar.map {|i| i * 2}
継承したクラス
class AnotherArray < Array
def each
d = self.dup
while item = d.shift
puts 'here!'
yield item
end
self
end
end
ar = AnotherArray.new [1,2,3,4,5]
ar.each{|v| puts v} #=> "here!"が出力される
m = ar.map{|v| v*v} #=> "here!"が出力されない
p m #=> [1, 4, 9, 16, 25]
自分で定義した Enumerable
なクラスの場合は上書きされる
もちろん、自分で定義した Enumerable
なクラスの場合は each
を上書きすると上書きしたものが使われる
ruby:another_array.rb
class AnotherArray
include Enumerable
def initialize src
@src = src
end
def each
puts 'AnotherArray#each'
@src.each {|item| yield item}
end
end
ar = AnotherArray.new [1, 2, 3, 4, 5]
m = ar.map {|v| v * 2} # => "AnotherArray#each" が出力される
p m # => [2, 4, 6, 8, 10]
再定義する
class AnotherArray
def each
puts 'This each is broken!'
end
end
ar = AnotherArray.new [1, 2, 3, 4, 5]
broken = ar.map {|v| v * 2} # => 'This each is broken!' が出力される
p broken # => []
特異メソッドで上書きする
ar = AnotherArray.new [1, 2, 3, 4, 5]
def ar.each
puts 'Fixed.'
@src.each {|item| yield item}
end
fixed = ar.map {|v| v * 2} # => "Fixed." が出力される
p fixed # => [2, 4, 6, 8, 10]
継承したクラス
class ChildAnotherArray < AnotherArray
def each
puts 'not implemented!'
end
end
child = ChildAnotherArray.new [1,2,3,4,5]
child.each{|v| puts v} #=> "not implemented!"
child.map{|v| v * 2} #=> "not implemented!"
追記
原因
コメントをいただきました。@scivola さんありがとうございます。
原因は、 Array#map
が Enumerable#map
と処理系レベルで別実装になっているから。 なので、array.cで実装されていない他の Enumerable
のメソッドでは再定義した each
が使われれます。
class Array
def each
puts 'broken each'
end
end
ar = [1, 2, 3, 4]
ar.map {|v|v * 2} # => "broken each" は出力されない
ar.member? 1 # => "broken each" が出力される
また、 mruby など処理系によっては Array#map
が Enumerable#map
のものもあるようです。 上記のコードを mruby で実行するとどちらも "broken each"
が出力されます。