简体   繁体   English

使用代码块创建的枚举器如何实际运行

[英]How does enumerators created with code block actually runs

It's just a simple question, how is y.<< method is able to halt the code-block mid execution??这只是一个简单的问题, y.<<方法如何能够在代码块执行过程中停止?

I have expected the code block to run only once and never halt in the middle:/我期望代码块只运行一次并且永远不会在中间停止:/

e = Enumerator.new do |y|
    puts "Ruby"
    y << 1
    y << 2
    puts "Ruby"
    y << 3
end

puts e.each.next
puts e.each.next
puts e.each.next
e.rewind
puts e.each.next
puts e.each.next
puts e.each.next

Almost all Ruby implementations are Free Software and Open Source, so you can just look at the source code to see how it is implemented.几乎所有 Ruby 的实现都是自由软件和开源的,因此您只需查看源代码即可了解其实现方式。

In Rubinius , the most interesting part is Enumerator::Iterator#reset , implemented in core/enumerator.rb :Rubinius中,最有趣的部分是Enumerator::Iterator#reset ,在core/enumerator.rb中实现:

@fiber = Fiber.new stack_size: STACK_SIZE do
  obj = @object
  @result = obj.each { |*val| Fiber.yield *val }
  @done = true
end

and Enumerator::Iterator#next :Enumerator::Iterator#next

val = @fiber.resume

TruffleRuby 's implementation is very similar, as you can see in src/main/ruby/truffleruby/core/enumerator.rb : TruffleRuby的实现非常相似,如您在src/main/ruby/truffleruby/core/enumerator.rb中所见:

class FiberGenerator
  # irrelevant methods omitted

  def next
    reset unless @fiber

    val = @fiber.resume

    raise StopIteration, 'iteration has ended' if @done

    val
  end

  def reset
    @done = false
    @fiber = Fiber.new do
      obj = @object
      @result = obj.each do |*val|
        Fiber.yield(*val)
      end
      @done = true
    end
  end
end

JRuby is also very similar, as you can see in core/src/main/ruby/jruby/kernel/enumerator.rb : JRuby也非常相似,您可以在core/src/main/ruby/jruby/kernel/enumerator.rb中看到:

class FiberGenerator
  # irrelevant methods omitted

  def next
    reset unless @fiber&.__alive__

    val = @fiber.resume

    raise StopIteration, 'iteration has ended' if @state.done

    val
  end

  def reset
    @state.done = false
    @state.result = nil
    @fiber = Fiber.new(&@state)
  end

end

MRuby 's implementation is very similar, as you can see in mrbgems/mruby-enumerator/mrblib/enumerator.rb . MRuby的实现非常相似,您可以在mrbgems/mruby-enumerator/mrblib/enumerator.rb中看到。

YARV also uses Fibers, as can be seen inenumerator.c , for example here: YARV也使用纤程,如enumerator.c所示,例如:

static void
next_init(VALUE obj, struct enumerator *e)
{
    VALUE curr = rb_fiber_current();
    e->dst = curr;
    e->fib = rb_fiber_new(next_i, obj);
    e->lookahead = Qundef;
}

static VALUE
get_next_values(VALUE obj, struct enumerator *e)
{
    VALUE curr, vs;

    if (e->stop_exc)
    rb_exc_raise(e->stop_exc);

    curr = rb_fiber_current();

    if (!e->fib || !rb_fiber_alive_p(e->fib)) {
    next_init(obj, e);
    }

    vs = rb_fiber_resume(e->fib, 1, &curr);
    if (e->stop_exc) {
    e->fib = 0;
    e->dst = Qnil;
    e->lookahead = Qundef;
    e->feedvalue = Qundef;
    rb_exc_raise(e->stop_exc);
    }
    return vs;
}

So, not surprisingly, Enumerator is implemented using Fiber s in many Ruby implementations.因此,毫不奇怪,在许多 Ruby 实现中, Enumerator是使用Fiber实现的。 Fiber is essentially just Ruby's name for semi-coroutines , and of course, coroutines are a popular way of implementing generators and iterators . Fiber本质上只是 Ruby 对半协程的称呼,当然,协程是一种流行的实现生成器迭代器的方式。 Eg CPython and CoreCLR also implement generators using coroutines.例如 CPython 和 CoreCLR 也使用协程实现生成器。

One exception to this seems to be Opal .一个例外似乎是蛋白石 My assumption was that Opal would use ECMAScript Generators to implement Ruby Enumerator s, but it does not look like that is the case.我的假设是 Opal 会使用ECMAScript Generators来实现 Ruby Enumerator s,但事实并非如此。 The implementation of Ruby Enumerator s in Opal is found in opal/corelib/enumerator.rb , opal/corelib/enumerator/generator.rb , and opal/corelib/enumerator/yielder.rb with some help from opal/corelib/runtime.js , but unfortunately, I don't fully understand it. Opal 中 Ruby Enumerator的实现在opal/corelib/enumerator.rbopal/corelib/enumerator/generator.rbopal/corelib/enumerator/yielder.rb中找到,并得到了opal/corelib/runtime.js的一些帮助,但不幸的是,我并不完全理解它。 It does not appear to use either Ruby Fiber s or ECMAScript Generators , though.不过,它似乎没有使用 Ruby Fiber或 ECMAScript Generators

By the way, your usage of Enumerator s is somewhat strange: you call Enumerator#each six times without a block, but calling Enumerator#each without a block just returns the Enumerator itself:顺便说一下,您对Enumerator的使用有些奇怪:您在没有块的情况下调用Enumerator#each六次,但是在没有块的情况下调用Enumerator#each只会返回Enumerator本身:

eachenum eachenum

Iterates over the block according to how this Enumerator was constructed.根据此Enumerator的构造方式遍历块。 If no block and no arguments are given, returns self.如果没有给出块和 arguments,则返回 self。

So, in other words, all those calls to Enumerator#each are just no-ops.因此,换句话说,所有对Enumerator#each的调用都只是空操作。 It would make much more sense to just call Enumerator#next directly:直接调用Enumerator#next会更有意义:

puts e.next
puts e.next
puts e.next
e.rewind
puts e.next
puts e.next
puts e.next

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM