Reputation: 882
tl;dr
I expect both of those lines to return {:a => 10}
p (0..5).each_with_object({a: 0}) { |i, acc| acc[:a] += i}
p (0..Float::INFINITY).lazy.each_with_object({a: 0}) { |i, acc| acc[:a] += i}.first(5)
However, the second line loops forever.
Longer version
This example is of course simplified whilst the real problem is very similar. The idea is to run each_with_object
body until certain condition is met, however this condition relies on result that this very each_with_object
function generates over each iteration so the stop condition must come after each_with_object
.
The closest I could get was
p (0..Float::INFINITY).lazy.each_with_object({a: 0}).take_while{ |i, acc| acc[:a] += i}.first(5)
but the result is not exactly what I want as it returns the following
[[0, {:a=>10}], [1, {:a=>10}], [2, {:a=>10}], [3, {:a=>10}], [4, {:a=>10}]]
which not only forces me to do .last.last
but also makes the array grow immensely as I iterate many times (and also it stores 5 times the {:a => 10}
instead of {a:=>0}, {a:=>1}, {a:=>3}, ...
which is also a mystery to me.
Upvotes: 0
Views: 206
Reputation: 114248
each_with_object
only returns an enumerator if you don't pass a block.
Maybe you could create your own method:
class Enumerator::Lazy
def my_each_with_object(obj)
Lazy.new(self) do |yielder, *args|
yield *args, obj
yielder << obj
end
end
end
(1..Float::INFINITY).lazy
.my_each_with_object({a: 0}) { |i, acc| acc[:a] += i }
.drop_while { |acc| acc[:a] <= 10 }
.first
#=> {:a=>15}
I've used acc[:a] <= 10
as a condition because you said that it depends on the value calculated by each_with_object
.
Upvotes: 2