如何在Ruby中获得惰性数组?

如何在Ruby中获得惰性数组?,ruby,lazy-evaluation,Ruby,Lazy Evaluation,如何在Ruby中获得惰性数组 在Haskell中,我可以谈论[1..],这是一个无限列表,根据需要惰性地生成。我还可以做一些事情,比如迭代(+2)0,它应用我给它的任何函数来生成一个惰性列表。在这种情况下,它会给我所有的偶数 我确信我可以在Ruby中做这些事情,但似乎不知道如何做。Ruby数组可以根据需要动态扩展。您可以对它们应用块以返回偶数之类的内容 array = [] array.size # => 0 array[0] # => nil array[9999] # =>

如何在Ruby中获得惰性数组

在Haskell中,我可以谈论
[1..]
,这是一个无限列表,根据需要惰性地生成。我还可以做一些事情,比如迭代(+2)0,它应用我给它的任何函数来生成一个惰性列表。在这种情况下,它会给我所有的偶数


我确信我可以在Ruby中做这些事情,但似乎不知道如何做。

Ruby数组可以根据需要动态扩展。您可以对它们应用块以返回偶数之类的内容

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]
array=[]
array.size#=>0
数组[0]#=>nil
数组[9999]#=>nil
数组1

数组在Ruby 1.9中,您可以使用枚举器类。这是文档中的一个示例:

  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]
但这一项仅适用于连续值。

惰性范围(自然数):

延迟范围(偶数):

注意,您还可以使用一些方法扩展
可枚举的
,使使用惰性范围(等等)更加方便:

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]
更多信息请点击此处:

还有一些lazy_map和lazy_select的实现,用于
枚举器
类,可以在这里找到:

正如我在评论中所说的,实现懒惰数组这样的东西是不明智的

相反,使用Enumerable在某些情况下可以很好地工作,但在某些方面与惰性列表不同:像map和filter这样的方法不会被惰性地计算(因此它们不会在无限的Enumerable上工作),并且已经计算过一次的元素不会被存储,因此如果您访问一个元素两次,它将被计算两次


如果您想了解ruby中haskell懒惰列表的确切行为,那么有一个实现懒惰列表的方法。

最近,可枚举::lazy被添加到ruby主干中。我们将在ruby 2.0中看到它。 特别是:

a = data.lazy.map(&:split).map(&:reverse)
不会立即进行评估。
结果是Enumerable::Lazy的实例,它可以被进一步的延迟链接。如果你想得到一个实际的结果-使用
#to#u a
#take(n)
#take
现在也很懒,使用
#to#a
#force
)等等。

如果您想了解更多关于这个主题和我的C补丁的信息,请参阅Ruby 2.0.0中我的博客文章,他们在Enumerable类中引入了新方法“Lazy”

您可以在此处检查惰性函数核心和用法




这将循环到无穷远:

0.step{|i| puts i}
这将以两倍的速度循环到无穷大:

0.step(nil, 2){|i| puts i}
只有当您希望它变为无穷大时,它才会变为无穷大(结果是一个枚举数)


正确的答案已经确定了“lazy”方法,但提供的示例并不太有用。我将给出一个更好的例子,说明什么时候使用lazy和数组是合适的。如上所述,lazy被定义为模块可枚举的实例方法,它可以作用于实现可枚举模块的对象(例如数组-
[].lazy
)或作为可枚举模块中迭代器返回值的枚举器(例如每个_片-
[].每个_片(2).lazy
)。请注意,在Enumerable模块中,一些实例方法返回更原始的值(如true或false),一些返回集合(如数组)和一些返回枚举数。如果未给定块,则某些返回枚举数

但对于我们的示例,IO类在每行中都有一个迭代器,它返回一个枚举数,因此可以与“lazy”一起使用。返回枚举数的美妙之处在于,它实际上并没有将正在处理的集合(例如大数组)加载到内存中。相反,它有一个指向集合的指针,然后讲述它将在该集合上使用的算法(例如,
每个片段(2)
),例如,当您希望使用类似于
的内容来处理集合时

因此,如果您正在使用枚举器以获得巨大的性能提升,现在可以将lazy附加到枚举器。因此,与其遍历整个集合以匹配此条件,不如:

file.each_line.select { |line| line.size == 5 }.first(5)
您可以调用lazy:

file.each_line.lazy.select { |line| line.size == 5 }.first(5)

如果我们正在扫描一个大文本文件,查找前5个匹配项,那么一旦找到了5个匹配项,就不需要继续执行。因此,lazy对任何类型的可枚举对象都具有强大的功能

它们不必是连续的:(10..100)。步骤(20)。take(5)#=>[10,20,30,40,50]在Ruby 1.8中,您可以
要求“backport”
来获得此功能:-)注意,尽管执行
fib.map{x | x+1}.take(10)
将不起作用,因为map将尝试创建一个数组。还请注意,如果您执行两次fib.take(10)
,元素将被计算两次(不同于惰性列表,在惰性列表中,元素一经计算就保存在内存中)。因此,这并不完全等同于惰性列表。为了使枚举数等效于
fib.map
,您需要对(:map)
执行
fib.enum\u。枚举数和无穷大在Ruby中对我来说是新概念。今天我第一次查阅了一些Haskell的东西,所以看到一些类似的语法在Ruby中表达类似的概念(例如take)很好,这不是关于对象的惰性生成的问题吗?正确的解决方案不是关于数组(总是完全由Ruby确定),而是关于其他类型的
可枚举的
,正如Sanjana的回答所述。我刚开始使用Ruby,我认为这是正确的。我会解决这个问题——希望你对惰性数组的愿望成真:数组与列表有很大的不同。允许无限数组的惰性数组的实现将具有可怕的运行时属性。请注意,与枚举器解决方案一样,您不能执行
infinite_range.filter{x | f(x)}.take(5)
,因此它的行为与惰性列表不同。@sepp2k,添加了指向站点的链接,该站点实现了
lazy\u select
etc,用于
Enumerator
class
0.step(nil, 2){|i| puts i}
table_of_3 = 0.step(nil, 3)
file.each_line.select { |line| line.size == 5 }.first(5)
file.each_line.lazy.select { |line| line.size == 5 }.first(5)