[ACCEPTED]-Can a Ruby method yield as an iterator or return an array depending on context?-return-value

Accepted answer
Score: 19
def arbitrary
  values = [1,2,3,4]
  return values unless block_given? 
  values.each { |val| yield(val) }
end
arbitrary { |x| puts x }
arbitrary

0

Score: 14

There is a syntax for that:

def arbitrary(&block)
  values = [1, 2, 3, 4]
  if block
    values.each do |v|
      yield v
    end
  else
    values
  end
end

Note:

yield v

Can be replaced 1 with:

block.call v
Score: 14

In ruby 1.9+ you can use Enumerator to implement that.

def arbitrary(&block)
  Enumerator.new do |y|
    values = [1,2,3,4]
    values.each { |val| y.yield(val) }
  end.each(&block)
end

It 5 has the advantage that it works for infinite 4 streams too:

# block-only version
#
def natural_numbers
  0.upto(1/0.0) { |x| yield x }
end

# returning an enumerator when no block is given
#
def natural_numbers(&block)
  Enumerator.new do |y|
    0.upto(1/0.0) { |x| y.yield(x) }
  end.each(&block)
end

But the most idiomatic way to 3 do it is to guard your method with to_enum(your_method_name, your_args) like 2 so:

def arbitrary
  return to_enum(:arbitrary) unless block_given?

  yield 1
  yield 2
  yield 3
  yield 4
end

This is an idiom that ruby core libraries 1 themselves use in multiple places.

More Related questions