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被提升。
这种方法可以用来区分yield
和yield 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;
}