枚举器如何在 Ruby 1.9.1 中工作? [英] How do Enumerators work in Ruby 1.9.1?

查看:39
本文介绍了枚举器如何在 Ruby 1.9.1 中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题不是关于如何在 Ruby 1.9.1 中使用枚举器,而是我很好奇它们是如何工作的.这是一些代码:

This question is not about how to use Enumerators in Ruby 1.9.1 but rather I am curious how they work. Here is some code:

class Bunk
  def initialize
    @h = [*1..100]
  end

  def each
    if !block_given?
      enum_for(:each)
    else
      0.upto(@h.length) { |i|
        yield @h[i]
      }
    end
  end
end

在上面的代码中我可以使用e = Bunk.new.each,然后e.next, e.next 得到每个连续的元素,但它究竟是如何暂停执行然后在正确的位置恢复的?

In the above code I can use e = Bunk.new.each, and then e.next, e.next to get each successive element, but how exactly is it suspending execution and then resuming at the right spot?

我知道如果 0.upto 中的 yield 被替换为 Fiber.yield 那么很容易理解,但这里的情况并非如此.这是一个普通的旧 yield,那么它是如何工作的呢?

I am aware that if the yield in the 0.upto is replaced with Fiber.yield then it's easy to understand, but that is not the case here. It is a plain old yield, so how does it work?

我查看了 enumerator.c,但对我来说有点难以理解.也许有人可以在 Ruby 中提供一个实现,使用纤程,而不是 1.8.6 风格的基于延续的枚举器,这一切都清楚了吗?

I looked at enumerator.c but it's neigh on incomprehensible for me. Maybe someone could provide an implementation in Ruby, using fibers, not 1.8.6 style continuation-based enumerators, that makes it all clear?

推荐答案

这是一个使用 Fibers 的普通 ruby​​ 枚举器,其行为应该与原来的非常相似:

Here's a plain ruby enumerator that uses Fibers and should pretty much behave like the original:

class MyEnumerator
  include Enumerable

  def initialize(obj, iterator_method)
    @f = Fiber.new do
      obj.send(iterator_method) do |*args|
        Fiber.yield(*args)
      end
      raise StopIteration
    end
  end

  def next
    @f.resume
  end

  def each
    loop do
      yield self.next
    end
  rescue StopIteration
    self
  end
end

如果有人对控制流使用异常感到不安:真正的 Enumerator 最后也引发了 StopIteration,所以我只是模拟了原始行为.

And in case anyone is feeling uneasy about using exceptions for control flow: The real Enumerator raises StopIteration at the end, too, so I just emulated the original behaviour.

用法:

>> enum = MyEnumerator.new([1,2,3,4], :each_with_index)
=> #<MyEnumerator:0x9d184f0 @f=#<Fiber:0x9d184dc>
>> enum.next
=> [1, 0]
>> enum.next
=> [2, 1]
>> enum.to_a
=> [[3, 2], [4, 3]]

这篇关于枚举器如何在 Ruby 1.9.1 中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆