Feature #14781 » enumerator-generate.patch
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
|