Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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 March 05, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 91 additions and 1 deletion. Show diff stats Hide diff stats

  1. 92  enumerator.c
92  enumerator.c
@@ -115,6 +115,18 @@ struct enumerator {
115 115
     VALUE lookahead;
116 116
     VALUE feedvalue;
117 117
     VALUE stop_exc;
  118
+    VALUE lazy;
  119
+    VALUE procs;
  120
+};
  121
+
  122
+static struct proc_entry {
  123
+    VALUE proc;
  124
+    VALUE type;
  125
+};
  126
+
  127
+static enum proc_entry_type {
  128
+    T_PROC_MAP = 0,
  129
+    T_PROC_SELECT = 1
118 130
 };
119 131
 
120 132
 static VALUE rb_cGenerator, rb_cYielder;
@@ -144,6 +156,8 @@ enumerator_mark(void *p)
144 156
     rb_gc_mark(ptr->lookahead);
145 157
     rb_gc_mark(ptr->feedvalue);
146 158
     rb_gc_mark(ptr->stop_exc);
  159
+    rb_gc_mark(ptr->lazy);
  160
+    rb_gc_mark(ptr->procs);
147 161
 }
148 162
 
149 163
 #define enumerator_free RUBY_TYPED_DEFAULT_FREE
@@ -246,6 +260,8 @@ enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
246 260
     ptr->lookahead = Qundef;
247 261
     ptr->feedvalue = Qundef;
248 262
     ptr->stop_exc = Qfalse;
  263
+    ptr->lazy = Qfalse;
  264
+    ptr->procs = rb_ary_new();
249 265
 
250 266
     return enum_obj;
251 267
 }
@@ -412,6 +428,54 @@ enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
412 428
     return rb_block_call(e->obj, meth, argc, argv, func, arg);
413 429
 }
414 430
 
  431
+static VALUE
  432
+enumerator_lazy(VALUE obj)
  433
+{
  434
+    struct enumerator *e = enumerator_ptr(obj);
  435
+
  436
+    e->lazy = Qtrue;
  437
+
  438
+    return obj;
  439
+}
  440
+
  441
+static VALUE
  442
+enumerator_map(VALUE obj)
  443
+{
  444
+    struct enumerator *e = enumerator_ptr(obj);
  445
+    struct proc_entry *entry;
  446
+    VALUE entry_obj;
  447
+
  448
+    if (e->lazy) {
  449
+        entry_obj = Data_Make_Struct(rb_cObject, struct proc_entry, 0, RUBY_DEFAULT_FREE, entry);
  450
+        Data_Get_Struct(entry_obj, struct proc_entry, entry);
  451
+        entry->proc = rb_block_proc();
  452
+        entry->type = T_PROC_MAP;
  453
+        rb_ary_push(e->procs, entry_obj);
  454
+        return obj;
  455
+    } else {
  456
+        return rb_call_super(0, 0);
  457
+    }
  458
+}
  459
+
  460
+static VALUE
  461
+enumerator_select(VALUE obj)
  462
+{
  463
+    struct enumerator *e = enumerator_ptr(obj);
  464
+    struct proc_entry *entry;
  465
+    VALUE entry_obj;
  466
+
  467
+    if (e->lazy) {
  468
+        entry_obj = Data_Make_Struct(rb_cObject, struct proc_entry, 0, RUBY_DEFAULT_FREE, entry);
  469
+        Data_Get_Struct(entry_obj, struct proc_entry, entry);
  470
+        entry->proc = rb_block_proc();
  471
+        entry->type = T_PROC_SELECT;
  472
+        rb_ary_push(e->procs, entry_obj);
  473
+        return obj;
  474
+    } else {
  475
+        return rb_call_super(0, 0);
  476
+    }
  477
+}
  478
+
415 479
 /*
416 480
  * call-seq:
417 481
  *   enum.each {...}
@@ -532,7 +596,30 @@ next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
532 596
     struct enumerator *e = enumerator_ptr(obj);
533 597
     VALUE feedvalue = Qnil;
534 598
     VALUE args = rb_ary_new4(argc, argv);
535  
-    rb_fiber_yield(1, &args);
  599
+    VALUE result = i;
  600
+    struct proc_entry *entry;
  601
+    VALUE move_next = Qtrue;
  602
+
  603
+    if (e->lazy) {
  604
+        int j = 0;
  605
+        for (j = 0; j < RARRAY_LEN(e->procs); j++) {
  606
+            Data_Get_Struct(RARRAY_PTR(e->procs)[j], struct proc_entry, entry);
  607
+            switch ((enum proc_entry_type) entry->type) {
  608
+                case T_PROC_MAP:
  609
+                    result = rb_funcall(entry->proc, rb_intern("call"), 1, result);
  610
+                    break;
  611
+                case T_PROC_SELECT:
  612
+                    if (move_next)
  613
+                        move_next = rb_funcall(entry->proc, rb_intern("call"), 1, result);
  614
+                    break;
  615
+            }
  616
+        }
  617
+        if (move_next)
  618
+            rb_fiber_yield(1, &result);
  619
+    } else {
  620
+        rb_fiber_yield(1, &args);
  621
+    }
  622
+
536 623
     if (e->feedvalue != Qundef) {
537 624
         feedvalue = e->feedvalue;
538 625
         e->feedvalue = Qundef;
@@ -1202,6 +1289,9 @@ Init_Enumerator(void)
1202 1289
     rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
1203 1290
     rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
1204 1291
     rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
  1292
+    rb_define_method(rb_cEnumerator, "lazy", enumerator_lazy, 0);
  1293
+    rb_define_method(rb_cEnumerator, "map", enumerator_map, 0);
  1294
+    rb_define_method(rb_cEnumerator, "select", enumerator_select, 0);
1205 1295
     rb_define_method(rb_cEnumerator, "each_with_index", enumerator_each_with_index, 0);
1206 1296
     rb_define_method(rb_cEnumerator, "each_with_object", enumerator_with_object, 1);
1207 1297
     rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, -1);
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.