Can a Ruby method yield as an iterator or return an array depending on context?
-
20-08-2019 - |
Question
I have an arbitrary method in Ruby that yields multiple values so it can be handed to a block:
def arbitrary
yield 1
yield 2
yield 3
yield 4
end
arbitrary { |x| puts x }
I'd like to modify this method so that, if there is no block, it just returns the values as an array. So this construct would work as well:
myarray = arbitrary
p a -----> [1, 2, 3, 4, 5]
Is this possible in Ruby?
Solution
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 with:
block.call v
OTHER TIPS
def arbitrary
values = [1,2,3,4]
return values unless block_given?
values.each { |val| yield(val) }
end
arbitrary { |x| puts x }
arbitrary
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 has the advantage that it works for infinite 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 do it is to guard your method with to_enum(your_method_name, your_args)
like 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 themselves use in multiple places.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow