Hi,

At Sat, 26 Sep 2009 10:57:19 +0900,
Tanaka Akira wrote in [ruby-core:25782]:
> I think Enumerator.new (and possibly to_enum, enum_for)
> should be able to take an array consists of two method names
> for internal and external iteration as:
>   Enumerator.new(obj, [method1, method2], *args)
> method1 is for internal iteration and method2 is for
> external iteration.  The enumerator created by
> Enumerator.new uses method2 when next method is called if
> method2 is provided.  obj.method2 should return an object
> for external iteration. 

Interesting.


Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 25098) +++ include/ruby/intern.h (working copy) @@ -189,4 +189,5 @@ VALUE rb_enumeratorize(VALUE, VALUE, int argc, argv); \ } while (0) +NORETURN(void rb_stop_iteration(void)); /* error.c */ VALUE rb_exc_new(VALUE, const char*, long); Index: enumerator.c =================================================================== --- enumerator.c (revision 25098) +++ enumerator.c (working copy) @@ -85,4 +85,5 @@ struct enumerator { VALUE obj; ID meth; + ID meth_ext; VALUE args; VALUE fib; @@ -340,4 +341,5 @@ enumerator_init(VALUE enum_obj, VALUE ob { struct enumerator *ptr; + VALUE methpair; TypedData_Get_Struct(enum_obj, struct enumerator, &enumerator_data_type, ptr); @@ -348,5 +350,17 @@ enumerator_init(VALUE enum_obj, VALUE ob ptr->obj = obj; - ptr->meth = rb_to_id(meth); + if (!NIL_P(methpair = rb_check_array_type(meth)) && RARRAY_LEN(methpair) == 2) { + VALUE meth_ext = RARRAY_PTR(methpair)[1]; + ptr->meth = rb_to_id(RARRAY_PTR(methpair)[0]); + ptr->meth_ext = rb_to_id(meth_ext); + } + else { + ptr->meth = rb_to_id(meth); + meth = rb_usascii_str_new_cstr("external_iterator_for_"); + rb_str_append(meth, rb_id2str(ptr->meth)); + ptr->meth_ext = rb_intern_str(meth); + if (!rb_obj_respond_to(obj, ptr->meth_ext, TRUE)) + ptr->meth_ext = 0; + } if (argc) ptr->args = rb_ary_new4(argc, argv); ptr->fib = 0; @@ -401,5 +415,6 @@ enumerator_initialize(int argc, VALUE *a recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc()); - } else { + } + else { recv = *argv++; if (--argc) { @@ -432,4 +447,5 @@ enumerator_init_copy(VALUE obj, VALUE or ptr1->obj = ptr0->obj; ptr1->meth = ptr0->meth; + ptr1->meth_ext = ptr0->meth_ext; ptr1->args = ptr0->args; ptr1->fib = 0; @@ -585,4 +601,16 @@ next_ii(VALUE i, VALUE obj, int argc, VA static VALUE +make_stopper(void) +{ + return rb_exc_new2(rb_eStopIteration, "iteration reached an end"); +} + +void +rb_stop_iteration(void) +{ + rb_exc_raise(make_stopper()); +} + +static VALUE next_i(VALUE curr, VALUE obj) { @@ -592,5 +620,5 @@ next_i(VALUE curr, VALUE obj) result = rb_block_call(obj, id_each, 0, 0, next_ii, obj); - e->stop_exc = rb_exc_new2(rb_eStopIteration, "iteration reached an end"); + e->stop_exc = make_stopper(); rb_ivar_set(e->stop_exc, rb_intern("result"), result); return rb_fiber_yield(1, &nil); @@ -611,4 +639,14 @@ get_next_values(VALUE obj, struct enumer VALUE curr, vs; + if (e->meth_ext) { + int argc = 0; + VALUE *argv = 0; + if (e->args) { + argc = RARRAY_LENINT(e->args); + argv = RARRAY_PTR(e->args); + } + return rb_funcall2(e->obj, e->meth_ext, argc, argv); + } + if (e->stop_exc) rb_exc_raise(e->stop_exc); Index: io.c =================================================================== --- io.c (revision 25098) +++ io.c (working copy) @@ -2617,4 +2617,17 @@ rb_io_each_line(int argc, VALUE *argv, V } +NORETURN(void rb_stop_iteration(void)); + +static VALUE +rb_io_external_iterator_for_each_line(int argc, VALUE *argv, VALUE io) +{ + VALUE line = rb_io_gets_m(argc, argv, io); + + if (NIL_P(line)) { + rb_stop_iteration(); + } + return line; +} + /* * call-seq: @@ -9704,4 +9717,6 @@ Init_IO(void) rb_define_method(rb_cIO, "each", rb_io_each_line, -1); rb_define_method(rb_cIO, "each_line", rb_io_each_line, -1); + rb_define_private_method(rb_cIO, "external_iterator_for_each", rb_io_external_iterator_for_each_line, -1); + rb_define_private_method(rb_cIO, "external_iterator_for_each_line", rb_io_external_iterator_for_each_line, -1); rb_define_method(rb_cIO, "each_byte", rb_io_each_byte, 0); rb_define_method(rb_cIO, "each_char", rb_io_each_char, 0);
-- Nobu Nakada