如何在Ruby中获取惰性数组?
在Haskell中,我可以谈论[1..]
,这是一个无限列表,根据需要懒洋洋地生成。我也可以做像iterate (+2) 0
这样的事情,它应用我给它的任何函数来生成一个惰性列表。在这种情况下,它会给我所有偶数。
我确信我可以在Ruby中做这些事情,但似乎无法解决问题。
答案 0 :(得分:41)
使用Ruby 1.9,您可以使用Enumerator类。这是文档中的一个示例:
fib = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
此外,这是一个很好的技巧:
Infinity = 1.0/0
range = 5..Infinity
p range.take(10) #=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
这个只适用于连续值。
答案 1 :(得分:21)
最近Enumerable :: Lazy已添加到ruby trunk。我们将在ruby 2.0中看到它。 特别是:
a = data.lazy.map(&:split).map(&:reverse)
不会立即评估
结果是Enumerable :: Lazy的实例,可以进一步延迟链接。如果您想获得实际结果 - 使用#to_a
, (#take(n)
#take
现在也很懒,请使用#to_a
或{{1}等)
如果您想了解更多有关此主题和我的C补丁的信息,请参阅我的博文Ruby 2.0 Enumerable::Lazy
答案 2 :(得分:6)
懒惰范围(自然数):
Inf = 1.0/0.0
(1..Inf).take(3) #=> [1, 2, 3]
懒惰范围(偶数):
(0..Inf).step(2).take(5) #=> [0, 2, 4, 6, 8]
注意,您还可以使用某些方法扩展Enumerable
以使处理惰性范围(等等)更方便:
module Enumerable
def lazy_select
Enumerator.new do |yielder|
each do |obj|
yielder.yield(obj) if yield(obj)
end
end
end
end
# first 4 even numbers
(1..Inf).lazy_select { |v| v.even? }.take(4)
output:
[2, 4, 6, 8]
更多信息: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/
还可以在此处找到Enumerator
类的lazy_map和lazy_select实现:
http://www.michaelharrison.ws/weblog/?p=163
答案 3 :(得分:4)
在Ruby 2.0.0中,他们在Enumerable类中引入了新方法“Lazy”。
您可以在这里查看延迟功能核心和用法..
http://www.ruby-doc.org/core-2.0/Enumerator/Lazy.html
https://github.com/yhara/enumerable-lazy
http://shugomaeda.blogspot.in/2012/03/enumerablelazy-and-its-benefits.html
答案 4 :(得分:1)
正如我在评论中已经说过的那样,实现懒惰数组这样的事情是不明智的。
使用Enumerable可以在某些情况下很好地工作,但在某些方面与懒惰列表不同:map和filter等方法不会被懒惰地评估(因此它们不能用于无限的枚举)和已经计算过的元素一旦没有存储,所以如果你访问一个元素两次,它会被计算两次。
如果你想在ruby中使用haskell的惰性列表的确切行为,那么有一个lazylist gem来实现惰性列表。
答案 5 :(得分:1)
这将循环到无限:
0.step{|i| puts i}
这将以两倍的速度循环到无穷大:
0.step(nil, 2){|i| puts i}
只有你想要它时才会进入无穷大(产生一个枚举器)。
table_of_3 = 0.step(nil, 3)
答案 6 :(得分:0)
正确的答案已经确定了“惰性”方法,但是提供的示例不太有用。我将给出一个更好的例子,说明何时适合将lazy与数组配合使用。如前所述,惰性定义为Enumerable模块的实例方法,它适用于实现Enumerable模块的EITHER对象(例如,数组-[] .lazy)或枚举数,这些枚举数是可枚举模块中迭代器的返回值(例如,each_slice -[] .each_slice(2).lazy)。请注意,在Enumerable模块中,某些实例方法返回更多的原始值(如true或false),一些返回集合(如数组)和一些返回枚举器。如果未给出块,则一些返回枚举器。
但是在我们的示例中,IO类还具有一个迭代器each_line,该迭代器返回一个枚举器,因此可以与“ lazy”一起使用。返回枚举器的美丽之处在于它实际上并未将集合(例如大型数组)加载到正在处理的内存中。相反,它有一个指向该集合的指针,然后例如当您要使用类似to_a
的集合来处理该集合时,将讲述将在该集合上使用的算法(例如each_slice(2))。>
因此,如果您正在使用枚举器来提高性能,那么现在您可以将懒惰附加到枚举器上。因此,与其遍历整个集合来匹配此条件,不如:
file.each_line.select { |line| line.size == 5 }.first(5)
您可以调用惰性:
file.each_line.lazy.select { |line| line.size == 5 }.first(5)
如果我们正在扫描大型文本文件的前5个匹配项,那么一旦找到5个匹配项,就无需继续执行。因此,任何类型的可枚举对象的懒惰能力。
答案 7 :(得分:-3)
Ruby Arrays根据需要动态扩展。您可以对它们应用块来返回偶数等内容。
array = []
array.size # => 0
array[0] # => nil
array[9999] # => nil
array << 1
array.size # => 1
array << 2 << 3 << 4
array.size # => 4
array = (0..9).to_a
array.select do |e|
e % 2 == 0
end
# => [0,2,4,6,8]
这有帮助吗?