[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]