非常教程

Ruby 2.4参考手册

枚举 | Enumerator

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;
}
Ruby 2.4

Ruby 是一种面向对象、命令式、函数式、动态的通用编程语言,是世界上最优美而巧妙的语言。

主页 https://www.ruby-lang.org/
源码 https://github.com/ruby/ruby
版本 2.4
发布版本 2.4.1

Ruby 2.4目录

1.缩略 | Abbrev
2.ARGF
3.数组 | Array
4.Base64
5.基本对象 | BasicObject
6.基准测试 | Benchmark
7.BigDecimal
8.绑定 | Binding
9.CGI
10.类 | Class
11.比较 | Comparable
12.负责 | Complex
13.计算续体 | Continuation
14.覆盖 | Coverage
15.CSV
16.日期 | Date
17.日期时间 | DateTime
18.DBM
19.代理 | Delegator
20.摘要 | Digest
21.Dir
22.DRb
23.编码 | Encoding
24.枚举 | Enumerable
25.枚举 | Enumerator
26.ENV
27.ERB
28.错误 | Errors
29.Etc
30.期望值 | Exception
31.错误类 | FalseClass
32.Fiber
33.Fiddle
34.文件 | File
35.文件实用程序 | FileUtils
36.查找 | Find
37.浮点 | Float
38.Forwardable
39.GC
40.GDBM
41.GetoptLong
42.Hash
43.Integer
44.IO
45.IPAddr
46.JSON
47.Kernel
48.语言 | 3Language
49.记录 | Logger
50.编排 | Marshal
51.MatchData
52.数学 | Math
53.矩阵 | Matrix
54.方法 | Method
55.模型 | Module
56.监控 | Monitor
57. 互斥 | Mutex
58.Net
59.Net::FTP
60.Net::HTTP
61.Net::IMAP
62.Net::SMTP
63.NilClass
64.数字 | Numeric
65.对象 | Object
66.ObjectSpace
67.Observable
68.Open3
69.OpenSSL
70.OpenStruct
71.OpenURI
72.OptionParser
73.路径名 | Pathname
74.完整输出 | PrettyPrint
75.Prime
76.Proc
77.过程 | Process
78.PStore
79.PTY
80.队列 | Queue
81.随机 | Random
82.范围 | Range
83.合理的 | Rational
84.Readline
85.Regexp
86.Resolv
87.Ripper
88.RubyVM
89.Scanf
90.SDBM
91.SecureRandom
92.Set
93.Shell
94.信号 | Signal
95.Singleton
96.套接字 | Socket
97.字符串 | String
98.StringIO
99.StringScanner
100.结构 | Struct