ruby-changes:4463
From: ko1@a...
Date: Thu, 10 Apr 2008 19:53:12 +0900 (JST)
Subject: [ruby-changes:4463] knu - Ruby:r15954 (ruby_1_8): * enumerator.c (rb_eStopIteration), eval.c (rb_f_loop), ruby.h:
knu 2008-04-10 19:52:50 +0900 (Thu, 10 Apr 2008) New Revision: 15954 Modified files: branches/ruby_1_8/ChangeLog branches/ruby_1_8/NEWS branches/ruby_1_8/enumerator.c branches/ruby_1_8/eval.c branches/ruby_1_8/lib/generator.rb branches/ruby_1_8/ruby.h Log: * enumerator.c (rb_eStopIteration), eval.c (rb_f_loop), ruby.h: Add a new exception class StopIteration, which breaks Kernel#loop iteration when raised; backported from 1.9. * enumerator.c (enumerator_next, enumerator_rewind): Implement #next and #rewind using the "generator" library. * lib/generator.rb: Implement Enumerable::Enumerator#next and #rewind. http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/lib/generator.rb?r1=15954&r2=15953&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ruby.h?r1=15954&r2=15953&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ChangeLog?r1=15954&r2=15953&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/enumerator.c?r1=15954&r2=15953&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/NEWS?r1=15954&r2=15953&diff_format=u http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/eval.c?r1=15954&r2=15953&diff_format=u Index: ruby_1_8/NEWS =================================================================== --- ruby_1_8/NEWS (revision 15953) +++ ruby_1_8/NEWS (revision 15954) @@ -61,10 +61,16 @@ * Regexp.union accepts an array of patterns. + * StopIteration + + New exception class that causes Kernel#loop to stop iteration when + raised. + * enumerator - * Enumerator is now a built-in module. Almost everything has been - backported from 1.9, except for the #next and #rewind methods. + * Enumerator is now a built-in module. The #next and #rewind + methods are implemented using the "generator" library. Use with + care and be aware of the performance loss. * ipaddr Index: ruby_1_8/ChangeLog =================================================================== --- ruby_1_8/ChangeLog (revision 15953) +++ ruby_1_8/ChangeLog (revision 15954) @@ -1,3 +1,15 @@ +Thu Apr 10 19:49:10 2008 Akinori MUSHA <knu@i...> + + * enumerator.c (rb_eStopIteration), eval.c (rb_f_loop), ruby.h: + Add a new exception class StopIteration, which breaks Kernel#loop + iteration when raised; backported from 1.9. + + * enumerator.c (enumerator_next, enumerator_rewind): Implement + #next and #rewind using the "generator" library. + + * lib/generator.rb: Implement Enumerable::Enumerator#next and + #rewind. + Thu Apr 10 19:29:48 2008 Akinori MUSHA <knu@i...> * array.c (rb_ary_first, rb_ary_last): Return a shared array when Index: ruby_1_8/enumerator.c =================================================================== --- ruby_1_8/enumerator.c (revision 15953) +++ ruby_1_8/enumerator.c (revision 15954) @@ -23,6 +23,8 @@ VALUE rb_cEnumerator; static VALUE sym_each, sym_call; +VALUE rb_eStopIteration; + static VALUE proc_call(proc, args) VALUE proc; @@ -387,6 +389,45 @@ enumerator_with_index_i, (VALUE)&memo); } +/* + * call-seq: + * e.next => object + * + * Returns the next object in the enumerator, and move the internal + * position forward. When the position reached at the end, internal + * position is rewinded then StopIteration is raised. + * + * Note that enumeration sequence by next method does not affect other + * non-external enumeration methods, unless underlying iteration + * methods itself has side-effect, e.g. IO#each_line. + * + * Caution: Calling this method causes the "generator" library to be + * loaded. + */ + +static VALUE +enumerator_next(obj) + VALUE obj; +{ + rb_require("generator"); + return rb_funcall(obj, rb_intern("next"), 0, 0); +} + +/* + * call-seq: + * e.rewind => e + * + * Rewinds the enumeration sequence by the next method. + */ + +static VALUE +enumerator_rewind(obj) + VALUE obj; +{ + rb_require("generator"); + return rb_funcall(obj, rb_intern("rewind"), 0, 0); +} + void Init_Enumerator() { @@ -406,7 +447,11 @@ rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1); rb_define_method(rb_cEnumerator, "each", enumerator_each, 0); rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0); + rb_define_method(rb_cEnumerator, "next", enumerator_next, 0); + rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); + rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError); + sym_each = ID2SYM(rb_intern("each")); sym_call = ID2SYM(rb_intern("call")); Index: ruby_1_8/lib/generator.rb =================================================================== --- ruby_1_8/lib/generator.rb (revision 15953) +++ ruby_1_8/lib/generator.rb (revision 15954) @@ -165,6 +165,44 @@ end end +class Enumerable::Enumerator + def __generator + @generator ||= Generator.new(self) + end + private :__generator + + # call-seq: + # e.next => object + # + # Returns the next object in the enumerator, and move the internal + # position forward. When the position reached at the end, internal + # position is rewinded then StopIteration is raised. + # + # Note that enumeration sequence by next method does not affect other + # non-external enumeration methods, unless underlying iteration + # methods itself has side-effect, e.g. IO#each_line. + # + # Caution: This feature internally uses Generator, which uses callcc + # to stop and resume enumeration to fetch each value. Use with care + # and be aware of the performance loss. + def next + g = __generator + return g.next unless g.end? + + g.rewind + raise StopIteration, 'iteration reached at end' + end + + # call-seq: + # e.rewind => e + # + # Rewinds the enumeration sequence by the next method. + def rewind + __generator.rewind + self + end +end + # # SyncEnumerator creates an Enumerable object from multiple Enumerable # objects and enumerates them synchronously. Index: ruby_1_8/ruby.h =================================================================== --- ruby_1_8/ruby.h (revision 15953) +++ ruby_1_8/ruby.h (revision 15954) @@ -659,6 +659,7 @@ RUBY_EXTERN VALUE rb_eArgError; RUBY_EXTERN VALUE rb_eEOFError; RUBY_EXTERN VALUE rb_eIndexError; +RUBY_EXTERN VALUE rb_eStopIteration; RUBY_EXTERN VALUE rb_eRangeError; RUBY_EXTERN VALUE rb_eIOError; RUBY_EXTERN VALUE rb_eRuntimeError; Index: ruby_1_8/eval.c =================================================================== --- ruby_1_8/eval.c (revision 15953) +++ ruby_1_8/eval.c (revision 15954) @@ -5157,6 +5157,16 @@ return rb_yield_0(values, 0, 0, 0, avalue); } +static VALUE +loop_i() +{ + for (;;) { + rb_yield_0(Qundef, 0, 0, 0, Qfalse); + CHECK_INTS; + } + return Qnil; +} + /* * call-seq: * loop {|| block } @@ -5169,15 +5179,14 @@ * break if !line or line =~ /^qQ/ * # ... * end + * + * StopIteration raised in the block breaks the loop. */ static VALUE rb_f_loop() { - for (;;) { - rb_yield_0(Qundef, 0, 0, 0, Qfalse); - CHECK_INTS; - } + rb_rescue2(loop_i, (VALUE)0, 0, 0, rb_eStopIteration, (VALUE)0); return Qnil; /* dummy */ } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/