Class: Enumerator
- Includes:
- Enumerable
- Defined in:
- enumerator.c,
enumerator.c
Overview
A class which allows both internal and external iteration.
An Enumerator can be created by the following methods.
-
Kernel#to_enum
-
Kernel#enum_for
-
Enumerator.new
Most methods have two forms: a block form where the contents are evaluated for each item in the enumeration, and a non-block form which returns a new Enumerator wrapping the iteration.
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
This allows you to chain Enumerators together. For example, you can map a list’s elements to strings containing the index and the element as a string via:
puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
# => ["0:foo", "1:bar", "2:baz"]
An Enumerator can also be used as an external iterator. For example, Enumerator#next returns the next value of the iterator or raises StopIteration if the Enumerator is at the end.
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
You can use this to implement an internal iterator as follows:
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
Direct Known Subclasses
Defined Under Namespace
Classes: Generator, Lazy, Yielder
Instance Method Summary collapse
-
#each(*args) ⇒ Object
Iterates over the block according to how this Enumerator was constructed.
-
#each_with_index ⇒ Object
Same as Enumerator#with_index(0), i.e.
-
#each_with_object(memo) ⇒ Object
Iterates the given block for each element with an arbitrary object,
obj
, and returnsobj
. -
#obj ⇒ nil
Sets the value to be returned by the next yield inside
e
. -
#initialize(*args) ⇒ Object
constructor
Creates a new Enumerator object, which can be used as an Enumerable.
-
#initialize_copy(orig) ⇒ Object
:nodoc:.
-
#inspect ⇒ String
Creates a printable version of e.
-
#next ⇒ Object
Returns the next object in the enumerator, and move the internal position forward.
-
#next_values ⇒ Array
Returns the next object as an array in the enumerator, and move the internal position forward.
-
#peek ⇒ Object
Returns the next object in the enumerator, but doesn’t move the internal position forward.
-
#peek_values ⇒ Array
Returns the next object as an array, similar to Enumerator#next_values, but doesn’t move the internal position forward.
-
#rewind ⇒ Object
Rewinds the enumeration sequence to the beginning.
-
#size ⇒ Integer, ...
Returns the size of the enumerator, or
nil
if it can’t be calculated lazily. -
#with_index(*args) ⇒ Object
Iterates the given block for each element with an index, which starts from
offset
. -
#with_object(memo) ⇒ Object
Iterates the given block for each element with an arbitrary object,
obj
, and returnsobj
.
Methods included from Enumerable
#all?, #any?, #chunk, #collect, #collect_concat, #count, #cycle, #detect, #drop, #drop_while, #each_cons, #each_entry, #each_slice, #entries, #find, #find_all, #find_index, #first, #flat_map, #grep, #group_by, #include?, #inject, #lazy, #map, #max, #max_by, #member?, #min, #min_by, #minmax, #minmax_by, #none?, #one?, #partition, #reduce, #reject, #reverse_each, #select, #slice_after, #slice_before, #slice_when, #sort, #sort_by, #take, #take_while, #to_a, #to_h, #zip
Constructor Details
#new(size = nil) {|yielder| ... } ⇒ Object #new(obj, method = :each, *args) ⇒ Object
Creates a new Enumerator object, which can be used as an Enumerable.
In the first form, iteration is defined by the given block, in which a “yielder” object, given as block parameter, can be used to yield a value by calling the yield
method (aliased as <<):
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]
The optional parameter can be used to specify how to calculate the size in a lazy fashion (see Enumerator#size). It can either be a value or a callable object.
In the second, deprecated, form, a generated Enumerator iterates over the given object using the given method with the given arguments passed.
Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum instead.
e = Enumerator.new(ObjectSpace, :each_object)
#-> ObjectSpace.enum_for(:each_object)
e.select { |obj| obj.is_a?(Class) } #=> array of all classes
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'enumerator.c', line 332
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);
}
|
Instance Method Details
#each {|elm| ... } ⇒ Object #each ⇒ Enumerator #each(*appending_args) {|elm| ... } ⇒ Object #each(*appending_args) ⇒ Object
Iterates over the block according to how this Enumerator was constructed. If no block and no arguments are given, returns self.
Examples
"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
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'enumerator.c', line 471
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| ... } ⇒ Object #each_with_index ⇒ Object
Same as Enumerator#with_index(0), i.e. there is no starting offset.
If no block is given, a new Enumerator is returned that includes the index.
552 553 554 555 556 |
# File 'enumerator.c', line 552
static VALUE
enumerator_each_with_index(VALUE obj)
{
return enumerator_with_index(0, NULL, obj);
}
|
#each_with_object(obj) {|(*args), obj| ... } ⇒ Object #each_with_object(obj) ⇒ Object #with_object(obj) {|(*args), obj| ... } ⇒ Object #with_object(obj) ⇒ Object
Iterates the given block for each element with an arbitrary object, obj
, and returns obj
If no block is given, returns a new Enumerator.
Example
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
596 597 598 599 600 601 602 603 |
# File 'enumerator.c', line 596
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;
}
|
#obj ⇒ nil
Sets the value to be returned by the next yield inside e
.
If the value is not set, the yield returns nil.
This value is cleared after being yielded.
# 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)
897 898 899 900 901 902 903 904 905 906 907 908 |
# File 'enumerator.c', line 897
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;
}
|
#initialize_copy(orig) ⇒ Object
:nodoc:
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
# File 'enumerator.c', line 366
static VALUE
enumerator_init_copy(VALUE obj, VALUE orig)
{
struct enumerator *ptr0, *ptr1;
if (!OBJ_INIT_COPY(obj, orig)) return obj;
ptr0 = enumerator_ptr(orig);
if (ptr0->fib) {
/* Fibers cannot be copied */
rb_raise(rb_eTypeError, "can't copy execution context");
}
TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr1);
if (!ptr1) {
rb_raise(rb_eArgError, "unallocated enumerator");
}
ptr1->obj = ptr0->obj;
ptr1->meth = ptr0->meth;
ptr1->args = ptr0->args;
ptr1->fib = 0;
ptr1->lookahead = Qundef;
ptr1->feedvalue = Qundef;
ptr1->size = ptr0->size;
ptr1->size_fn = ptr0->size_fn;
return obj;
}
|
#inspect ⇒ String
Creates a printable version of e.
1019 1020 1021 1022 1023 |
# File 'enumerator.c', line 1019
static VALUE
enumerator_inspect(VALUE obj)
{
return rb_exec_recursive(inspect_enumerator, obj, 0);
}
|
#next ⇒ Object
Returns the next object in the enumerator, and move the internal position forward. When the position reached at the end, StopIteration is raised.
Example
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
Note that enumeration sequence by next
does not affect other non-external enumeration methods, unless the underlying iteration methods itself has side-effect, e.g. IO#each_line.
770 771 772 773 774 775 |
# File 'enumerator.c', line 770
static VALUE
enumerator_next(VALUE obj)
{
VALUE vs = enumerator_next_values(obj);
return ary2sv(vs, 0);
}
|
#next_values ⇒ Array
Returns the next object as an array in the enumerator, and move the internal position forward. When the position reached at the end, StopIteration is raised.
This method can be used to distinguish yield
and yield nil
.
Example
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]
Note that next_values
does not affect other non-external enumeration methods unless underlying iteration method itself has side-effect, e.g. IO#each_line.
713 714 715 716 717 718 719 720 721 722 723 724 725 726 |
# File 'enumerator.c', line 713
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
Returns the next object in the enumerator, but doesn’t move the internal position forward. If the position is already at the end, StopIteration is raised.
Example
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
844 845 846 847 848 849 |
# File 'enumerator.c', line 844
static VALUE
enumerator_peek(VALUE obj)
{
VALUE vs = enumerator_peek_values(obj);
return ary2sv(vs, 1);
}
|
#peek_values ⇒ Array
Returns the next object as an array, similar to Enumerator#next_values, but doesn’t move the internal position forward. If the position is already at the end, StopIteration is raised.
Example
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
816 817 818 819 820 |
# File 'enumerator.c', line 816
static VALUE
enumerator_peek_values_m(VALUE obj)
{
return rb_ary_dup(enumerator_peek_values(obj));
}
|
#rewind ⇒ Object
Rewinds the enumeration sequence to the beginning.
If the enclosed object responds to a “rewind” method, it is called.
919 920 921 922 923 924 925 926 927 928 929 930 931 932 |
# File 'enumerator.c', line 919
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 ⇒ Integer, ...
Returns the size of the enumerator, or nil
if it can’t be calculated lazily.
(1..100).to_a.permutation(4).size # => 94109400
loop.size # => Float::INFINITY
(1..100).drop_while.size # => nil
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
# File 'enumerator.c', line 1036
static VALUE
enumerator_size(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
int argc = 0;
const VALUE *argv = NULL;
VALUE size;
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| ... } ⇒ Object #with_index(offset = 0) ⇒ Object
Iterates the given block for each element with an index, which starts from offset
. If no block is given, returns a new Enumerator that includes the index, starting from offset
offset
-
the starting index to use
528 529 530 531 532 533 534 535 536 537 538 539 540 |
# File 'enumerator.c', line 528
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)NEW_MEMO(memo, 0, 0));
}
|
#each_with_object(obj) {|(*args), obj| ... } ⇒ Object #each_with_object(obj) ⇒ Object #with_object(obj) {|(*args), obj| ... } ⇒ Object #with_object(obj) ⇒ Object
Iterates the given block for each element with an arbitrary object, obj
, and returns obj
If no block is given, returns a new Enumerator.
Example
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
596 597 598 599 600 601 602 603 |
# File 'enumerator.c', line 596
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;
}
|