Project

General

Profile

Bug #6250 » lazy_enumerator.diff

gregolsen (Innokenty Mikhailov), 04/03/2012 05:52 PM

View differences:

enumerator.c
VALUE lookahead;
VALUE feedvalue;
VALUE stop_exc;
VALUE lazy;
VALUE procs;
};
static VALUE rb_cGenerator, rb_cYielder;
......
static VALUE generator_allocate(VALUE klass);
static VALUE generator_init(VALUE obj, VALUE proc);
static VALUE process_element(struct enumerator *e, VALUE result);
/*
* Enumerator
*/
......
}
/* Lazy Enumerator methods */
struct proc_entry {
VALUE proc;
VALUE type;
};
enum proc_entry_type {
T_PROC_MAP = 0,
T_PROC_SELECT = 1
};
static VALUE
lazy_init_iterator(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE result;
if (argc == 1) {
VALUE args[2];
args[0] = m;
args[1] = val;
result = rb_yield_values2(2, args);
process_element(struct enumerator *e, VALUE result)
{
struct proc_entry *entry;
VALUE move_next = Qtrue;
VALUE *procs = RARRAY_PTR(e->procs);
long procs_number = RARRAY_LENINT(e->procs);
long i = 0;
for (i = 0; i < procs_number; i++) {
Data_Get_Struct(procs[i], struct proc_entry, entry);
switch ((enum proc_entry_type) entry->type) {
case T_PROC_MAP:
result = rb_funcall(entry->proc, rb_intern("call"), 1, result);
break;
case T_PROC_SELECT:
if (RTEST(move_next)) {
move_next = rb_funcall(entry->proc, rb_intern("call"), 1, result);
}
break;
}
}
else {
VALUE args;
int len = rb_long2int((long)argc + 1);
args = rb_ary_tmp_new(len);
rb_ary_push(args, m);
if (argc > 0) {
rb_ary_cat(args, argv, argc);
}
result = rb_yield_values2(len, RARRAY_PTR(args));
RB_GC_GUARD(args);
if (RTEST(move_next)) {
return result;
} else {
return Qundef;
}
if (result == Qundef) rb_iter_break();
return Qnil;
}
static VALUE
lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
lazy_iterator_block(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE result;
result = rb_funcall2(m, id_yield, argc, argv);
if (result == Qundef) rb_iter_break();
return Qnil;
}
VALUE yielder = RARRAY_PTR(m)[0];
static VALUE
lazy_init_block_i(VALUE val, VALUE m, int argc, VALUE *argv)
{
rb_block_call(m, id_each, argc-1, argv+1, lazy_init_iterator, val);
return Qnil;
result = process_element(enumerator_ptr(RARRAY_PTR(m)[1]), val);
if (result != Qundef) {
return yielder_yield(yielder, rb_ary_new3(1, result));
} else {
return Qnil;
}
}
static VALUE
lazy_init_block(VALUE val, VALUE m, int argc, VALUE *argv)
lazy_generator_block(VALUE val, VALUE m, int argc, VALUE *argv)
{
rb_block_call(m, id_each, argc-1, argv+1, lazy_init_yielder, val);
return Qnil;
VALUE obj = RARRAY_PTR(m)[0];
VALUE enumerator = RARRAY_PTR(m)[1];
return rb_block_call(obj, id_each, 0, Qnil,
lazy_iterator_block, rb_ary_new3(2, val, enumerator));
}
static VALUE
lazy_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE obj, meth;
VALUE generator;
int offset;
VALUE obj;
struct enumerator *ptr;
if (argc < 1) {
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..)", argc);
}
else {
obj = argv[0];
if (argc == 1) {
meth = sym_each;
offset = 1;
}
else {
meth = argv[1];
offset = 2;
}
}
obj = argv[0];
generator = generator_allocate(rb_cGenerator);
rb_block_call(generator, id_initialize, 0, 0,
(rb_block_given_p() ? lazy_init_block_i : lazy_init_block),
obj);
enumerator_init(self, generator, meth, argc - offset, argv + offset);
rb_ivar_set(self, id_receiver, obj);
rb_block_call(generator, rb_intern("initialize"), 0, Qnil,
lazy_generator_block, rb_ary_new3(2, obj, self));
enumerator_init(self, generator, sym_each, 0, Qnil);
ptr = enumerator_ptr(self);
ptr->lazy = Qtrue;
ptr->procs = rb_ary_new();
return self;
}
static VALUE
lazy_set_method(VALUE lazy, VALUE args)
create_proc_entry(enum proc_entry_type proc_type)
{
ID id = rb_frame_this_func();
rb_ivar_set(lazy, id_method, ID2SYM(id));
if (NIL_P(args)) {
/* Qfalse indicates that the arguments are empty */
rb_ivar_set(lazy, id_arguments, Qfalse);
}
else {
rb_ivar_set(lazy, id_arguments, args);
}
return lazy;
}
struct proc_entry *entry;
VALUE entry_obj;
/*
* call-seq:
* e.lazy -> lazy_enumerator
*
* Returns a lazy enumerator, whose methods map/collect,
* flat_map/collect_concat, select/find_all, reject, grep, zip, take,
* take_while, drop, drop_while, and cycle enumerate values only on an
* as-needed basis. However, if a block is given to zip or cycle, values
* are enumerated immediately.
*
* === Example
*
* The following program finds pythagorean triples:
*
* def pythagorean_triples
* (1..Float::INFINITY).lazy.flat_map {|z|
* (1..z).flat_map {|x|
* (x..z).select {|y|
* x**2 + y**2 == z**2
* }.map {|y|
* [x, y, z]
* }
* }
* }
* end
* # show first ten pythagorean triples
* p pythagorean_triples.take(10).force # take is lazy, so force is needed
* p pythagorean_triples.first(10) # first is eager
* # show pythagorean triples less than 100
* p pythagorean_triples.take_while { |*, z| z < 100 }.force
*/
static VALUE
enumerable_lazy(VALUE obj)
{
VALUE result;
entry_obj = Data_Make_Struct(rb_cObject, struct proc_entry, 0, RUBY_DEFAULT_FREE, entry);
Data_Get_Struct(entry_obj, struct proc_entry, entry);
entry->proc = rb_block_proc();
entry->type = proc_type;
result = rb_class_new_instance(1, &obj, rb_cLazy);
/* Qfalse indicates that the Enumerator::Lazy has no method name */
rb_ivar_set(result, id_method, Qfalse);
return result;
}
static VALUE
lazy_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE result = rb_yield_values2(argc - 1, &argv[1]);
rb_funcall(argv[0], id_yield, 1, result);
return Qnil;
return entry_obj;
}
static VALUE
lazy_map(VALUE obj)
{
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "tried to call lazy map without a block");
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_map_func, 0),
Qnil);
}
static VALUE
lazy_flat_map_i(VALUE i, VALUE yielder, int argc, VALUE *argv)
{
return rb_funcall2(yielder, id_yield, argc, argv);
}
static VALUE
lazy_flat_map_each(VALUE obj)
{
NODE *memo = RNODE(obj);
rb_block_call(memo->u1.value, id_each, 0, 0, lazy_flat_map_i,
memo->u2.value);
return Qnil;
}
static VALUE
lazy_flat_map_to_ary(VALUE obj)
{
NODE *memo = RNODE(obj);
VALUE ary = rb_check_array_type(memo->u1.value);
if (NIL_P(ary)) {
rb_funcall(memo->u2.value, id_yield, 1, memo->u1.value);
}
else {
long i;
for (i = 0; i < RARRAY_LEN(ary); i++) {
rb_funcall(memo->u2.value, id_yield, 1, RARRAY_PTR(ary)[i]);
}
}
return Qnil;
}
static VALUE
lazy_flat_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE result = rb_yield_values2(argc - 1, &argv[1]);
if (TYPE(result) == T_ARRAY) {
long i;
for (i = 0; i < RARRAY_LEN(result); i++) {
rb_funcall(argv[0], id_yield, 1, RARRAY_PTR(result)[i]);
}
}
else {
NODE *memo;
memo = NEW_MEMO(result, argv[0], 0);
rb_rescue2(lazy_flat_map_each, (VALUE) memo,
lazy_flat_map_to_ary, (VALUE) memo,
rb_eNoMethodError, (VALUE)0);
}
return Qnil;
}
static VALUE
lazy_flat_map(VALUE obj)
{
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_flat_map_func, 0),
Qnil);
}
struct enumerator *e = enumerator_ptr(obj);
static VALUE
lazy_select_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
rb_ary_push(e->procs, create_proc_entry(T_PROC_MAP));
if (RTEST(rb_yield(element))) {
return rb_funcall(argv[0], id_yield, 1, element);
}
return Qnil;
return obj;
}
static VALUE
lazy_select(VALUE obj)
{
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "tried to call lazy select without a block");
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_select_func, 0),
Qnil);
}
static VALUE
lazy_reject_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
if (!RTEST(rb_yield(element))) {
return rb_funcall(argv[0], id_yield, 1, element);
}
return Qnil;
}
static VALUE
lazy_reject(VALUE obj)
{
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "tried to call lazy reject without a block");
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_reject_func, 0),
Qnil);
}
static VALUE
lazy_grep_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
VALUE result = rb_funcall(m, id_eqq, 1, i);
if (RTEST(result)) {
rb_funcall(argv[0], id_yield, 1, i);
}
return Qnil;
}
static VALUE
lazy_grep_iter(VALUE val, VALUE m, int argc, VALUE *argv)
{
VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
VALUE result = rb_funcall(m, id_eqq, 1, i);
if (RTEST(result)) {
rb_funcall(argv[0], id_yield, 1, rb_yield(i));
}
return Qnil;
}
static VALUE
lazy_grep(VALUE obj, VALUE pattern)
{
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
rb_block_given_p() ?
lazy_grep_iter : lazy_grep_func,
pattern),
rb_ary_new3(1, pattern));
}
static VALUE
call_next(VALUE obj)
{
return rb_funcall(obj, id_next, 0);
}
static VALUE
next_stopped(VALUE obj)
{
return Qnil;
}
static VALUE
lazy_zip_func(VALUE val, VALUE arg, int argc, VALUE *argv)
{
VALUE yielder, ary, v;
long i;
yielder = argv[0];
ary = rb_ary_new2(RARRAY_LEN(arg) + 1);
rb_ary_push(ary, argv[1]);
for (i = 0; i < RARRAY_LEN(arg); i++) {
v = rb_rescue2(call_next, RARRAY_PTR(arg)[i], next_stopped, 0,
rb_eStopIteration, (VALUE)0);
rb_ary_push(ary, v);
}
rb_funcall(yielder, id_yield, 1, ary);
return Qnil;
}
static VALUE
lazy_zip(int argc, VALUE *argv, VALUE obj)
{
VALUE ary;
int i;
if (rb_block_given_p()) {
return rb_call_super(argc, argv);
}
ary = rb_ary_new2(argc);
for (i = 0; i < argc; i++) {
rb_ary_push(ary, rb_funcall(argv[i], id_lazy, 0));
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_zip_func, ary),
rb_ary_new4(argc, argv));
}
static VALUE
lazy_take_func(VALUE val, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
if (--memo->u3.cnt == 0) {
return Qundef;
}
else {
return Qnil;
}
}
static VALUE
lazy_take(VALUE obj, VALUE n)
{
NODE *memo;
long len = NUM2LONG(n);
int argc = 1;
VALUE argv[3];
if (len < 0) {
rb_raise(rb_eArgError, "attempt to take negative size");
}
argv[0] = obj;
if (len == 0) {
argv[1] = sym_cycle;
argv[2] = INT2NUM(0);
argc = 3;
}
memo = NEW_MEMO(0, 0, len);
return lazy_set_method(rb_block_call(rb_cLazy, id_new, argc, argv,
lazy_take_func, (VALUE) memo),
rb_ary_new3(1, n));
}
static VALUE
lazy_take_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
{
VALUE result = rb_yield_values2(argc - 1, &argv[1]);
if (!RTEST(result)) return Qundef;
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
return Qnil;
}
static VALUE
lazy_take_while(VALUE obj)
{
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_take_while_func, 0),
Qnil);
}
static VALUE
lazy_drop_func(VALUE val, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
if (memo->u3.cnt == 0) {
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
}
else {
memo->u3.cnt--;
}
return Qnil;
}
static VALUE
lazy_drop(VALUE obj, VALUE n)
{
NODE *memo;
long len = NUM2LONG(n);
if (len < 0) {
rb_raise(rb_eArgError, "attempt to drop negative size");
}
memo = NEW_MEMO(0, 0, len);
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_drop_func, (VALUE) memo),
rb_ary_new3(1, n));
}
static VALUE
lazy_drop_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
if (!memo->u3.state && !RTEST(rb_yield_values2(argc - 1, &argv[1]))) {
memo->u3.state = TRUE;
}
if (memo->u3.state) {
rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
}
return Qnil;
}
static VALUE
lazy_drop_while(VALUE obj)
{
NODE *memo;
struct enumerator *e = enumerator_ptr(obj);
memo = NEW_MEMO(0, 0, FALSE);
return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_drop_while_func, (VALUE) memo),
Qnil);
}
rb_ary_push(e->procs, create_proc_entry(T_PROC_SELECT));
static VALUE
lazy_cycle_func(VALUE val, VALUE m, int argc, VALUE *argv)
{
return rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
return obj;
}
static VALUE
lazy_cycle(int argc, VALUE *argv, VALUE obj)
enumerable_lazy(VALUE self)
{
VALUE args;
int len = rb_long2int((long)argc + 2);
if (rb_block_given_p()) {
return rb_call_super(argc, argv);
}
args = rb_ary_tmp_new(len);
rb_ary_push(args, obj);
rb_ary_push(args, sym_cycle);
if (argc > 0) {
rb_ary_cat(args, argv, argc);
}
return lazy_set_method(rb_block_call(rb_cLazy, id_new, len,
RARRAY_PTR(args), lazy_cycle_func,
args /* prevent from GC */),
rb_ary_new4(argc, argv));
return rb_class_new_instance(1, &self, rb_cLazy);
}
static VALUE
lazy_lazy(VALUE obj)
{
return obj;
}
/* Lazy Enumerator methods */
//static VALUE
//lazy_init_iterator(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE result;
// if (argc == 1) {
// VALUE args[2];
// args[0] = m;
// args[1] = val;
// result = rb_yield_values2(2, args);
// }
// else {
// VALUE args;
// int len = rb_long2int((long)argc + 1);
//
// args = rb_ary_tmp_new(len);
// rb_ary_push(args, m);
// if (argc > 0) {
// rb_ary_cat(args, argv, argc);
// }
// result = rb_yield_values2(len, RARRAY_PTR(args));
// RB_GC_GUARD(args);
// }
// if (result == Qundef) rb_iter_break();
// return Qnil;
//}
//
//static VALUE
//lazy_init_yielder(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE result;
// result = rb_funcall2(m, id_yield, argc, argv);
// if (result == Qundef) rb_iter_break();
// return Qnil;
//}
//
//static VALUE
//lazy_init_block_i(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// rb_block_call(m, id_each, argc-1, argv+1, lazy_init_iterator, val);
// return Qnil;
//}
//
//static VALUE
//lazy_init_block(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// rb_block_call(m, id_each, argc-1, argv+1, lazy_init_yielder, val);
// return Qnil;
//}
//
//static VALUE
//lazy_initialize(int argc, VALUE *argv, VALUE self)
//{
// VALUE obj, meth;
// VALUE generator;
// int offset;
//
// if (argc < 1) {
// rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..)", argc);
// }
// else {
// obj = argv[0];
// if (argc == 1) {
// meth = sym_each;
// offset = 1;
// }
// else {
// meth = argv[1];
// offset = 2;
// }
// }
// generator = generator_allocate(rb_cGenerator);
// rb_block_call(generator, id_initialize, 0, 0,
// (rb_block_given_p() ? lazy_init_block_i : lazy_init_block),
// obj);
// enumerator_init(self, generator, meth, argc - offset, argv + offset);
// rb_ivar_set(self, id_receiver, obj);
//
// return self;
//}
//
//static VALUE
//lazy_set_method(VALUE lazy, VALUE args)
//{
// ID id = rb_frame_this_func();
// rb_ivar_set(lazy, id_method, ID2SYM(id));
// if (NIL_P(args)) {
// /* Qfalse indicates that the arguments are empty */
// rb_ivar_set(lazy, id_arguments, Qfalse);
// }
// else {
// rb_ivar_set(lazy, id_arguments, args);
// }
// return lazy;
//}
//
///*
// * call-seq:
// * e.lazy -> lazy_enumerator
// *
// * Returns a lazy enumerator, whose methods map/collect,
// * flat_map/collect_concat, select/find_all, reject, grep, zip, take,
// * take_while, drop, drop_while, and cycle enumerate values only on an
// * as-needed basis. However, if a block is given to zip or cycle, values
// * are enumerated immediately.
// *
// * === Example
// *
// * The following program finds pythagorean triples:
// *
// * def pythagorean_triples
// * (1..Float::INFINITY).lazy.flat_map {|z|
// * (1..z).flat_map {|x|
// * (x..z).select {|y|
// * x**2 + y**2 == z**2
// * }.map {|y|
// * [x, y, z]
// * }
// * }
// * }
// * end
// * # show first ten pythagorean triples
// * p pythagorean_triples.take(10).force # take is lazy, so force is needed
// * p pythagorean_triples.first(10) # first is eager
// * # show pythagorean triples less than 100
// * p pythagorean_triples.take_while { |*, z| z < 100 }.force
// */
//static VALUE
//enumerable_lazy(VALUE obj)
//{
// VALUE result;
//
// result = rb_class_new_instance(1, &obj, rb_cLazy);
// /* Qfalse indicates that the Enumerator::Lazy has no method name */
// rb_ivar_set(result, id_method, Qfalse);
// return result;
//}
//
//static VALUE
//lazy_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE result = rb_yield_values2(argc - 1, &argv[1]);
//
// rb_funcall(argv[0], id_yield, 1, result);
// return Qnil;
//}
//
//static VALUE
//lazy_map(VALUE obj)
//{
// if (!rb_block_given_p()) {
// rb_raise(rb_eArgError, "tried to call lazy map without a block");
// }
//
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_map_func, 0),
// Qnil);
//}
//
//static VALUE
//lazy_flat_map_i(VALUE i, VALUE yielder, int argc, VALUE *argv)
//{
// return rb_funcall2(yielder, id_yield, argc, argv);
//}
//
//static VALUE
//lazy_flat_map_each(VALUE obj)
//{
// NODE *memo = RNODE(obj);
// rb_block_call(memo->u1.value, id_each, 0, 0, lazy_flat_map_i,
// memo->u2.value);
// return Qnil;
//}
//
//static VALUE
//lazy_flat_map_to_ary(VALUE obj)
//{
// NODE *memo = RNODE(obj);
// VALUE ary = rb_check_array_type(memo->u1.value);
// if (NIL_P(ary)) {
// rb_funcall(memo->u2.value, id_yield, 1, memo->u1.value);
// }
// else {
// long i;
// for (i = 0; i < RARRAY_LEN(ary); i++) {
// rb_funcall(memo->u2.value, id_yield, 1, RARRAY_PTR(ary)[i]);
// }
// }
// return Qnil;
//}
//
//static VALUE
//lazy_flat_map_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE result = rb_yield_values2(argc - 1, &argv[1]);
// if (TYPE(result) == T_ARRAY) {
// long i;
// for (i = 0; i < RARRAY_LEN(result); i++) {
// rb_funcall(argv[0], id_yield, 1, RARRAY_PTR(result)[i]);
// }
// }
// else {
// NODE *memo;
// memo = NEW_MEMO(result, argv[0], 0);
// rb_rescue2(lazy_flat_map_each, (VALUE) memo,
// lazy_flat_map_to_ary, (VALUE) memo,
// rb_eNoMethodError, (VALUE)0);
// }
// return Qnil;
//}
//
//static VALUE
//lazy_flat_map(VALUE obj)
//{
// if (!rb_block_given_p()) {
// rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
// }
//
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_flat_map_func, 0),
// Qnil);
//}
//
//static VALUE
//lazy_select_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
//
// if (RTEST(rb_yield(element))) {
// return rb_funcall(argv[0], id_yield, 1, element);
// }
// return Qnil;
//}
//
//static VALUE
//lazy_select(VALUE obj)
//{
// if (!rb_block_given_p()) {
// rb_raise(rb_eArgError, "tried to call lazy select without a block");
// }
//
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_select_func, 0),
// Qnil);
//}
//
//static VALUE
//lazy_reject_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE element = rb_enum_values_pack(argc - 1, argv + 1);
//
// if (!RTEST(rb_yield(element))) {
// return rb_funcall(argv[0], id_yield, 1, element);
// }
// return Qnil;
//}
//
//static VALUE
//lazy_reject(VALUE obj)
//{
// if (!rb_block_given_p()) {
// rb_raise(rb_eArgError, "tried to call lazy reject without a block");
// }
//
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_reject_func, 0),
// Qnil);
//}
//
//static VALUE
//lazy_grep_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
// VALUE result = rb_funcall(m, id_eqq, 1, i);
//
// if (RTEST(result)) {
// rb_funcall(argv[0], id_yield, 1, i);
// }
// return Qnil;
//}
//
//static VALUE
//lazy_grep_iter(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// VALUE i = rb_enum_values_pack(argc - 1, argv + 1);
// VALUE result = rb_funcall(m, id_eqq, 1, i);
//
// if (RTEST(result)) {
// rb_funcall(argv[0], id_yield, 1, rb_yield(i));
// }
// return Qnil;
//}
//
//static VALUE
//lazy_grep(VALUE obj, VALUE pattern)
//{
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// rb_block_given_p() ?
// lazy_grep_iter : lazy_grep_func,
// pattern),
// rb_ary_new3(1, pattern));
//}
//
//static VALUE
//call_next(VALUE obj)
//{
// return rb_funcall(obj, id_next, 0);
//}
//
//static VALUE
//next_stopped(VALUE obj)
//{
// return Qnil;
//}
//
//static VALUE
//lazy_zip_func(VALUE val, VALUE arg, int argc, VALUE *argv)
//{
// VALUE yielder, ary, v;
// long i;
//
// yielder = argv[0];
// ary = rb_ary_new2(RARRAY_LEN(arg) + 1);
// rb_ary_push(ary, argv[1]);
// for (i = 0; i < RARRAY_LEN(arg); i++) {
// v = rb_rescue2(call_next, RARRAY_PTR(arg)[i], next_stopped, 0,
// rb_eStopIteration, (VALUE)0);
// rb_ary_push(ary, v);
// }
// rb_funcall(yielder, id_yield, 1, ary);
// return Qnil;
//}
//
//static VALUE
//lazy_zip(int argc, VALUE *argv, VALUE obj)
//{
// VALUE ary;
// int i;
//
// if (rb_block_given_p()) {
// return rb_call_super(argc, argv);
// }
// ary = rb_ary_new2(argc);
// for (i = 0; i < argc; i++) {
// rb_ary_push(ary, rb_funcall(argv[i], id_lazy, 0));
// }
//
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_zip_func, ary),
// rb_ary_new4(argc, argv));
//}
//
//static VALUE
//lazy_take_func(VALUE val, VALUE args, int argc, VALUE *argv)
//{
// NODE *memo = RNODE(args);
//
// rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
// if (--memo->u3.cnt == 0) {
// return Qundef;
// }
// else {
// return Qnil;
// }
//}
//
//static VALUE
//lazy_take(VALUE obj, VALUE n)
//{
// NODE *memo;
// long len = NUM2LONG(n);
// int argc = 1;
// VALUE argv[3];
//
// if (len < 0) {
// rb_raise(rb_eArgError, "attempt to take negative size");
// }
// argv[0] = obj;
// if (len == 0) {
// argv[1] = sym_cycle;
// argv[2] = INT2NUM(0);
// argc = 3;
// }
// memo = NEW_MEMO(0, 0, len);
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, argc, argv,
// lazy_take_func, (VALUE) memo),
// rb_ary_new3(1, n));
//}
//
//static VALUE
//lazy_take_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
//{
// VALUE result = rb_yield_values2(argc - 1, &argv[1]);
// if (!RTEST(result)) return Qundef;
// rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
// return Qnil;
//}
//
//static VALUE
//lazy_take_while(VALUE obj)
//{
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_take_while_func, 0),
// Qnil);
//}
//
//static VALUE
//lazy_drop_func(VALUE val, VALUE args, int argc, VALUE *argv)
//{
// NODE *memo = RNODE(args);
//
// if (memo->u3.cnt == 0) {
// rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
// }
// else {
// memo->u3.cnt--;
// }
// return Qnil;
//}
//
//static VALUE
//lazy_drop(VALUE obj, VALUE n)
//{
// NODE *memo;
// long len = NUM2LONG(n);
//
// if (len < 0) {
// rb_raise(rb_eArgError, "attempt to drop negative size");
// }
// memo = NEW_MEMO(0, 0, len);
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_drop_func, (VALUE) memo),
// rb_ary_new3(1, n));
//}
//
//static VALUE
//lazy_drop_while_func(VALUE val, VALUE args, int argc, VALUE *argv)
//{
// NODE *memo = RNODE(args);
//
// if (!memo->u3.state && !RTEST(rb_yield_values2(argc - 1, &argv[1]))) {
// memo->u3.state = TRUE;
// }
// if (memo->u3.state) {
// rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
// }
// return Qnil;
//}
//
//static VALUE
//lazy_drop_while(VALUE obj)
//{
// NODE *memo;
//
// memo = NEW_MEMO(0, 0, FALSE);
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
// lazy_drop_while_func, (VALUE) memo),
// Qnil);
//}
//
//static VALUE
//lazy_cycle_func(VALUE val, VALUE m, int argc, VALUE *argv)
//{
// return rb_funcall2(argv[0], id_yield, argc - 1, argv + 1);
//}
//
//static VALUE
//lazy_cycle(int argc, VALUE *argv, VALUE obj)
//{
// VALUE args;
// int len = rb_long2int((long)argc + 2);
//
// if (rb_block_given_p()) {
// return rb_call_super(argc, argv);
// }
// args = rb_ary_tmp_new(len);
// rb_ary_push(args, obj);
// rb_ary_push(args, sym_cycle);
// if (argc > 0) {
// rb_ary_cat(args, argv, argc);
// }
// return lazy_set_method(rb_block_call(rb_cLazy, id_new, len,
// RARRAY_PTR(args), lazy_cycle_func,
// args /* prevent from GC */),
// rb_ary_new4(argc, argv));
//}
//
//static VALUE
//lazy_lazy(VALUE obj)
//{
// return obj;
//}
/*
* Document-class: StopIteration
......
rb_define_method(rb_mEnumerable, "lazy", enumerable_lazy, 0);
rb_define_method(rb_cLazy, "initialize", lazy_initialize, -1);
rb_define_method(rb_cLazy, "map", lazy_map, 0);
rb_define_method(rb_cLazy, "collect", lazy_map, 0);
rb_define_method(rb_cLazy, "flat_map", lazy_flat_map, 0);
rb_define_method(rb_cLazy, "collect_concat", lazy_flat_map, 0);
//rb_define_method(rb_cLazy, "collect", lazy_map, 0);
//rb_define_method(rb_cLazy, "flat_map", lazy_flat_map, 0);
//rb_define_method(rb_cLazy, "collect_concat", lazy_flat_map, 0);
rb_define_method(rb_cLazy, "select", lazy_select, 0);
rb_define_method(rb_cLazy, "find_all", lazy_select, 0);
rb_define_method(rb_cLazy, "reject", lazy_reject, 0);
rb_define_method(rb_cLazy, "grep", lazy_grep, 1);
rb_define_method(rb_cLazy, "zip", lazy_zip, -1);
rb_define_method(rb_cLazy, "take", lazy_take, 1);
rb_define_method(rb_cLazy, "take_while", lazy_take_while, 0);
rb_define_method(rb_cLazy, "drop", lazy_drop, 1);
rb_define_method(rb_cLazy, "drop_while", lazy_drop_while, 0);
rb_define_method(rb_cLazy, "cycle", lazy_cycle, -1);
rb_define_method(rb_cLazy, "lazy", lazy_lazy, 0);
rb_define_alias(rb_cLazy, "force", "to_a");
//rb_define_method(rb_cLazy, "reject", lazy_reject, 0);
//rb_define_method(rb_cLazy, "grep", lazy_grep, 1);
//rb_define_method(rb_cLazy, "zip", lazy_zip, -1);
//rb_define_method(rb_cLazy, "take", lazy_take, 1);
//rb_define_method(rb_cLazy, "take_while", lazy_take_while, 0);
//rb_define_method(rb_cLazy, "drop", lazy_drop, 1);
//rb_define_method(rb_cLazy, "drop_while", lazy_drop_while, 0);
//rb_define_method(rb_cLazy, "cycle", lazy_cycle, -1);
//rb_define_method(rb_cLazy, "lazy", lazy_lazy, 0);
//rb_define_alias(rb_cLazy, "force", "to_a");
rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
rb_define_method(rb_eStopIteration, "result", stop_result, 0);
(1-1/3)