如何使无限的each_with_object实际上是懒惰的?

时间:2018-12-05 10:41:05

标签: ruby

tl; dr

我希望这两行都返回{: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)

但是,第二行永远循环。

长版

当然,此示例已简化,但实际问题非常相似。这个想法是运行each_with_object主体直到满足特定条件,但是此条件依赖于这个each_with_object函数在每次迭代中生成的结果,因此停止条件必须在之后 each_with_object

我能得到的最接近的是

p (0..Float::INFINITY).lazy.each_with_object({a: 0}).take_while{ |i, acc| acc[:a] += i}.first(5)

但是结果不完全是我想要的,因为它返回以下内容

[[0, {:a=>10}], [1, {:a=>10}], [2, {:a=>10}], [3, {:a=>10}], [4, {:a=>10}]]

这不仅迫使我执行.last.last,而且随着我多次迭代,它使数组极大地增长(而且它存储的是{:a => 10}而不是{a:=>0}, {a:=>1}, {a:=>3}, ...的5倍对我来说是个谜。

1 个答案:

答案 0 :(得分:1)

each_with_object仅在不传递块的情况下返回枚举数。

也许您可以创建自己的方法:

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}

我使用acc[:a] <= 10作为条件,因为您说它取决于each_with_object计算的值。