4

多くの場合、インターネット上eachで Enumerable のメソッドの例を次のように見ました。

def each(&block)
  @items.each do |item|
    block.call(item)
  end
end

人々がこれを使用しない理由:

def each(&block)
  @items.each(&block)
end

違いはありますか?

4

1 に答える 1

4

実際、次のバージョンがさらに一般的に見られると思います。

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
于 2012-05-05T22:05:24.810 に答える