Ruby 取惰性枚举数的最后一个元素
我有这样一个函数:Ruby 取惰性枚举数的最后一个元素,ruby,optimization,lazy-evaluation,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).
(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)来实现这一点
编辑:这里的示例不需要惰性,但我认为降低函数的空间复杂度可能更有用?如果您不想保存整个数组:
(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
解决方案 使用inject保留当前值,而不是构建数组
(0..Float::INFINITY).lazy.take_while {|n|(n**2+ 1*n+41).prime?}.inject{|acc,n| n }
请注意,必须使用lazy
,否则inject
将构建一个中间数组
ObjectSpace.enum_for(:each_object, Array).each_with_object([]) {|e, acc|
acc << e if e.size == 40 and e.first == 0
}
验证
要查看如果不使用lazy会发生什么,请在重新启动ruby并运行非lazy版本后运行以下命令。它将返回“看起来”像中间数组的数组
ObjectSpace.enum_for(:each_object, Array).each_with_object([]) {|e, acc|
acc << e if e.size == 40 and e.first == 0
}
使用lazy重新执行测试将返回一个空数组。这与删除
lazy
和force
有何不同?39双向。为什么1*n
?说得好,在这种情况下,懒惰是不必要的,因为我们使用take_,而我通常在处理无穷大时使用它,即将编辑问题以反映这一点。错误。。。你已经在使用枚举数了,不是吗?是的,你想在不建立列表的情况下使用枚举数,这一点在问题中得到了澄清。:)注意,实际构建数组可能有好处,这取决于你真正在做什么:用它,垃圾收集器在寻找价值的过程中不太可能踢进去。正是我正在寻找的东西,我不知道E.Peek。enumerators@Denis,[可枚举#查找](http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-find)
返回表达式对其求值的块中的第一个值n
(不是数组)。在这种情况下,这是40
,但我们想要最后一个;因此-1
。当你有这样的问题时,你应该养成检查文档和/或在irb中尝试的习惯。不是批评,而是建议。是的,这是我一直在寻找的东西(为什么我首先提出这个问题),我知道会有一个枚举表,它满足了我的需要,我只是在文档中找不到它,再次感谢!