summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
-rw-r--r--enum.c2
-rw-r--r--enumerator.c44
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--test/ruby/test_iterator.rb19
5 files changed, 68 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 2bf1d4b32a..d838a68485 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+Wed Aug 8 15:52:01 2007 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * enumerator.c (enumerator_next_p): should check correctly even when
+ e.next has not been called before.
+
+ * enumerator.c (enumerator_next): raise StopIteration (name taken
+ from Python) instead of IndexError.
+
+ * enum.c (enum_zip): catch StopIteration exception.
+
+ * enumerator.c (enumerator_with_index): return Enumerator if no
+ block is given.
+
+ * test/ruby/test_iterator.rb (TestIterator::test_enumerator): add
+ test for enumerators.
+
Wed Aug 8 11:48:37 2007 Nobuyoshi Nakada <nobu@ruby-lang.org>
* bignum.c (rb_big2str0): should not use RTEST for non-VALUE.
diff --git a/enum.c b/enum.c
index fa58762463..d7f9f740c7 100644
--- a/enum.c
+++ b/enum.c
@@ -1411,7 +1411,7 @@ enum_zip(int argc, VALUE *argv, VALUE obj)
RETURN_ENUMERATOR(obj, argc, argv);
result = rb_block_given_p() ? Qnil : rb_ary_new();
memo = rb_node_newnode(NODE_MEMO, result, rb_ary_new4(argc, argv), obj);
- rb_rescue2(zip_b, (VALUE)memo, 0, 0, rb_eIndexError, (VALUE)0);
+ rb_rescue2(zip_b, (VALUE)memo, 0, 0, rb_eStopIteration, (VALUE)0);
return result;
}
diff --git a/enumerator.c b/enumerator.c
index 3260a809f4..766d13758c 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -23,6 +23,8 @@
static VALUE rb_cEnumerator;
static VALUE sym_each, sym_each_with_index, sym_each_slice, sym_each_cons;
+VALUE rb_eStopIteration;
+
static VALUE
proc_call(VALUE proc, VALUE args)
{
@@ -234,7 +236,7 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
}
if (argc) ptr->args = rb_ary_new4(argc, argv);
ptr->fib = 0;
- ptr->next = ptr->dst = Qundef;
+ ptr->next = ptr->dst = Qnil;
return enum_obj;
}
@@ -339,7 +341,7 @@ enumerator_with_index(VALUE obj)
int argc = 0;
VALUE *argv = 0;
-/* RETURN_ENUMERATOR(obj, 0, 0); ?? */
+ RETURN_ENUMERATOR(obj, 0, 0);
if (e->args) {
argc = RARRAY_LEN(e->args);
argv = RARRAY_PTR(e->args);
@@ -368,9 +370,9 @@ next_ii(VALUE i, VALUE obj)
VALUE tmp = e->next;
e->next = i;
- if (tmp != Qundef) {
- e->next = i;
- e->dst = rb_fiber_yield(e->dst, 1, &tmp);
+ tmp = rb_fiber_yield(e->dst, 1, &tmp);
+ if (tmp != Qnil) {
+ e->dst = tmp;
}
return Qnil;
}
@@ -385,13 +387,23 @@ next_i(VALUE curr, VALUE obj)
return e->next;
}
+static void
+next_init(VALUE obj, struct enumerator *e)
+{
+ VALUE curr = rb_fiber_current();
+
+ e->dst = curr;
+ e->fib = rb_block_call(rb_cFiber, rb_intern("new"), 0, 0, next_i, obj);
+ rb_fiber_yield(e->fib, 1, &curr);
+}
+
/*
* 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 IndexError is raised.
+ * 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
@@ -407,13 +419,12 @@ enumerator_next(VALUE obj)
curr = rb_fiber_current();
if (!e->fib) {
- e->dst = curr;
- e->fib = rb_block_call(rb_cFiber, rb_intern("new"), 0, 0, next_i, obj);
+ next_init(obj, e);
}
- else if (!rb_fiber_alive_p(e->fib)) {
+ if (!rb_fiber_alive_p(e->fib)) {
e->fib = 0;
- e->next = e->dst = Qundef;
- rb_raise(rb_eIndexError, "Enumerator#each reached at end");
+ e->next = e->dst = Qnil;
+ rb_raise(rb_eStopIteration, "Enumerator#each reached at end");
}
v = rb_fiber_yield(e->fib, 1, &curr);
return v;
@@ -429,7 +440,12 @@ enumerator_next(VALUE obj)
static VALUE
enumerator_next_p(VALUE obj)
{
- return rb_fiber_alive_p(enumerator_ptr(obj)->fib);
+ struct enumerator *e = enumerator_ptr(obj);
+
+ if (!e->fib) {
+ next_init(obj, e);
+ }
+ return rb_fiber_alive_p(e->fib);
}
/*
@@ -445,7 +461,7 @@ enumerator_rewind(VALUE obj)
struct enumerator *e = enumerator_ptr(obj);
e->fib = 0;
- e->next = e->dst = Qundef;
+ e->next = e->dst = Qnil;
return obj;
}
@@ -471,6 +487,8 @@ Init_Enumerator(void)
rb_define_method(rb_cEnumerator, "next?", enumerator_next_p, 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_each_with_index = ID2SYM(rb_intern("each_with_index"));
sym_each_slice = ID2SYM(rb_intern("each_slice"));
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 905ff7d4a9..acb101a02d 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -798,6 +798,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_eKeyError;
RUBY_EXTERN VALUE rb_eRangeError;
RUBY_EXTERN VALUE rb_eIOError;
diff --git a/test/ruby/test_iterator.rb b/test/ruby/test_iterator.rb
index e638237135..45f1452629 100644
--- a/test/ruby/test_iterator.rb
+++ b/test/ruby/test_iterator.rb
@@ -488,4 +488,23 @@ class TestIterator < Test::Unit::TestCase
def test_block_given_within_iterator
assert_equal(["b"], ["a", "b", "c"].grep(IterString.new("b")) {|s| s})
end
+
+ def test_enumerator
+ [1,2,3].each.with_index {|x,i|
+ assert_equal(x, i+1)
+ }
+
+ e = [1,2,3].each
+ assert_equal(1, e.next)
+ assert_equal(true, e.next?)
+ assert_equal(2, e.next)
+ assert_equal(3, e.next)
+ assert_raises(StopIteration){e.next}
+ e.rewind
+ assert_equal(true, e.next?)
+ assert_equal(1, e.next)
+
+ assert_equal([[1, 8, 10], [2, 6, 11], [3, 4, 12]],
+ (1..10).zip([8,6,4],(10..100)).to_a)
+ end
end