Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

enumerator extended with lazy select and map #100

Closed
wants to merge 3 commits into from

2 participants

Innokenty Mikhailov 7rans
Innokenty Mikhailov

Please, see http://bugs.ruby-lang.org/issues/4890 for more info about enumerator laziness.

I've added Enumerator#lazy method that marks enumerator as lazy.
Also lazy Enumerator#map and Enumerator#select defined.
The idea is very simple - block that passed to lazy method (select or map) is converted to Proc and stored in enumerator itself.
When next element requested - all Proc objects are called for this value and the result returned. Proc#call result handling depends on proc_entry type.

Example:

a = [1,2,3,4].to_enum.lazy
a.map { |a| a*10 }.select { |a| a > 10 }
puts a.next #=> 20
puts a.next #=> 30
puts a.next #=> 40

Keep in mind - I'm kinda a newbie in ruby patching, so please, let me know your thoughts/fixes/comments.
Does it make any sense to add laziness this way?

Thanks.

7rans

Is this marking the Enumerator in place rather returning a new "lazy enumerator"?

Innokenty Mikhailov

Yeah, it's marking in place. But that's not a problem since we have enumerator_init_copy, that can be used in Enumerator#lazy.
This PR is to demonstrate an idea, if the idea makes sense - I'll add necessary fixes and methods.

Innokenty Mikhailov gregolsen closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 24, 2012
  1. Innokenty Mikhailov
Commits on Feb 25, 2012
  1. Innokenty Mikhailov
Commits on Feb 26, 2012
  1. Innokenty Mikhailov
This page is out of date. Refresh to see the latest.
Showing with 91 additions and 1 deletion.
  1. +91 −1 enumerator.c
92 enumerator.c
View
@@ -115,6 +115,18 @@ struct enumerator {
VALUE lookahead;
VALUE feedvalue;
VALUE stop_exc;
+ VALUE lazy;
+ VALUE procs;
+};
+
+static struct proc_entry {
+ VALUE proc;
+ VALUE type;
+};
+
+static enum proc_entry_type {
+ T_PROC_MAP = 0,
+ T_PROC_SELECT = 1
};
static VALUE rb_cGenerator, rb_cYielder;
@@ -144,6 +156,8 @@ enumerator_mark(void *p)
rb_gc_mark(ptr->lookahead);
rb_gc_mark(ptr->feedvalue);
rb_gc_mark(ptr->stop_exc);
+ rb_gc_mark(ptr->lazy);
+ rb_gc_mark(ptr->procs);
}
#define enumerator_free RUBY_TYPED_DEFAULT_FREE
@@ -246,6 +260,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
ptr->lookahead = Qundef;
ptr->feedvalue = Qundef;
ptr->stop_exc = Qfalse;
+ ptr->lazy = Qfalse;
+ ptr->procs = rb_ary_new();
return enum_obj;
}
@@ -412,6 +428,54 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
return rb_block_call(e->obj, meth, argc, argv, func, arg);
}
+static VALUE
+enumerator_lazy(VALUE obj)
+{
+ struct enumerator *e = enumerator_ptr(obj);
+
+ e->lazy = Qtrue;
+
+ return obj;
+}
+
+static VALUE
+enumerator_map(VALUE obj)
+{
+ struct enumerator *e = enumerator_ptr(obj);
+ struct proc_entry *entry;
+ VALUE entry_obj;
+
+ if (e->lazy) {
+ 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 = T_PROC_MAP;
+ rb_ary_push(e->procs, entry_obj);
+ return obj;
+ } else {
+ return rb_call_super(0, 0);
+ }
+}
+
+static VALUE
+enumerator_select(VALUE obj)
+{
+ struct enumerator *e = enumerator_ptr(obj);
+ struct proc_entry *entry;
+ VALUE entry_obj;
+
+ if (e->lazy) {
+ 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 = T_PROC_SELECT;
+ rb_ary_push(e->procs, entry_obj);
+ return obj;
+ } else {
+ return rb_call_super(0, 0);
+ }
+}
+
/*
* call-seq:
* enum.each {...}
@@ -532,7 +596,30 @@ next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
struct enumerator *e = enumerator_ptr(obj);
VALUE feedvalue = Qnil;
VALUE args = rb_ary_new4(argc, argv);
- rb_fiber_yield(1, &args);
+ VALUE result = i;
+ struct proc_entry *entry;
+ VALUE move_next = Qtrue;
+
+ if (e->lazy) {
+ int j = 0;
+ for (j = 0; j < RARRAY_LEN(e->procs); j++) {
+ Data_Get_Struct(RARRAY_PTR(e->procs)[j], 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 (move_next)
+ move_next = rb_funcall(entry->proc, rb_intern("call"), 1, result);
+ break;
+ }
+ }
+ if (move_next)
+ rb_fiber_yield(1, &result);
+ } else {
+ rb_fiber_yield(1, &args);
+ }
+
if (e->feedvalue != Qundef) {
feedvalue = e->feedvalue;
e->feedvalue = Qundef;
@@ -1202,6 +1289,9 @@ Init_Enumerator(void)
rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
+ rb_define_method(rb_cEnumerator, "lazy", enumerator_lazy, 0);
+ rb_define_method(rb_cEnumerator, "map", enumerator_map, 0);
+ rb_define_method(rb_cEnumerator, "select", enumerator_select, 0);
rb_define_method(rb_cEnumerator, "each_with_index", enumerator_each_with_index, 0);
rb_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
Something went wrong with that request. Please try again.