Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Ruby (http://www.velocityreviews.com/forums/f66-ruby.html)
-   -   RCR: More enumerator functionality (http://www.velocityreviews.com/forums/t814663-rcr-more-enumerator-functionality.html)

Kristof Bastiaensen 05-17-2004 11:30 AM

RCR: More enumerator functionality
 
Hi,
I would like to know your opinion about the following idea's.
(It is actually are two RCR's)

(1)* Enumerable::filter and Enumerable::enum_filter

filter([[method = :each], *args, ] block)
yield values for which block returns true

3.filter(:times, &lambda{ |i| i[0] == 0 }) do |n|
puts "%i is even" % n
end
=> 2 is even

composers = %W(Brahms Berlioz Chopin)
composers.filter(&lambda { |i| i[0] == ?B }) do |c|
puts "composer with B: #{c}"
end
=> composer with B: Brahms
=> composer with B: Berlioz

and now the fun stuff (turning into an enum):

5.enum_filter(:times){ |i| i[0] == 0 }.collect
=> [2, 4]

(2)* Enumerable::Enumerator takes a optional block

powers = 4.enum_for(:times){ |i| i * i }
powers.collect
=> [0, 1, 4, 9]

Thanks,
Kristof

nobu.nokada@softhome.net 05-17-2004 12:46 PM

Re: RCR: More enumerator functionality
 
Hi,

At Mon, 17 May 2004 20:34:05 +0900,
Kristof Bastiaensen wrote in [ruby-talk:100500]:
> filter([[method = :each], *args, ] block)
> yield values for which block returns true
>
> 3.filter(:times, &lambda{ |i| i[0] == 0 }) do |n|
> puts "%i is even" % n
> end
> => 2 is even


$ ruby -c
3.filter(:times, &lambda{ |i| i[0] == 0 }) do |n|
puts "%i is even" % n
end
-:3: both block arg and actual block given

> and now the fun stuff (turning into an enum):
>
> 5.enum_filter(:times){ |i| i[0] == 0 }.collect
> => [2, 4]


Is it a method of Enumerator, although Integer doesn't include
it?

> (2)* Enumerable::Enumerator takes a optional block
>
> powers = 4.enum_for(:times){ |i| i * i }
> powers.collect
> => [0, 1, 4, 9]


Seems considerble, IMHO.

--
Nobu Nakada



Kristof Bastiaensen 05-17-2004 01:39 PM

Re: RCR: More enumerator functionality
 
On Mon, 17 May 2004 21:46:45 +0900, nobu.nokada wrote:

>> and now the fun stuff (turning into an enum):
>>
>> 5.enum_filter(:times){ |i| i[0] == 0 }.collect
>> => [2, 4]

>
> Is it a method of Enumerator, although Integer doesn't include
> it?


Sorry, I forgot to mention it.
It would be a method of Object (like enum_for).

Regards,
Kristof

gabriele renzi 05-17-2004 01:48 PM

Re: RCR: More enumerator functionality
 
il Mon, 17 May 2004 13:30:49 +0200, Kristof Bastiaensen
<kristof@vleeuwen.org> ha scritto::

>Hi,
>I would like to know your opinion about the following idea's.
>(It is actually are two RCR's)
>
>(1)* Enumerable::filter and Enumerable::enum_filter
>
>filter([[method = :each], *args, ] block)
> yield values for which block returns true
>
>3.filter(:times, &lambda{ |i| i[0] == 0 }) do |n|
> puts "%i is even" % n
>end
>=> 2 is even


what's wrong with
(1..3).select {|x| x[0]==0}.each {|x| p x}
?



>
>(2)* Enumerable::Enumerator takes a optional block
>
>powers = 4.enum_for(:times){ |i| i * i }
>powers.collect
>=> [0, 1, 4, 9]


interesting, you should post this on rcrarchive (or maybe more than
one)

Robert Klemme 05-17-2004 01:55 PM

Re: RCR: More enumerator functionality
 

"gabriele renzi" <surrender_it@remove.yahoo.it> schrieb im Newsbeitrag
news:f9gha09qpp95hdmuhq498pjneu2qkqmeh1@4ax.com...
> il Mon, 17 May 2004 13:30:49 +0200, Kristof Bastiaensen
> <kristof@vleeuwen.org> ha scritto::
>
> >Hi,
> >I would like to know your opinion about the following idea's.
> >(It is actually are two RCR's)
> >
> >(1)* Enumerable::filter and Enumerable::enum_filter
> >
> >filter([[method = :each], *args, ] block)
> > yield values for which block returns true
> >
> >3.filter(:times, &lambda{ |i| i[0] == 0 }) do |n|
> > puts "%i is even" % n
> >end
> >=> 2 is even

>
> what's wrong with
> (1..3).select {|x| x[0]==0}.each {|x| p x}
> ?


If the Enumerable contains a huge number of elements then this version
will burn a lot of memory while in certain cases (e.g. printing) it is
more efficient to be able to handle one element at a time only.

Well, of course in those cases you could simply do

(1..3).each {|x| p x if x[0]==0}

:-)

Regards

robert


Kristof Bastiaensen 05-17-2004 02:22 PM

Re: RCR: More enumerator functionality
 
Hi,

On Mon, 17 May 2004 15:55:25 +0200, Robert Klemme wrote:
>>
>> what's wrong with
>> (1..3).select {|x| x[0]==0}.each {|x| p x}
>> ?

>
> If the Enumerable contains a huge number of elements then this version
> will burn a lot of memory while in certain cases (e.g. printing) it is
> more efficient to be able to handle one element at a time only.
>
> Well, of course in those cases you could simply do
>
> (1..3).each {|x| p x if x[0]==0}
>
> :-)
>


That's indeed more elegant than with filter and lambdas.
The filter method would be only useful for implementing
enum_filter, maybe I will exclude it from the RCR.

Enum_filter would also behave like select, but it would
be more efficient for memory. And you can keep the
enumerable around it you need it later (even when the
original object has changed it's contents).

> Regards
>
> robert


Thanks,
Kristof

David A. Black 05-18-2004 06:44 PM

Re: RCR: More enumerator functionality
 
Hi --

On Mon, 17 May 2004, Kristof Bastiaensen wrote:

> 5.enum_filter(:times){ |i| i[0] == 0 }.collect
> => [2, 4]


[...]

> (2)* Enumerable::Enumerator takes a optional block
>
> powers = 4.enum_for(:times){ |i| i * i }
> powers.collect
> => [0, 1, 4, 9]


Keep in mind rejected RCR #50
(http://www.rcrchive.net/rgarchive/rejected.html#rcr50). It's perhaps
not identical to what you're proposing, but very similar. (It's
proposed as a modification of #map.)

(Aside: collect without a block returns an array with the same
elements as the receiver, so there's probably no point calling it.)


David

--
David A. Black
dblack@wobblini.net




Robert Klemme 05-18-2004 07:12 PM

Re: RCR: More enumerator functionality
 

"David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
news:Pine.LNX.4.44.0405181140170.1696-100000@wobblini...
> Hi --
>
> On Mon, 17 May 2004, Kristof Bastiaensen wrote:
>
> > 5.enum_filter(:times){ |i| i[0] == 0 }.collect
> > => [2, 4]

>
> [...]
>
> > (2)* Enumerable::Enumerator takes a optional block
> >
> > powers = 4.enum_for(:times){ |i| i * i }
> > powers.collect
> > => [0, 1, 4, 9]

>
> Keep in mind rejected RCR #50
> (http://www.rcrchive.net/rgarchive/rejected.html#rcr50). It's perhaps
> not identical to what you're proposing, but very similar. (It's
> proposed as a modification of #map.)
>
> (Aside: collect without a block returns an array with the same
> elements as the receiver, so there's probably no point calling it.)


Probably #to_a would be clearer here. I guess the aim was to print an array
from the Enumerator.

robert


David A. Black 05-18-2004 09:14 PM

Re: RCR: More enumerator functionality
 
Hi --

On Wed, 19 May 2004, Robert Klemme wrote:

>
> "David A. Black" <dblack@wobblini.net> schrieb im Newsbeitrag
> news:Pine.LNX.4.44.0405181140170.1696-100000@wobblini...
> > Hi --
> >
> > On Mon, 17 May 2004, Kristof Bastiaensen wrote:
> >
> > > 5.enum_filter(:times){ |i| i[0] == 0 }.collect
> > > => [2, 4]

> >
> > [...]
> >
> > > (2)* Enumerable::Enumerator takes a optional block
> > >
> > > powers = 4.enum_for(:times){ |i| i * i }
> > > powers.collect
> > > => [0, 1, 4, 9]

> >
> > Keep in mind rejected RCR #50
> > (http://www.rcrchive.net/rgarchive/rejected.html#rcr50). It's perhaps
> > not identical to what you're proposing, but very similar. (It's
> > proposed as a modification of #map.)
> >
> > (Aside: collect without a block returns an array with the same
> > elements as the receiver, so there's probably no point calling it.)

>
> Probably #to_a would be clearer here. I guess the aim was to print an array
> from the Enumerator.


Sorry; I was misreading the whole post, and therefore
misunderstanding.


David

--
David A. Black
dblack@wobblini.net




nobu.nokada@softhome.net 05-19-2004 02:47 AM

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




All times are GMT. The time now is 04:19 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.