Bug #6250 » lazy_enumerator.diff
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);
|