From: nobu.nokada@... Date: 2004-05-19T11:47:30+09:00 Subject: Re: RCR: More enumerator functionality Hi, At Mon, 17 May 2004 20:34:05 +0900, Kristof Bastiaensen wrote in [ruby-talk:100500]: > 5.enum_filter(:times){ |i| i[0] == 0 }.collect > => [2, 4] => [0, 2, 4] Isn't it? > (2)* Enumerable::Enumerator takes a optional block > > powers = 4.enum_for(:times){ |i| i * i } > powers.collect > => [0, 1, 4, 9] I tried the implementation of these two experimentally. But I feel enum_filter is overdoing after all. This patch provides block for Enumerable#enum_for and Enumerable#enum_if instead. $ ./ruby -renumerator -e 'p 5.enum_for(:times){|i| i*i}.to_a' [0, 1, 4, 9, 16] $ ./ruby -renumerator -e 'p 5.enum_for(:times).enum_if{|i| i[0] == 0}.to_a' [0, 2, 4] Just my 2 yens. Index: eval.c =================================================================== RCS file: /cvs/ruby/src/ruby/eval.c,v retrieving revision 1.663 diff -U2 -p -d -r1.663 eval.c --- eval.c 14 May 2004 16:45:21 -0000 1.663 +++ eval.c 18 May 2004 01:02:30 -0000 @@ -141,5 +141,5 @@ static VALUE rb_f_block_given_p _((void) static VALUE block_pass _((VALUE,NODE*)); static VALUE rb_cMethod; -static VALUE method_call _((int, VALUE*, VALUE)); +VALUE rb_method_call _((int, VALUE*, VALUE)); static VALUE rb_cUnboundMethod; static VALUE umethod_bind _((VALUE, VALUE)); @@ -8116,6 +8116,6 @@ proc_invoke(proc, args, self, klass) */ -static VALUE -proc_call(proc, args) +VALUE +rb_proc_call(proc, args) VALUE proc, args; /* OK */ { @@ -8606,5 +8606,5 @@ method_unbind(obj) */ -static VALUE +VALUE rb_obj_method(obj, vid) VALUE obj; @@ -8686,6 +8686,6 @@ method_clone(self) */ -static VALUE -method_call(argc, argv, method) +VALUE +rb_method_call(argc, argv, method) int argc; VALUE *argv; @@ -8981,5 +8981,5 @@ bmcall(args, method) a = svalue_to_avalue(args); - return method_call(RARRAY(a)->len, RARRAY(a)->ptr, method); + return rb_method_call(RARRAY(a)->len, RARRAY(a)->ptr, method); } @@ -9179,7 +9179,7 @@ Init_Proc() rb_define_method(rb_cProc, "clone", proc_clone, 0); - rb_define_method(rb_cProc, "call", proc_call, -2); + rb_define_method(rb_cProc, "call", rb_proc_call, -2); rb_define_method(rb_cProc, "arity", proc_arity, 0); - rb_define_method(rb_cProc, "[]", proc_call, -2); + rb_define_method(rb_cProc, "[]", rb_proc_call, -2); rb_define_method(rb_cProc, "==", proc_eq, 1); rb_define_method(rb_cProc, "eql?", proc_eq, 1); @@ -9199,6 +9199,6 @@ Init_Proc() rb_define_method(rb_cMethod, "hash", method_hash, 0); rb_define_method(rb_cMethod, "clone", method_clone, 0); - rb_define_method(rb_cMethod, "call", method_call, -1); - rb_define_method(rb_cMethod, "[]", method_call, -1); + rb_define_method(rb_cMethod, "call", rb_method_call, -1); + rb_define_method(rb_cMethod, "[]", rb_method_call, -1); rb_define_method(rb_cMethod, "arity", method_arity, 0); rb_define_method(rb_cMethod, "inspect", method_inspect, 0); Index: ext/enumerator/enumerator.c =================================================================== RCS file: /cvs/ruby/src/ruby/ext/enumerator/enumerator.c,v retrieving revision 1.3 diff -U2 -p -d -r1.3 enumerator.c --- ext/enumerator/enumerator.c 17 Oct 2003 14:09:43 -0000 1.3 +++ ext/enumerator/enumerator.c 19 May 2004 02:40:29 -0000 @@ -14,9 +14,70 @@ #include "ruby.h" -#include "node.h" static VALUE rb_cEnumerator; static ID sym_each, sym_each_with_index, sym_each_slice, sym_each_cons; -static ID id_new, id_enum_obj, id_enum_method, id_enum_args; +#if !defined(HAVE_RB_PROC_CALL) || !defined(HAVE_RB_METHOD_CALL) +static ID id_call; +#endif + +static VALUE +proc_call(proc, args) + VALUE proc, args; +{ +#ifdef HAVE_RB_PROC_CALL + if (TYPE(args) != T_ARRAY) { + args = rb_values_new(1, args); + } + return rb_proc_call(proc, args); +#else + return rb_funcall2(proc, id_call, 1, &args); +#endif +} + +static VALUE +method_call(method, args) + VALUE method, args; +{ +#ifdef HAVE_RB_METHOD_CALL + return rb_method_call(RARRAY(args)->len, RARRAY(args)->ptr, method); +#else + return rb_funcall2(method, id_call, RARRAY(args)->len, RARRAY(args)->ptr); +#endif +} + +struct enumerator { + VALUE method; + VALUE proc; + VALUE args; + VALUE (*iter)_((VALUE, struct enumerator *)); +}; + +static void +enumerator_mark(ptr) + struct enumerator *ptr; +{ + rb_gc_mark(ptr->method); + rb_gc_mark(ptr->proc); + rb_gc_mark(ptr->args); +} + +static struct enumerator * +enumerator_ptr(obj) + VALUE obj; +{ + struct enumerator *ptr = DATA_PTR(obj); + if (!ptr) { + rb_raise(rb_eArgError, "uninitialized enumerator"); + } + return ptr; +} + +static VALUE +enumerator_iter_i(i, e) + VALUE i; + struct enumerator *e; +{ + return rb_yield(proc_call(e->proc, i)); +} static VALUE @@ -26,5 +87,7 @@ obj_to_enum(obj, enum_args) rb_ary_unshift(enum_args, obj); - return rb_apply(rb_cEnumerator, id_new, enum_args); + return rb_class_new_instance(RARRAY(enum_args)->len, + RARRAY(enum_args)->ptr, + rb_cEnumerator); } @@ -33,14 +96,17 @@ enumerator_enum_with_index(obj) VALUE obj; { - return rb_funcall(rb_cEnumerator, id_new, 2, obj, sym_each_with_index); + VALUE args[2]; + args[0] = obj; + args[1] = sym_each_with_index; + return rb_class_new_instance(2, args, rb_cEnumerator); } static VALUE -each_slice_i(val, memo) +each_slice_i(val, args) VALUE val; - NODE *memo; + VALUE *args; { - VALUE ary = memo->u1.value; - long size = memo->u3.cnt; + VALUE ary = args[0]; + long size = (long)args[1]; rb_ary_push(ary, val); @@ -48,5 +114,5 @@ each_slice_i(val, memo) if (RARRAY(ary)->len == size) { rb_yield(ary); - memo->u1.value = rb_ary_new2(size); + args[0] = rb_ary_new2(size); } @@ -59,17 +125,16 @@ enum_each_slice(obj, n) { long size = NUM2LONG(n); - NODE *memo; - VALUE ary; + VALUE args[2], ary; if (size <= 0) rb_raise(rb_eArgError, "invalid slice size"); - memo = rb_node_newnode(NODE_MEMO, rb_ary_new2(size), 0, size); + args[0] = rb_ary_new2(size); + args[1] = (VALUE)size; - rb_iterate(rb_each, obj, each_slice_i, (VALUE)memo); + rb_iterate(rb_each, obj, each_slice_i, (VALUE)args); - ary = memo->u1.value; + ary = args[0]; if (RARRAY(ary)->len > 0) rb_yield(ary); - rb_gc_force_recycle((VALUE)memo); return Qnil; } @@ -79,14 +144,18 @@ enumerator_enum_slice(obj, n) VALUE obj, n; { - return rb_funcall(rb_cEnumerator, id_new, 3, obj, sym_each_slice, n); + VALUE args[2]; + args[0] = obj; + args[1] = sym_each_slice; + args[2] = n; + return rb_class_new_instance(3, args, rb_cEnumerator); } static VALUE -each_cons_i(val, memo) +each_cons_i(val, args) VALUE val; - NODE *memo; + VALUE *args; { - VALUE ary = memo->u1.value; - long size = memo->u3.cnt; + VALUE ary = args[0]; + long size = (long)args[1]; long len = RARRAY(ary)->len; @@ -107,13 +176,12 @@ enum_each_cons(obj, n) { long size = NUM2LONG(n); - NODE *memo; - VALUE ary; + VALUE args[3]; if (size <= 0) rb_raise(rb_eArgError, "invalid size"); - memo = rb_node_newnode(NODE_MEMO, rb_ary_new2(size), 0, size); + args[0] = rb_ary_new2(size); + args[1] = (VALUE)size; - rb_iterate(rb_each, obj, each_cons_i, (VALUE)memo); + rb_iterate(rb_each, obj, each_cons_i, (VALUE)args); - rb_gc_force_recycle((VALUE)memo); return Qnil; } @@ -123,5 +191,41 @@ enumerator_enum_cons(obj, n) VALUE obj, n; { - return rb_funcall(rb_cEnumerator, id_new, 3, obj, sym_each_cons, n); + VALUE args[2]; + args[0] = obj; + args[1] = sym_each_cons; + args[2] = n; + return rb_class_new_instance(3, args, rb_cEnumerator); +} + +static VALUE +enumerator_select(i, e) + VALUE i; + struct enumerator *e; +{ + if (!RTEST(proc_call(e->proc, i))) return Qnil; + return rb_yield(i); +} + +static VALUE +enumerator_enum_if(obj, enum_args) + VALUE obj, enum_args; +{ + rb_ary_unshift(enum_args, obj); + + obj = rb_class_new_instance(RARRAY(enum_args)->len, + RARRAY(enum_args)->ptr, + rb_cEnumerator); + ((struct enumerator *)DATA_PTR(obj))->iter = enumerator_select; + return obj; +} + +static VALUE enumerator_allocate _((VALUE)); +static VALUE +enumerator_allocate(klass) + VALUE klass; +{ + struct enumerator *ptr; + return Data_Make_Struct(rb_cEnumerator, struct enumerator, + enumerator_mark, -1, ptr); } @@ -133,4 +237,5 @@ enumerator_initialize(argc, argv, obj) { VALUE enum_obj, enum_method, enum_args; + struct enumerator *ptr = enumerator_ptr(obj); rb_scan_args(argc, argv, "11*", &enum_obj, &enum_method, &enum_args); @@ -139,16 +244,25 @@ enumerator_initialize(argc, argv, obj) enum_method = sym_each; - rb_ivar_set(obj, id_enum_obj, enum_obj); - rb_ivar_set(obj, id_enum_method, enum_method); - rb_ivar_set(obj, id_enum_args, enum_args); + ptr->method = rb_obj_method(enum_obj, enum_method); + if (rb_block_given_p()) { + ptr->proc = rb_block_proc(); + ptr->iter = enumerator_iter_i; + } + else { + ptr->iter = (VALUE (*)())rb_yield; + } + ptr->args = enum_args; - return Qnil; + return obj; } +static VALUE enumerator_iter _((VALUE)); static VALUE -enumerator_iter(memo) - NODE *memo; +enumerator_iter(arg) + VALUE arg; { - return rb_apply(memo->u1.value, memo->u2.id, memo->u3.value); + struct enumerator *e = (struct enumerator *)arg; + + return method_call(e->method, e->args); } @@ -157,13 +271,16 @@ enumerator_each(obj) VALUE obj; { - VALUE val; + struct enumerator *e = enumerator_ptr(obj); - obj = (VALUE)rb_node_newnode(NODE_MEMO, - rb_ivar_get(obj, id_enum_obj), - rb_to_id(rb_ivar_get(obj, id_enum_method)), - rb_ivar_get(obj, id_enum_args)); - val = rb_iterate((VALUE (*)_((VALUE)))enumerator_iter, obj, rb_yield, 0); - rb_gc_force_recycle(obj); - return val; + return rb_iterate(enumerator_iter, (VALUE)e, e->iter, (VALUE)e); +} + +static VALUE +enumerator_call(obj, args) + VALUE obj, args; +{ + struct enumerator *e = enumerator_ptr(obj); + + return method_call(e->method, rb_ary_concat(e->args, args)); } @@ -183,10 +300,13 @@ Init_enumerator() rb_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1); rb_define_method(rb_mEnumerable, "enum_cons", enumerator_enum_cons, 1); + rb_define_method(rb_mEnumerable, "enum_if", enumerator_enum_if, -2); rb_cEnumerator = rb_define_class_under(rb_mEnumerable, "Enumerator", rb_cObject); rb_include_module(rb_cEnumerator, rb_mEnumerable); + rb_define_alloc_func(rb_cEnumerator, enumerator_allocate); rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1); rb_define_method(rb_cEnumerator, "each", enumerator_each, 0); + rb_define_method(rb_cEnumerator, "call", enumerator_call, -2); sym_each = ID2SYM(rb_intern("each")); @@ -195,7 +315,6 @@ Init_enumerator() sym_each_cons = ID2SYM(rb_intern("each_cons")); - id_new = rb_intern("new"); - id_enum_obj = rb_intern("enum_obj"); - id_enum_method = rb_intern("enum_method"); - id_enum_args = rb_intern("enum_args"); +#if !defined(HAVE_RB_PROC_CALL) || !defined(HAVE_RB_METHOD_CALL) + id_call = rb_intern("call"); +#endif } Index: ext/enumerator/extconf.rb =================================================================== RCS file: ext/enumerator/extconf.rb diff -N ext/enumerator/extconf.rb --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ext/enumerator/extconf.rb 18 May 2004 11:50:10 -0000 @@ -0,0 +1,5 @@ +require 'mkmf' + +%w"rb_obj_method rb_method_call".all? {|f| have_func(f, "ruby.h")} +have_func("rb_proc_call", "ruby.h") +create_makefile("enumerator") -- Nobu Nakada