Ruby 2.4

Enumerator

类枚举器

Parent:ObjectIncluded modules:Enumerable

一个允许内部和外部迭代的类。

枚举器可以通过以下方法创建。

  • Kernel#to_enum

  • Kernel#enum_for

  • ::new

大多数方法有两种形式:枚举中为每个项目评估内容的块形式,以及返回包装迭代的新Enumerator的非块形式。

enumerator = %w(one two three).each puts enumerator.class # => Enumerator enumerator.each_with_object("foo") do |item, obj| puts "#{obj}: #{item}" end # foo: one # foo: two # foo: three enum_with_obj = enumerator.each_with_object("foo") puts enum_with_obj.class # => Enumerator enum_with_obj.each do |item, obj| puts "#{obj}: #{item}" end # foo: one # foo: two # foo: three

这使您可以将枚举器链接在一起。例如,可以通过以下方式将列表元素映射到包含索引和元素的字符串:

puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } # => ["0:foo", "1:bar", "2:baz"]

枚举器也可以用作外部迭代器。例如,#next返回迭代器的下一个值,或者如果枚举器在最后,则引发StopIteration。

e = [1,2,3].each # returns an enumerator object. puts e.next # => 1 puts e.next # => 2 puts e.next # => 3 puts e.next # raises StopIteration

你可以使用它来实现一个内部迭代器,如下所示:

def ext_each(e) while true begin vs = e.next_values rescue StopIteration return $!.result end y = yield(*vs) e.feed y end end o = Object.new def o.each puts yield puts yield(1) puts yield(1, 2) 3 end # use o.each as an internal iterator directly. puts o.each {|*x| puts x; [:b, *x] } # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # convert o.each to an external iterator for # implementing an internal iterator. puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] } # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3

公共类方法

new(size = nil) { |yielder| ... } Show source

new(obj, method = :each, *args)

创建一个新的Enumerator对象,该对象可以用作Enumerable。

在第一种形式中,迭代由给定的块定义,其中通过调用yield方法(别名为+ << +),可以使用作为块参数给出的“yielder”对象来产生值:

fib = Enumerator.new do |y| a = b = 1 loop do y << a a, b = b, a + b end end p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

可选参数可用于指定如何以惰性方式计算大小(请参阅#size)。它可以是一个值或一个可调用的对象。

在第二种不推荐使用的形式中,生成的Enumerator使用给定的方法在给定参数传递的情况下对给定对象进行迭代。

不鼓励使用这种形式。改用Kernel#enum_for或Kernel#to_enum。

e = Enumerator.new(ObjectSpace, :each_object) #-> ObjectSpace.enum_for(:each_object) e.select { |obj| obj.is_a?(Class) } #=> array of all classes

static VALUE enumerator_initialize(int argc, VALUE *argv, VALUE obj) { VALUE recv, meth = sym_each; VALUE size = Qnil; if (rb_block_given_p()) { rb_check_arity(argc, 0, 1 recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc() if (argc) { if (NIL_P(argv[0]) || rb_respond_to(argv[0], id_call) || (RB_TYPE_P(argv[0], T_FLOAT) && RFLOAT_VALUE(argv[0]) == INFINITY)) { size = argv[0]; } else { size = rb_to_int(argv[0] } argc = 0; } } else { rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS rb_warn("Enumerator.new without a block is deprecated; use Object#to_enum" recv = *argv++; if (--argc) { meth = *argv++; --argc; } } return enumerator_init(obj, recv, meth, argc, argv, 0, size }

公共实例方法

each { |elm| block } → obj Show source

each → enum

each(*appending_args) { |elm| block } → obj

each(*appending_args) → an_enumerator

根据Enumerator的构造方式对块进行迭代。如果没有给出块并且没有参数,则返回自身。

例子

"Hello, world!".scan(/\w+/) #=> ["Hello", "world"] "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"] "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"] obj = Object.new def obj.each_arg(a, b=:b, *rest) yield a yield b yield rest :method_returned end enum = obj.to_enum :each_arg, :a, :x enum.each.to_a #=> [:a, :x, []] enum.each.equal?(enum) #=> true enum.each { |elm| elm } #=> :method_returned enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]] enum.each(:y, :z).equal?(enum) #=> false enum.each(:y, :z) { |elm| elm } #=> :method_returned

static VALUE enumerator_each(int argc, VALUE *argv, VALUE obj) { if (argc > 0) { struct enumerator *e = enumerator_ptr(obj = rb_obj_dup(obj) VALUE args = e->args; if (args) { #if SIZEOF_INT < SIZEOF_LONG /* check int range overflow */ rb_long2int(RARRAY_LEN(args) + argc #endif args = rb_ary_dup(args rb_ary_cat(args, argv, argc } else { args = rb_ary_new4(argc, argv } e->args = args; } if (!rb_block_given_p()) return obj; return enumerator_block_call(obj, 0, obj }

each_with_index {|(*args), idx| ... } Show source

each_with_index

与#with_index相同,即没有起始偏移量。

如果没有给出块,则返回包含该索引的新Enumerator。

static VALUE enumerator_each_with_index(VALUE obj) { return enumerator_with_index(0, NULL, obj }

each_with_object(obj) {|(*args), obj| ... } Show source

each_with_object(obj)

用任意对象obj遍历每个元素的给定块,并返回obj

如果没有给出块,则返回一个新的枚举器。

to_three = Enumerator.new do |y| 3.times do |x| y << x end end to_three_with_string = to_three.with_object("foo") to_three_with_string.each do |x,string| puts "#{string}: #{x}" end # => foo:0 # => foo:1 # => foo:2

static VALUE enumerator_with_object(VALUE obj, VALUE memo) { RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enumerator_enum_size enumerator_block_call(obj, enumerator_with_object_i, memo return memo; }

feed obj → nil Show source

设置e中的下一个产值返回的值。

如果该值未设置,则产值返回零。

这个值在被生成后被清除。

# Array#map passes the array's elements to "yield" and collects the # results of "yield" as an array. # Following example shows that "next" returns the passed elements and # values passed to "feed" are collected as an array which can be # obtained by StopIteration#result. e = [1,2,3].map p e.next #=> 1 e.feed "a" p e.next #=> 2 e.feed "b" p e.next #=> 3 e.feed "c" begin e.next rescue StopIteration p $!.result #=> ["a", "b", "c"] end o = Object.new def o.each x = yield # (2) blocks p x # (5) => "foo" x = yield # (6) blocks p x # (8) => nil x = yield # (9) blocks p x # not reached w/o another e.next end e = o.to_enum e.next # (1) e.feed "foo" # (3) e.next # (4) e.next # (7) # (10)

static VALUE enumerator_feed(VALUE obj, VALUE v) { struct enumerator *e = enumerator_ptr(obj if (e->feedvalue != Qundef) { rb_raise(rb_eTypeError, "feed value already set" } e->feedvalue = v; return Qnil; }

inspect → string Show source

创建e 的可打印版本。

static VALUE enumerator_inspect(VALUE obj) { return rb_exec_recursive(inspect_enumerator, obj, 0 }

next → object Show source

返回枚举数中的下一个对象,并向前移动内部位置。当位置到达时,StopIteration被提升。

a = [1,2,3] e = a.to_enum p e.next #=> 1 p e.next #=> 2 p e.next #=> 3 p e.next #raises StopIteration

请注意枚举序列next不会影响其他非外部枚举方法,除非基础迭代方法本身具有副作用,例如IO#each_line。

static VALUE enumerator_next(VALUE obj) { VALUE vs = enumerator_next_values(obj return ary2sv(vs, 0 }

next_values → array Show source

将下一个对象作为枚举数组中的数组返回,并向前移动内部位置。当位置到达时,StopIteration被提升。

这种方法可以用来区分yieldyield nil

o = Object.new def o.each yield yield 1 yield 1, 2 yield nil yield [1, 2] end e = o.to_enum p e.next_values p e.next_values p e.next_values p e.next_values p e.next_values e = o.to_enum p e.next p e.next p e.next p e.next p e.next ## yield args next_values next # yield [] nil # yield 1 [1] 1 # yield 1, 2 [1, 2] [1, 2] # yield nil [nil] nil # yield [1, 2] [[1, 2]] [1, 2]

请注意,除非基础迭代方法本身具有副作用,否则next_values不会影响其他非外部枚举方法。例如:IO#each_line。

static VALUE enumerator_next_values(VALUE obj) { struct enumerator *e = enumerator_ptr(obj VALUE vs; if (e->lookahead != Qundef) { vs = e->lookahead; e->lookahead = Qundef; return vs; } return get_next_values(obj, e }

peek → object Show source

返回枚举数中的下一个对象,但不向前移动内部位置。如果该位置已经结束,则会触发StopIteration。

a = [1,2,3] e = a.to_enum p e.next #=> 1 p e.peek #=> 2 p e.peek #=> 2 p e.peek #=> 2 p e.next #=> 2 p e.next #=> 3 p e.peek #raises StopIteration

static VALUE enumerator_peek(VALUE obj) { VALUE vs = enumerator_peek_values(obj return ary2sv(vs, 1 }

peek_values → array Show source

以数组形式返回下一个对象,与#next_values类似,但不向前移动内部位置。如果该位置已经结束,则会触发StopIteration。

o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum p e.peek_values #=> [] e.next p e.peek_values #=> [1] p e.peek_values #=> [1] e.next p e.peek_values #=> [1, 2] e.next p e.peek_values # raises StopIteration

static VALUE enumerator_peek_values_m(VALUE obj) { return rb_ary_dup(enumerator_peek_values(obj) }

rewind → e Show source

将枚举序列倒回到开头。

如果封闭对象响应“倒回”方法,则调用该方法。

static VALUE enumerator_rewind(VALUE obj) { struct enumerator *e = enumerator_ptr(obj rb_check_funcall(e->obj, id_rewind, 0, 0 e->fib = 0; e->dst = Qnil; e->lookahead = Qundef; e->feedvalue = Qundef; e->stop_exc = Qfalse; return obj; }

size → int, Float::INFINITY or nil Show source

返回枚举数的大小,如果不能延迟计算,则返回nil。

(1..100).to_a.permutation(4).size # => 94109400 loop.size # => Float::INFINITY (1..100).drop_while.size # => nil

static VALUE enumerator_size(VALUE obj) { struct enumerator *e = enumerator_ptr(obj int argc = 0; const VALUE *argv = NULL; VALUE size; if (e->procs) { struct generator *g = generator_ptr(e->obj VALUE receiver = rb_check_funcall(g->obj, id_size, 0, 0 long i = 0; for (i = 0; i < RARRAY_LEN(e->procs i++) { VALUE proc = RARRAY_AREF(e->procs, i struct proc_entry *entry = proc_entry_ptr(proc lazyenum_size_func *size = entry->fn->size; if (!size) { return Qnil; } receiver = (*size)(proc, receiver } return receiver; } if (e->size_fn) { return (*e->size_fn)(e->obj, e->args, obj } if (e->args) { argc = (int)RARRAY_LEN(e->args argv = RARRAY_CONST_PTR(e->args } size = rb_check_funcall(e->size, id_call, argc, argv if (size != Qundef) return size; return e->size; }

with_index(offset = 0) {|(*args), idx| ... } Show source

with_index(offset = 0)

使用从偏移量开始的索引对每个元素的给定块进行迭代。 如果没有给出块,则返回一个包含索引的新Enumerator(从偏移量开始)

offset

要使用的起始索引

static VALUE enumerator_with_index(int argc, VALUE *argv, VALUE obj) { VALUE memo; rb_scan_args(argc, argv, "01", &memo RETURN_SIZED_ENUMERATOR(obj, argc, argv, enumerator_enum_size if (NIL_P(memo)) memo = INT2FIX(0 else memo = rb_to_int(memo return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)MEMO_NEW(memo, 0, 0) }

with_object(obj) {|(*args), obj| ... } Show source

with_object(obj)

用任意对象obj遍历每个元素的给定块,并返回obj

如果没有给出块,则返回一个新的枚举器。

to_three = Enumerator.new do |y| 3.times do |x| y << x end end to_three_with_string = to_three.with_object("foo") to_three_with_string.each do |x,string| puts "#{string}: #{x}" end # => foo:0 # => foo:1 # => foo:2

static VALUE enumerator_with_object(VALUE obj, VALUE memo) { RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enumerator_enum_size enumerator_block_call(obj, enumerator_with_object_i, memo return memo; }