mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* enumerator.c: implement Enumerator#{next_values,peek_values,feed}
and StopIteration#result. [ruby-dev:39109] (struct enumerator): replace no_next by stop_exc. new field feedvalue. (enumerator_mark): mark feedvalue and stop_exc. (enumerator_init): initialize feedvalue and stop_exc. (enumerator_init_copy): initialize feedvalue. (next_ii): send yield arguments as an array. return feedvalue. (next_i): generate StopIteration exception here. set result. (next_init): initialize feedvalue. (enumerator_next_values): new method Enumerator#next_values. (ary2sv): new function. (enumerator_peek_values): new method Enumerator#peek_values. (enumerator_feed): new method Enumerator#feed. (yielder_yield): return the yield value. (generator_each): return the iterator value. (stop_result): new method StopIteration#result. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@24587 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
b307bd9847
commit
3b4949ffa8
4 changed files with 435 additions and 38 deletions
20
ChangeLog
20
ChangeLog
|
@ -1,3 +1,23 @@
|
||||||
|
Thu Aug 20 01:28:42 2009 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
|
* enumerator.c: implement Enumerator#{next_values,peek_values,feed}
|
||||||
|
and StopIteration#result. [ruby-dev:39109]
|
||||||
|
(struct enumerator): replace no_next by stop_exc.
|
||||||
|
new field feedvalue.
|
||||||
|
(enumerator_mark): mark feedvalue and stop_exc.
|
||||||
|
(enumerator_init): initialize feedvalue and stop_exc.
|
||||||
|
(enumerator_init_copy): initialize feedvalue.
|
||||||
|
(next_ii): send yield arguments as an array. return feedvalue.
|
||||||
|
(next_i): generate StopIteration exception here. set result.
|
||||||
|
(next_init): initialize feedvalue.
|
||||||
|
(enumerator_next_values): new method Enumerator#next_values.
|
||||||
|
(ary2sv): new function.
|
||||||
|
(enumerator_peek_values): new method Enumerator#peek_values.
|
||||||
|
(enumerator_feed): new method Enumerator#feed.
|
||||||
|
(yielder_yield): return the yield value.
|
||||||
|
(generator_each): return the iterator value.
|
||||||
|
(stop_result): new method StopIteration#result.
|
||||||
|
|
||||||
Thu Aug 20 01:06:48 2009 Yukihiro Matsumoto <matz@ruby-lang.org>
|
Thu Aug 20 01:06:48 2009 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||||
|
|
||||||
* dir.c (DEFINE_STRUCT_DIRENT): use union to allocate sufficient
|
* dir.c (DEFINE_STRUCT_DIRENT): use union to allocate sufficient
|
||||||
|
|
9
NEWS
9
NEWS
|
@ -8,7 +8,6 @@ reference information is supplied with. For a full list of changes
|
||||||
with all sufficient information, see the ChangeLog file.
|
with all sufficient information, see the ChangeLog file.
|
||||||
|
|
||||||
== Changes since the 1.9.1 release
|
== Changes since the 1.9.1 release
|
||||||
|
|
||||||
=== Library updates (outstanding ones only)
|
=== Library updates (outstanding ones only)
|
||||||
|
|
||||||
* builtin classes
|
* builtin classes
|
||||||
|
@ -22,6 +21,13 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* Dir.home
|
* Dir.home
|
||||||
|
|
||||||
* Enumerator
|
* Enumerator
|
||||||
|
* new methods:
|
||||||
|
* Enumerator#peek
|
||||||
|
* Enumerator#next_values
|
||||||
|
* Enumerator#peek_values
|
||||||
|
* Enumerator#feed
|
||||||
|
* StopIteration#result
|
||||||
|
|
||||||
* extended methods:
|
* extended methods:
|
||||||
* #with_index accepts an optional argument that specifies the
|
* #with_index accepts an optional argument that specifies the
|
||||||
index number to start with, defaulted to 0.
|
index number to start with, defaulted to 0.
|
||||||
|
@ -29,6 +35,7 @@ with all sufficient information, see the ChangeLog file.
|
||||||
* incompatible changes:
|
* incompatible changes:
|
||||||
* #rewind now calls the "rewind" method of the enclosed object
|
* #rewind now calls the "rewind" method of the enclosed object
|
||||||
if defined.
|
if defined.
|
||||||
|
* #next doesn't clear the position at end.
|
||||||
|
|
||||||
* IO
|
* IO
|
||||||
* new method:
|
* new method:
|
||||||
|
|
319
enumerator.c
319
enumerator.c
|
@ -19,6 +19,60 @@
|
||||||
*
|
*
|
||||||
* A class which provides a method `each' to be used as an Enumerable
|
* A class which provides a method `each' to be used as an Enumerable
|
||||||
* object.
|
* object.
|
||||||
|
*
|
||||||
|
* An enumerator can be created by following methods.
|
||||||
|
* - Kernel#to_enum
|
||||||
|
* - Kernel#enum_for
|
||||||
|
* - Enumerator.new
|
||||||
|
*
|
||||||
|
* Also, most iteration methods without a block returns an enumerator.
|
||||||
|
* For example, Array#map returns an enumerator if no block given.
|
||||||
|
* The enumerator has with_index.
|
||||||
|
* So ary.map.with_index works as follows.
|
||||||
|
*
|
||||||
|
* p [1,2,3].map.with_index {|o, i| o+i } #=> [1, 3, 5]
|
||||||
|
*
|
||||||
|
* An enumerator object can be used as an external iterator.
|
||||||
|
* I.e. Enumerator#next returns the next value of the iterator.
|
||||||
|
* Enumerator#next raises StopIteration at end.
|
||||||
|
*
|
||||||
|
* e = [1,2,3].each # enumerator object.
|
||||||
|
* p e.next #=> 1
|
||||||
|
* p e.next #=> 2
|
||||||
|
* p e.next #=> 3
|
||||||
|
* p e.next #raises StopIteration
|
||||||
|
*
|
||||||
|
* An external iterator can be used to implement an internal iterator as follows.
|
||||||
|
*
|
||||||
|
* def ext_each(e)
|
||||||
|
* while true
|
||||||
|
* begin
|
||||||
|
* vs = e.next_values
|
||||||
|
* rescue StopIteration
|
||||||
|
* return $!.result
|
||||||
|
* end
|
||||||
|
* y = yield *vs
|
||||||
|
* e.feed y
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* o = Object.new
|
||||||
|
* def o.each
|
||||||
|
* p yield
|
||||||
|
* p yield 1
|
||||||
|
* p yield(1, 2)
|
||||||
|
* 3
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* # use o.each as an internal iterator directly.
|
||||||
|
* p o.each {|*x| p x; [:b, *x] }
|
||||||
|
* #=> [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
|
||||||
|
*
|
||||||
|
* # convert o.each to an external external iterator for
|
||||||
|
* # implementing an internal iterator.
|
||||||
|
* p ext_each(o.to_enum) {|*x| p x; [:b, *x] }
|
||||||
|
* #=> [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
VALUE rb_cEnumerator;
|
VALUE rb_cEnumerator;
|
||||||
static ID id_rewind, id_each;
|
static ID id_rewind, id_each;
|
||||||
|
@ -33,7 +87,8 @@ struct enumerator {
|
||||||
VALUE fib;
|
VALUE fib;
|
||||||
VALUE dst;
|
VALUE dst;
|
||||||
VALUE lookahead;
|
VALUE lookahead;
|
||||||
VALUE no_next;
|
VALUE feedvalue;
|
||||||
|
VALUE stop_exc;
|
||||||
};
|
};
|
||||||
|
|
||||||
static VALUE rb_cGenerator, rb_cYielder;
|
static VALUE rb_cGenerator, rb_cYielder;
|
||||||
|
@ -61,6 +116,8 @@ enumerator_mark(void *p)
|
||||||
rb_gc_mark(ptr->fib);
|
rb_gc_mark(ptr->fib);
|
||||||
rb_gc_mark(ptr->dst);
|
rb_gc_mark(ptr->dst);
|
||||||
rb_gc_mark(ptr->lookahead);
|
rb_gc_mark(ptr->lookahead);
|
||||||
|
rb_gc_mark(ptr->feedvalue);
|
||||||
|
rb_gc_mark(ptr->stop_exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct enumerator *
|
static struct enumerator *
|
||||||
|
@ -284,7 +341,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
|
||||||
ptr->fib = 0;
|
ptr->fib = 0;
|
||||||
ptr->dst = Qnil;
|
ptr->dst = Qnil;
|
||||||
ptr->lookahead = Qundef;
|
ptr->lookahead = Qundef;
|
||||||
ptr->no_next = Qfalse;
|
ptr->feedvalue = Qundef;
|
||||||
|
ptr->stop_exc = Qfalse;
|
||||||
|
|
||||||
return enum_obj;
|
return enum_obj;
|
||||||
}
|
}
|
||||||
|
@ -365,6 +423,7 @@ enumerator_init_copy(VALUE obj, VALUE orig)
|
||||||
ptr1->args = ptr0->args;
|
ptr1->args = ptr0->args;
|
||||||
ptr1->fib = 0;
|
ptr1->fib = 0;
|
||||||
ptr1->lookahead = Qundef;
|
ptr1->lookahead = Qundef;
|
||||||
|
ptr1->feedvalue = Qundef;
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
@ -502,8 +561,15 @@ enumerator_with_object(VALUE obj, VALUE memo)
|
||||||
static VALUE
|
static VALUE
|
||||||
next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
|
next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
|
||||||
{
|
{
|
||||||
rb_fiber_yield(argc, argv);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
return Qnil;
|
VALUE feedvalue = Qnil;
|
||||||
|
VALUE args = rb_ary_new4(argc, argv);
|
||||||
|
rb_fiber_yield(1, &args);
|
||||||
|
if (e->feedvalue != Qundef) {
|
||||||
|
feedvalue = e->feedvalue;
|
||||||
|
e->feedvalue = Qundef;
|
||||||
|
}
|
||||||
|
return feedvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -511,9 +577,11 @@ next_i(VALUE curr, VALUE obj)
|
||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
VALUE nil = Qnil;
|
VALUE nil = Qnil;
|
||||||
|
VALUE result;
|
||||||
|
|
||||||
rb_block_call(obj, id_each, 0, 0, next_ii, obj);
|
result = rb_block_call(obj, id_each, 0, 0, next_ii, obj);
|
||||||
e->no_next = Qtrue;
|
e->stop_exc = rb_exc_new2(rb_eStopIteration, "iteration reached at end");
|
||||||
|
rb_ivar_set(e->stop_exc, rb_intern("result"), result);
|
||||||
return rb_fiber_yield(1, &nil);
|
return rb_fiber_yield(1, &nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,6 +592,102 @@ next_init(VALUE obj, struct enumerator *e)
|
||||||
e->dst = curr;
|
e->dst = curr;
|
||||||
e->fib = rb_fiber_new(next_i, obj);
|
e->fib = rb_fiber_new(next_i, obj);
|
||||||
e->lookahead = Qundef;
|
e->lookahead = Qundef;
|
||||||
|
e->feedvalue = Qundef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* e.next_values => array
|
||||||
|
*
|
||||||
|
* Returns the next object as an array in the enumerator,
|
||||||
|
* and move the internal position forward.
|
||||||
|
* When the position reached at the end, StopIteration is raised.
|
||||||
|
*
|
||||||
|
* This method can be used to distinguish <code>yield</code> and <code>yield nil</code>.
|
||||||
|
*
|
||||||
|
* o = Object.new
|
||||||
|
* def o.each
|
||||||
|
* yield
|
||||||
|
* yield 1
|
||||||
|
* yield 1, 2
|
||||||
|
* yield nil
|
||||||
|
* yield [1, 2]
|
||||||
|
* end
|
||||||
|
* e = o.to_enum
|
||||||
|
* p e.next_values
|
||||||
|
* p e.next_values
|
||||||
|
* p e.next_values
|
||||||
|
* p e.next_values
|
||||||
|
* p e.next_values
|
||||||
|
* e = o.to_enum
|
||||||
|
* p e.next
|
||||||
|
* p e.next
|
||||||
|
* p e.next
|
||||||
|
* p e.next
|
||||||
|
* p e.next
|
||||||
|
*
|
||||||
|
* # result
|
||||||
|
* # next_values next
|
||||||
|
* # [] nil
|
||||||
|
* # [1] 1
|
||||||
|
* # [1, 2] [1, 2]
|
||||||
|
* # [nil] nil
|
||||||
|
* # [[1, 2]] [1, 2]
|
||||||
|
*
|
||||||
|
* Note that enumeration sequence by next_values method does not affect other
|
||||||
|
* non-external enumeration methods, unless underlying iteration
|
||||||
|
* methods itself has side-effect, e.g. IO#each_line.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
enumerator_next_values(VALUE obj)
|
||||||
|
{
|
||||||
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
|
VALUE curr, v;
|
||||||
|
|
||||||
|
if (e->lookahead != Qundef) {
|
||||||
|
v = e->lookahead;
|
||||||
|
e->lookahead = Qundef;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->stop_exc)
|
||||||
|
rb_exc_raise(e->stop_exc);
|
||||||
|
|
||||||
|
curr = rb_fiber_current();
|
||||||
|
|
||||||
|
if (!e->fib || !rb_fiber_alive_p(e->fib)) {
|
||||||
|
next_init(obj, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
v = rb_fiber_resume(e->fib, 1, &curr);
|
||||||
|
if (e->stop_exc) {
|
||||||
|
e->fib = 0;
|
||||||
|
e->dst = Qnil;
|
||||||
|
e->lookahead = Qundef;
|
||||||
|
e->feedvalue = Qundef;
|
||||||
|
rb_exc_raise(e->stop_exc);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
ary2sv(VALUE args)
|
||||||
|
{
|
||||||
|
if (TYPE(args) != T_ARRAY)
|
||||||
|
return args;
|
||||||
|
|
||||||
|
switch (RARRAY_LEN(args)) {
|
||||||
|
case 0:
|
||||||
|
return Qnil;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return RARRAY_PTR(args)[0];
|
||||||
|
|
||||||
|
default:
|
||||||
|
return args;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -542,32 +706,49 @@ next_init(VALUE obj, struct enumerator *e)
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
enumerator_next(VALUE obj)
|
enumerator_next(VALUE obj)
|
||||||
|
{
|
||||||
|
VALUE vs = enumerator_next_values(obj);
|
||||||
|
return ary2sv(vs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* e.peek_values => array
|
||||||
|
*
|
||||||
|
* Returns the next object as an array in the enumerator,
|
||||||
|
* but don't move the internal position forward.
|
||||||
|
* When the position reached at the end, StopIteration is raised.
|
||||||
|
*
|
||||||
|
* o = Object.new
|
||||||
|
* def o.each
|
||||||
|
* yield
|
||||||
|
* yield 1
|
||||||
|
* yield 1, 2
|
||||||
|
* end
|
||||||
|
* e = o.to_enum
|
||||||
|
* p e.peek_values #=> []
|
||||||
|
* e.next
|
||||||
|
* p e.peek_values #=> [1]
|
||||||
|
* e.next
|
||||||
|
* p e.peek_values #=> [1, 2]
|
||||||
|
* e.next
|
||||||
|
* p e.peek_values # raises StopIteration
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
enumerator_peek_values(VALUE obj)
|
||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
VALUE curr, v;
|
VALUE v;
|
||||||
|
|
||||||
if (e->lookahead != Qundef) {
|
if (e->lookahead != Qundef) {
|
||||||
v = e->lookahead;
|
v = e->lookahead;
|
||||||
e->lookahead = Qundef;
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e->no_next)
|
v = enumerator_next_values(obj);
|
||||||
rb_raise(rb_eStopIteration, "iteration reached at end");
|
e->lookahead = v;
|
||||||
|
|
||||||
curr = rb_fiber_current();
|
|
||||||
|
|
||||||
if (!e->fib || !rb_fiber_alive_p(e->fib)) {
|
|
||||||
next_init(obj, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
v = rb_fiber_resume(e->fib, 1, &curr);
|
|
||||||
if (e->no_next) {
|
|
||||||
e->fib = 0;
|
|
||||||
e->dst = Qnil;
|
|
||||||
e->lookahead = Qundef;
|
|
||||||
rb_raise(rb_eStopIteration, "iteration reached at end");
|
|
||||||
}
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,18 +764,46 @@ enumerator_next(VALUE obj)
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
enumerator_peek(VALUE obj)
|
enumerator_peek(VALUE obj)
|
||||||
|
{
|
||||||
|
VALUE vs = enumerator_peek_values(obj);
|
||||||
|
return ary2sv(vs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* e.feed obj => nil
|
||||||
|
*
|
||||||
|
* Set the value for the next yield in the enumerator returns.
|
||||||
|
*
|
||||||
|
* If the value is not set, yield returns nil.
|
||||||
|
*
|
||||||
|
* This value is cleared after used.
|
||||||
|
*
|
||||||
|
* o = Object.new
|
||||||
|
* def o.each
|
||||||
|
* p yield #=> 1
|
||||||
|
* p yield #=> nil
|
||||||
|
* p yield
|
||||||
|
* end
|
||||||
|
* e = o.to_enum
|
||||||
|
* e.next
|
||||||
|
* e.feed 1
|
||||||
|
* e.next
|
||||||
|
* e.next
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
enumerator_feed(VALUE obj, VALUE v)
|
||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
VALUE v;
|
|
||||||
|
|
||||||
if (e->lookahead != Qundef) {
|
if (e->feedvalue != Qundef) {
|
||||||
v = e->lookahead;
|
rb_raise(rb_eTypeError, "feed value already set");
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
e->feedvalue = v;
|
||||||
|
|
||||||
v = enumerator_next(obj);
|
return Qnil;
|
||||||
e->lookahead = v;
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -617,7 +826,8 @@ enumerator_rewind(VALUE obj)
|
||||||
e->fib = 0;
|
e->fib = 0;
|
||||||
e->dst = Qnil;
|
e->dst = Qnil;
|
||||||
e->lookahead = Qundef;
|
e->lookahead = Qundef;
|
||||||
e->no_next = Qfalse;
|
e->feedvalue = Qundef;
|
||||||
|
e->stop_exc = Qfalse;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,9 +964,7 @@ yielder_yield(VALUE obj, VALUE args)
|
||||||
{
|
{
|
||||||
struct yielder *ptr = yielder_ptr(obj);
|
struct yielder *ptr = yielder_ptr(obj);
|
||||||
|
|
||||||
rb_proc_call(ptr->proc, args);
|
return rb_proc_call(ptr->proc, args);
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
|
@ -883,9 +1091,42 @@ generator_each(VALUE obj)
|
||||||
|
|
||||||
yielder = yielder_new();
|
yielder = yielder_new();
|
||||||
|
|
||||||
rb_proc_call(ptr->proc, rb_ary_new3(1, yielder));
|
return rb_proc_call(ptr->proc, rb_ary_new3(1, yielder));
|
||||||
|
}
|
||||||
|
|
||||||
return obj;
|
/*
|
||||||
|
* StopIteration
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* stopiteration.result => value
|
||||||
|
*
|
||||||
|
* Returns the return value of the iterator.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* o = Object.new
|
||||||
|
* def o.each
|
||||||
|
* yield 1
|
||||||
|
* yield 2
|
||||||
|
* yield 3
|
||||||
|
* 100
|
||||||
|
* end
|
||||||
|
* e = o.to_enum
|
||||||
|
* p e.next #=> 1
|
||||||
|
* p e.next #=> 2
|
||||||
|
* p e.next #=> 3
|
||||||
|
* begin
|
||||||
|
* e.next
|
||||||
|
* rescue StopIteration
|
||||||
|
* p $!.result #=> 100
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
stop_result(VALUE self)
|
||||||
|
{
|
||||||
|
return rb_attr_get(self, rb_intern("result"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -909,12 +1150,16 @@ Init_Enumerator(void)
|
||||||
rb_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
|
rb_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
|
||||||
rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
|
rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
|
||||||
rb_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1);
|
rb_define_method(rb_cEnumerator, "with_object", enumerator_with_object, 1);
|
||||||
|
rb_define_method(rb_cEnumerator, "next_values", enumerator_next_values, 0);
|
||||||
|
rb_define_method(rb_cEnumerator, "peek_values", enumerator_peek_values, 0);
|
||||||
rb_define_method(rb_cEnumerator, "next", enumerator_next, 0);
|
rb_define_method(rb_cEnumerator, "next", enumerator_next, 0);
|
||||||
rb_define_method(rb_cEnumerator, "peek", enumerator_peek, 0);
|
rb_define_method(rb_cEnumerator, "peek", enumerator_peek, 0);
|
||||||
|
rb_define_method(rb_cEnumerator, "feed", enumerator_feed, 1);
|
||||||
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
|
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
|
||||||
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
|
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
|
||||||
|
|
||||||
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
|
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
|
||||||
|
rb_define_method(rb_eStopIteration, "result", stop_result, 0);
|
||||||
|
|
||||||
/* Generator */
|
/* Generator */
|
||||||
rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject);
|
rb_cGenerator = rb_define_class_under(rb_cEnumerator, "Generator", rb_cObject);
|
||||||
|
|
|
@ -152,5 +152,130 @@ class TestEnumerator < Test::Unit::TestCase
|
||||||
assert_raise(StopIteration) { e.next }
|
assert_raise(StopIteration) { e.next }
|
||||||
assert_raise(StopIteration) { e.next }
|
assert_raise(StopIteration) { e.next }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_stop_result
|
||||||
|
a = [1]
|
||||||
|
res = a.each {}
|
||||||
|
e = a.each
|
||||||
|
assert_equal(1, e.next)
|
||||||
|
exc = assert_raise(StopIteration) { e.next }
|
||||||
|
assert_equal(res, exc.result)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_next_values
|
||||||
|
o = Object.new
|
||||||
|
def o.each
|
||||||
|
yield
|
||||||
|
yield 1
|
||||||
|
yield 1, 2
|
||||||
|
end
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal(nil, e.next)
|
||||||
|
assert_equal(1, e.next)
|
||||||
|
assert_equal([1,2], e.next)
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal([], e.next_values)
|
||||||
|
assert_equal([1], e.next_values)
|
||||||
|
assert_equal([1,2], e.next_values)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_peek_values
|
||||||
|
o = Object.new
|
||||||
|
def o.each
|
||||||
|
yield
|
||||||
|
yield 1
|
||||||
|
yield 1, 2
|
||||||
|
end
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal(nil, e.peek)
|
||||||
|
assert_equal(nil, e.next)
|
||||||
|
assert_equal(1, e.peek)
|
||||||
|
assert_equal(1, e.next)
|
||||||
|
assert_equal([1,2], e.peek)
|
||||||
|
assert_equal([1,2], e.next)
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal([], e.peek_values)
|
||||||
|
assert_equal([], e.next_values)
|
||||||
|
assert_equal([1], e.peek_values)
|
||||||
|
assert_equal([1], e.next_values)
|
||||||
|
assert_equal([1,2], e.peek_values)
|
||||||
|
assert_equal([1,2], e.next_values)
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal([], e.peek_values)
|
||||||
|
assert_equal(nil, e.next)
|
||||||
|
assert_equal([1], e.peek_values)
|
||||||
|
assert_equal(1, e.next)
|
||||||
|
assert_equal([1,2], e.peek_values)
|
||||||
|
assert_equal([1,2], e.next)
|
||||||
|
e = o.to_enum
|
||||||
|
assert_equal(nil, e.peek)
|
||||||
|
assert_equal([], e.next_values)
|
||||||
|
assert_equal(1, e.peek)
|
||||||
|
assert_equal([1], e.next_values)
|
||||||
|
assert_equal([1,2], e.peek)
|
||||||
|
assert_equal([1,2], e.next_values)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_feed
|
||||||
|
o = Object.new
|
||||||
|
def o.each(ary)
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
end
|
||||||
|
ary = []
|
||||||
|
e = o.to_enum(:each, ary)
|
||||||
|
e.next
|
||||||
|
e.feed 1
|
||||||
|
e.next
|
||||||
|
e.feed 2
|
||||||
|
e.next
|
||||||
|
e.feed 3
|
||||||
|
assert_raise(StopIteration) { e.next }
|
||||||
|
assert_equal([1,2,3], ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_feed_mixed
|
||||||
|
o = Object.new
|
||||||
|
def o.each(ary)
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
end
|
||||||
|
ary = []
|
||||||
|
e = o.to_enum(:each, ary)
|
||||||
|
e.next
|
||||||
|
e.feed 1
|
||||||
|
e.next
|
||||||
|
e.next
|
||||||
|
e.feed 3
|
||||||
|
assert_raise(StopIteration) { e.next }
|
||||||
|
assert_equal([1,nil,3], ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_feed_twice
|
||||||
|
o = Object.new
|
||||||
|
def o.each(ary)
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
ary << yield
|
||||||
|
end
|
||||||
|
ary = []
|
||||||
|
e = o.to_enum(:each, ary)
|
||||||
|
e.feed 1
|
||||||
|
assert_raise(TypeError) { e.feed 2 }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_feed_yielder
|
||||||
|
x = nil
|
||||||
|
e = Enumerator.new {|y| x = y.yield; 10 }
|
||||||
|
e.next
|
||||||
|
e.feed 100
|
||||||
|
exc = assert_raise(StopIteration) { e.next }
|
||||||
|
assert_equal(100, x)
|
||||||
|
assert_equal(10, exc.result)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue