1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* 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.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12905 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
matz 2007-08-08 07:07:03 +00:00
parent cc317d9208
commit c186fdb90b
5 changed files with 68 additions and 14 deletions

View file

@ -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.

2
enum.c
View file

@ -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;
}

View file

@ -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"));

View file

@ -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;

View file

@ -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