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中尝试的习惯。不是批评,而是建议。是的,这是我一直在寻找的东西(为什么我首先提出这个问题),我知道会有一个枚举表,它满足了我的需要,我只是在文档中找不到它,再次感谢!