Project

General

Profile

Feature #14781 » enumerator-generate.patch

zverok (Victor Shepelev), 08/11/2019 10:29 AM

View differences:

enumerator.c
return rb_attr_get(self, id_result);
}
static VALUE
enumeratorgenerate_i(RB_BLOCK_CALL_FUNC_ARGLIST(yielder, enumerator))
{
VALUE proc, cur;
cur = rb_ivar_get(enumerator, rb_intern("enumeratorgenerate_init"));
proc = rb_ivar_get(enumerator, rb_intern("enumeratorgenerate_proc"));
// We need to pass initial value too -- unless there were none.
if (cur == rb_intern("__none__")) {
cur = Qnil;
} else {
rb_funcallv(yielder, idLTLT, 1, &cur);
}
for (;;) {
cur = rb_proc_call(proc, rb_ary_new3(1, cur));
rb_funcallv(yielder, idLTLT, 1, &cur);
}
return Qnil;
}
/*
* call-seq:
* Enumerator.generate(initial) { |val| } -> enumerator
* Enumerator.generate { |val| } -> enumerator
*
* Creates an infinite enumerator from any block, just called over and over.
* Result of the previous iteration is passed to the next one. If +initial+
* is provided, it is passed to the first iteration, and becomes the first
* element of the enumerator; if it is not provided, first iteration receives
* +nil+, and its result becomes first element of the iterator.
*
* One can think of this method as a Ruby's way of expressing +loop+/+while+,
* in the same manner as Array#each is Ruby's way of expressing +for+.
*
* Examples of usage:
*
* # With initial value
*
* # Simplest possible infinite sequence:
* Enumerator.generate(1, &:succ) # => enumerator of 1, 2, 3, 4, ....
*
* # Find next Tuesday
* require 'date'
* Enumerator.generate(Date.today, &:succ).detect(&:tuesday?)
*
* # Some tree navigation:
* Enumerator.generate(node) { |n| n.parent }.take_while { |n| n.respond_to?(:parent) }
*
*
* # Without initial value
*
* # Infinite random number sequence:
* Enumerator.generate { rand(10) }
*
* # Simple lexer:
* require 'strscan'
* scanner = StringScanner.new('7+38/6')
* PATTERN = %r{\d+|[-+/*]}
* p Enumerator.generate { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first
* # => ["7", "+", "38", "/", "6"]
*/
static VALUE
enumerator_generate(int argc, VALUE *argv, VALUE dummy)
{
VALUE enumerator;
VALUE init, proc;
if (rb_scan_args(argc, argv, "01", &init) == 0) {
// We need a way to pass the fact that there were no start value,
// An `nil` is not good enough: it still can be "`nil` as a start value"
init = rb_intern("__none__");
}
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "no block given");
}
proc = rb_block_proc();
enumerator = rb_obj_alloc(rb_cEnumerator);
rb_ivar_set(enumerator, rb_intern("enumeratorgenerate_init"), init);
rb_ivar_set(enumerator, rb_intern("enumeratorgenerate_proc"), proc);
rb_block_call(enumerator, idInitialize, 0, 0, enumeratorgenerate_i, enumerator);
return enumerator;
}
/*
* Document-class: Enumerator::Chain
*
......
rb_include_module(rb_cEnumerator, rb_mEnumerable);
rb_define_alloc_func(rb_cEnumerator, enumerator_allocate);
rb_define_singleton_method(rb_cEnumerator, "generate", enumerator_generate, -1);
rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
rb_define_method(rb_cEnumerator, "each", enumerator_each, -1);
test/ruby/test_enumerator.rb
e5.inspect
)
end
def test_generate
# Without initial object
passed_args = []
enum = Enumerator.generate { |obj| passed_args << obj; (obj || 0).succ }
assert_instance_of(Enumerator, enum)
assert_equal [1, 2, 3], enum.take(3)
assert_equal [nil, 1, 2], passed_args
# With initial object
passed_args = []
enum = Enumerator.generate(1) { |obj| passed_args << obj; obj.succ }
assert_instance_of(Enumerator, enum)
assert_equal [1, 2, 3], enum.take(3)
assert_equal [1, 2], passed_args
# Without block
assert_raise(ArgumentError) do
Enumerator.generate
end
end
end
(2-2/3)