我有这样的功能:
(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,但我认为减少函数的空间复杂度可能更有用吗?
答案 0 :(得分:2)
如果您只是不想保存整个阵列:
(0..1.0/0).find {|n| !(n**2+n+41).prime?} - 1
1.0/0
与Float::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重新执行测试将返回一个空数组。