summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-10 10:52:50 +0000
committerknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-10 10:52:50 +0000
commitbadb86556777cc16d11beb47b38b25997d7aea75 (patch)
tree3b3e7fe955fd3c737f763d708be8b49d7755cb85
parent8202fc3bc37338e2ee4ac2e860b78b1a555f428e (diff)
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@15954 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog12
-rw-r--r--NEWS10
-rw-r--r--enumerator.c45
-rw-r--r--eval.c17
-rw-r--r--lib/generator.rb38
-rw-r--r--ruby.h1
6 files changed, 117 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index 78e4b4e3bb..5a1cee9d81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+Thu Apr 10 19:49:10 2008 Akinori MUSHA <knu@iDaemons.org>
+
+ * 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@iDaemons.org>
* array.c (rb_ary_first, rb_ary_last): Return a shared array when
diff --git a/NEWS b/NEWS
index f91c4b1efb..bd3e1f6e61 100644
--- a/NEWS
+++ b/NEWS
@@ -61,10 +61,16 @@ with all sufficient information, see the ChangeLog file.
* 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
diff --git a/enumerator.c b/enumerator.c
index e5bf538f0c..fefbc28b34 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -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(obj)
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,6 +447,10 @@ Init_Enumerator()
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"));
diff --git a/eval.c b/eval.c
index 8972f3bd72..5a03eccc91 100644
--- a/eval.c
+++ b/eval.c
@@ -5157,6 +5157,16 @@ rb_yield_splat(values)
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 @@ rb_yield_splat(values)
* 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 */
}
diff --git a/lib/generator.rb b/lib/generator.rb
index a010559b60..dbdd0f40a9 100644
--- a/lib/generator.rb
+++ b/lib/generator.rb
@@ -165,6 +165,44 @@ class Generator
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.
diff --git a/ruby.h b/ruby.h
index ac10e9d04d..95b4c4ca78 100644
--- a/ruby.h
+++ b/ruby.h
@@ -659,6 +659,7 @@ RUBY_EXTERN VALUE rb_eFatal;
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;