Ruby相当于C#的’yield’关键字,或创建序列而不预先分配内存

在C#中,你可以这样做:

public IEnumerable GetItems() { for (int i=0; i<10000000; i++) { yield return i; } } 

这将返回一个包含1000万个整数的可枚举序列,而无需在该长度的内存中分配集合。

有没有办法在Ruby中做同等的事情? 我想要处理的具体示例是将矩形数组展平为要枚举的值序列。 返回值不必是ArraySet ,而是某种只能按顺序迭代/枚举的序列,而不是索引。 因此,不需要在存储器中同时分配整个序列。 在.NET中,这是IEnumerableIEnumerable

在Ruby世界中使用的术语的任何澄清都会有所帮助,因为我更熟悉.NET术语。

编辑

也许我原来的问题还不够清楚 – 我认为在C#和Ruby中, yield含义差别很大,这就引起了混乱。

我不想要一个需要我的方法来使用块的解决方案。 我想要一个具有实际返回值的解决方案。 返回值允许方便地处理序列(过滤,投影,连接,压缩等)。

这是一个如何使用get_items的简单示例:

 things = obj.get_items.select { |i| !i.thing.nil? }.map { |i| i.thing } 

在C#中,任何返回使用yield return返回的IEnumerable方法都会导致编译器在幕后生成满足此行为的有限状态机。 我怀疑使用Ruby的延续可以实现类似的东西,但我还没有看到一个例子,我自己也不清楚如何做到这一点。

确实有可能使用Enumerable来实现这一目标。 一个简单的解决方案是给我们一个Array (包括模块Enumerable ),但我不想在内存中创建一个包含N个项目的中间集合,因为它可以只是懒得提供它们并且完全避免任何内存峰值。

如果这仍然没有意义,那么请考虑上面的代码示例。 get_items返回一个枚举,在该枚举上调用select 。 传递给select是一个知道如何在需要时提供序列中的下一个项目的实例。 重要的是,尚未计算整个项目集。 只有当select需要一个项目时它才会要求它,并且get_items的潜在代码将开始行动并提供它。 这种懒惰带有链,使得当地map要求时, select仅从序列中绘制下一个项目。 这样,可以一次对一个数据项执行长链操作。 实际上,以这种方式构造的代码甚至可以处理无限的值序列而不会出现任何类型的内存错误。

所以,这种懒惰很容易在C#中编码,我不知道如何在Ruby中编写它。

我希望更清楚(我将尽力避免将来在凌晨3点写问题。)

自Ruby 1.9以来, Enumerator支持它(后端移植到1.8.7)。 参见Generator:Ruby 。

陈词滥调的例子:

 fib = Enumerator.new do |y| y.yield i = 0 y.yield j = 1 while true k = i + j y.yield k i = j j = k end end 100.times { puts fib.next() } 

你的具体例子相当于10000000.times ,但让我们暂时假设时间方法不存在而你想自己实现它,它看起来像这样:

 class Integer def my_times return enum_for(:my_times) unless block_given? i=0 while i 

编辑:澄清我的答案:

在上面的例子中,my_times可以(而且)在没有块的情况下使用,它将返回一个Enumerable对象,它可以让你迭代从0到n的数字。 所以它完全等同于你在C#中的例子。

这使用enum_for方法。 enum_for方法将方法的名称作为其参数,这将产生一些项。 然后它返回一个Enumerator类的实例(包括模块Enumerable),迭代后将执行给定的方法,并为您提供该方法产生的项目。 请注意,如果您只迭代枚举的前x个项,则该方法将仅执行直到x项已被生成(即,只执行该方法所需的数量),并且如果迭代可枚举两次,则方法将执行两次。

在1.8.7+中,已经成为定义方法,它产生项目,因此当没有块调用时,它们将返回一个Enumerator,它将让用户懒洋洋地迭代这些项目。 这是通过添加行return enum_for(:name_of_this_method) unless block_given? 像我在我的例子中所做的那样开始。

在没有太多ruby经验的情况下,C#在yield return中的作用通常被称为惰性求值延迟执行 :仅在需要时提供答案。 它不是关于分配内存,而是关于在实际需要之前推迟计算,以类似于简单线性执行的方式表达(而不是基础迭代器与状态保存)。

一个快速的谷歌出现了测试版的ruby库 。 看看它是不是你想要的。

C#从Ruby中删除了’yield’关键字 – 请参阅此处的实现迭代器以获取更多信息。

至于你的实际问题,你可能是一个数组数组,你想在列表的整个长度上创建一个单向迭代? 也许值得将array.flatten视为一个起点 – 如果性能不错,那么你可能不需要进一步了解。