From: Yusuke ENDOH Date: 2007-09-25T02:09:33+09:00 Subject: [ruby-dev:31847] Re: generator from Enumerable#map 遠藤です。 > 1.9 では、Enumerable#map や Enumerable#index をブロックなしで呼ぶと > generator を返すようですが、これは Object#to_enum と何か異なるの > でしょうか。 情けないことにまだわかってません。気になるので教えていただけません でしょうか。 > Enumerator で yield の返り値相当などを指定できるなら意味がありそうです。 > 例えば Enumerator#value= と #result を入れてこんな風に。 > > g = [1, 2, 3].map > loop { x = g.next; g.value = x * 2 } > p g.result #=> [2, 4, 6] > > > g = [:a, :b, :c].index > loop { x = g.next; g.value = x == :b } > p g.result #=> 1 この拡張が特別に欲しいわけではないのですが、外部イテレータを fiber の 代用とするためには無いと不便なので、試しに実装してみました。 # Fiber#resume の引数 (= Fiber.yield の返値) を指定できないので。 メソッド名やインターフェイスはもっといいのがありそうです。 Index: enumerator.c =================================================================== --- enumerator.c (revision 13509) +++ enumerator.c (working copy) @@ -42,6 +42,8 @@ VALUE method; VALUE proc; VALUE args; + VALUE value; + VALUE result; enum_iter *iter; VALUE fib; VALUE dst; @@ -55,6 +57,8 @@ rb_gc_mark(ptr->method); rb_gc_mark(ptr->proc); rb_gc_mark(ptr->args); + rb_gc_mark(ptr->value); + rb_gc_mark(ptr->result); rb_gc_mark(ptr->fib); rb_gc_mark(ptr->dst); } @@ -238,6 +242,8 @@ ptr->iter = (enum_iter *)rb_yield; } if (argc) ptr->args = rb_ary_new4(argc, argv); + ptr->value = Qnil; + ptr->result = Qnil; ptr->fib = 0; ptr->dst = Qnil; ptr->no_next = Qfalse; @@ -286,6 +292,8 @@ ptr1->proc = ptr0->proc; ptr1->iter = ptr0->iter; ptr1->args = ptr0->args; + ptr1->value = ptr0->value; + ptr1->result = ptr0->result; ptr1->fib = ptr0->fib; return obj; @@ -371,8 +379,9 @@ next_ii(VALUE i, VALUE obj) { struct enumerator *e = enumerator_ptr(obj); + e->value = Qnil; rb_fiber_yield(1, &i); - return Qnil; + return e->value; } static VALUE @@ -381,7 +390,7 @@ struct enumerator *e = enumerator_ptr(obj); VALUE nil = Qnil; - rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj); + e->result = rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj); e->no_next = Qtrue; return rb_fiber_yield(1, &nil); } @@ -431,6 +440,36 @@ /* * call-seq: + * e.value = v => v + * + * Sets the value that current block will return. + */ + +static VALUE +enumerator_set_value(VALUE obj, VALUE value) { + struct enumerator *e = enumerator_ptr(obj); + + e->value = value; + + return value; +} + +/* + * call-seq: + * e.result => object + * + * Returns the value that the iteration method returned. + */ + +static VALUE +enumerator_result(VALUE obj) { + struct enumerator *e = enumerator_ptr(obj); + + return e->result; +} + +/* + * call-seq: * e.next? => e * * Rewinds the enumeration sequence by the next method. @@ -467,6 +506,8 @@ rb_define_method(rb_cEnumerator, "to_splat", enumerator_to_splat, 0); rb_define_method(rb_cEnumerator, "next", enumerator_next, 0); rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); + rb_define_method(rb_cEnumerator, "value=", enumerator_set_value, 1); + rb_define_method(rb_cEnumerator, "result", enumerator_result, 0); rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError); -- Yusuke ENDOH