取一个惰性枚举器的最后一个元素

时间:2013-11-19 21:40:52

标签: ruby optimization lazy-evaluation

我有这样的功能:

(0..Float::INFINITY).lazy.take_while {|n|(n**2+ 1*n+41).prime?}.force[-1]

我正在使用它作为优化练习。这样工作正常,但它有一个内存顺序O(n),因为它将创建整个数组,然后采取最后一个元素。

我试图在不构建整个列表的情况下得到这个,因此是懒惰的枚举器。除了使用while循环之外,我想不出什么。

(0..Float::INFINITY).lazy.take_while {|n|(n**2+ 1*n+41).prime?}.last.force

有没有办法在空间顺序O(1)而不是O(n)中使用枚举器?

编辑:这里的示例没有必要使用lazy,但我认为减少函数的空间复杂度可能更有用吗?

2 个答案:

答案 0 :(得分:2)

如果您只是不想保存整个阵列:

(0..1.0/0).find {|n| !(n**2+n+41).prime?} - 1

1.0/0Float::INFINITY相同。我用过它,以防你没看到它。据我所知,两者都不可取。

我的第一个想法显然是矫枉过正:

def do_it
  e = (0..1.0/0).to_enum
  loop do
    n = e.peek
    return e.inspect unless (n**2+n+41).prime?
    e.next
  end
end

do_it 

答案 1 :(得分:1)

<强>解决方案

使用inject来保持当前值而不是构建数组。

(0..Float::INFINITY).lazy.take_while {|n|(n**2+ 1*n+41).prime?}.inject{|acc,n| n }

请注意,必须使用lazy,否则inject将构建一个中间数组。

<强>验证

要了解如果不使用lazy会发生什么,请在重新启动ruby&amp;之后运行以下命令:运行非惰性版本。它将返回“看起来像”中间数组的数组。

ObjectSpace.enum_for(:each_object, Array).each_with_object([]) {|e, acc|
  acc << e if e.size == 40 and e.first == 0
}

非惰性版本将返回:

[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]]

使用lazy重新执行测试将返回一个空数组。