多くの場合、インターネット上each
で Enumerable のメソッドの例を次のように見ました。
def each(&block)
@items.each do |item|
block.call(item)
end
end
人々がこれを使用しない理由:
def each(&block)
@items.each(&block)
end
違いはありますか?
多くの場合、インターネット上each
で Enumerable のメソッドの例を次のように見ました。
def each(&block)
@items.each do |item|
block.call(item)
end
end
人々がこれを使用しない理由:
def each(&block)
@items.each(&block)
end
違いはありますか?
実際、次のバージョンがさらに一般的に見られると思います。
def each
@items.each { |i| yield i }
end
これは、最初のコードサンプルに相当します。ただし、これと2番目のバージョンには微妙な違いがあります。
class Test
def initialize(items)
@items = items
end
def each1
@items.each { |i| yield i }
end
def each2(&block)
@items.each(&block)
end
end
観察:
irb(main):053:0> Test.new([1,2,3]).each2
=> #<Enumerator: [1, 2, 3]:each>
irb(main):054:0> Test.new([1,2,3]).each1
LocalJumpError: no block given (yield)
from (irb):43:in `block in each1'
from (irb):43:in `each'
from (irb):43:in `each1'
from (irb):54
from /usr/bin/irb:12:in `<main>'
ブロックを基になるiterableに委任するバージョンは、ブロックが指定されていない場合、実際には列挙子を返します。これは非常に便利です。それは私達がこのようなものを書くことを可能にします:
irb(main):055:0> Test.new([1,2,3]).each2.map { |x| x + 1 }
=> [2, 3, 4]
明示的なバージョンで同じことを実現するには、次のように調整する必要があります。
def each1
return enum_for(:each1) unless block_given?
@items.each { |i| yield i }
end
これはさらに冗長です。結論:可能な場合はいつでも、ブロックを基になる列挙可能なものに委任して、コードの重複やこのような微妙な落とし穴から身を守ります。
ちなみに、each
メソッドの2つの性質に気付いたので、別の名前を付けるのは理にかなっています。たとえば、String#chars
またはのようなメソッドの例に従って、メソッドIO#lines
を呼び出すことができitems
ます。
def items(&block)
@items.each(&block)
end