StopIteration#result を新設するのはどうでしょう? 以下の例で、StopIteration#result は、(ブロックつきの) Array#each の返り値を返します。 a = [1,2,3] e = a.enum_for(:each) p e.next #=> 1 p e.next #=> 2 p e.next #=> 3 e.next rescue p $!.result #=> [1, 2, 3] Array#each はその配列自体を返すので、$!.result は [1, 2, 3] になっています。 Enumerator で外部イテレータを作って使うと、内部イテレータを Fiber 内で動かします。each などの内部イテレータは終了すると きに何か値を返すわけですが、現在はその値を得る方法がありませ ん。 まぁ、直接何かに役に立つかというと疑わしい気がしますが、外部 イテレータを使って内部イテレータを実装するときに後者の返り値 を前者から指定できるとか、ちょっとメタなことをやろうとしたと きに使える気がします。 StopIteration#result は外部イテレータと内部イテレータの機能 のずれを除去する、ともいえます。完全性というか。 というわけでどうでしょうか。 % svn diff --diff-cmd diff -x '-u -p' Index: enumerator.c =================================================================== --- enumerator.c (revision 24578) +++ enumerator.c (working copy) @@ -33,7 +33,7 @@ struct enumerator { VALUE fib; VALUE dst; VALUE lookahead; - VALUE no_next; + VALUE stop_exc; }; static VALUE rb_cGenerator, rb_cYielder; @@ -284,7 +284,7 @@ enumerator_init(VALUE enum_obj, VALUE ob ptr->fib = 0; ptr->dst = Qnil; ptr->lookahead = Qundef; - ptr->no_next = Qfalse; + ptr->stop_exc = Qfalse; return enum_obj; } @@ -506,14 +506,23 @@ next_ii(VALUE i, VALUE obj, int argc, VA return Qnil; } + +static VALUE +stop_result(VALUE self) +{ + return rb_attr_get(self, rb_intern("result")); +} + static VALUE next_i(VALUE curr, VALUE obj) { struct enumerator *e = enumerator_ptr(obj); VALUE nil = Qnil; + VALUE result; - rb_block_call(obj, id_each, 0, 0, next_ii, obj); - e->no_next = Qtrue; + result = rb_block_call(obj, id_each, 0, 0, next_ii, obj); + e->stop_exc = rb_exc_new2(rb_eStopIteration, "iteration reached at end"); + rb_ivar_set(e->stop_exc, rb_intern("result"), result); return rb_fiber_yield(1, &nil); } @@ -552,8 +561,8 @@ enumerator_next(VALUE obj) return v; } - if (e->no_next) - rb_raise(rb_eStopIteration, "iteration reached at end"); + if (e->stop_exc) + rb_exc_raise(e->stop_exc); curr = rb_fiber_current(); @@ -562,11 +571,11 @@ enumerator_next(VALUE obj) } v = rb_fiber_resume(e->fib, 1, &curr); - if (e->no_next) { + if (e->stop_exc) { e->fib = 0; e->dst = Qnil; e->lookahead = Qundef; - rb_raise(rb_eStopIteration, "iteration reached at end"); + rb_exc_raise(e->stop_exc); } return v; } @@ -617,7 +626,7 @@ enumerator_rewind(VALUE obj) e->fib = 0; e->dst = Qnil; e->lookahead = Qundef; - e->no_next = Qfalse; + e->stop_exc = Qfalse; return obj; } @@ -915,6 +924,7 @@ Init_Enumerator(void) rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0); rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError); + rb_define_method(rb_eStopIteration, "result", stop_result, 0); /* Generator */ rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject); Index: test/ruby/test_enumerator.rb =================================================================== --- test/ruby/test_enumerator.rb (revision 24578) +++ test/ruby/test_enumerator.rb (working copy) @@ -152,5 +152,14 @@ class TestEnumerator < Test::Unit::TestC assert_raise(StopIteration) { e.next } assert_raise(StopIteration) { e.next } end + + def test_stop_result + a = [1] + res = a.each {} + e = a.each + assert_equal(1, e.next) + exc = assert_raise(StopIteration) { e.next } + assert_equal(res, exc.result) + end end -- [田中 哲][たなか あきら][Tanaka Akira]