On Wed, Apr 20, 2005 at 07:02:24AM +0900, nobu.nokada / softhome.net wrote:
> > --- array.c.orig        2005-04-18 00:11:33.000000000 +0200
> > +++ array.c     2005-04-18 00:22:18.000000000 +0200
> > @@ -2074,12 +2074,12 @@
> >         argv[i] = to_ary(argv[i]);
> >      }
> >      if (rb_block_given_p()) {
> > +        VALUE tmp = rb_ary_new2(argc+1);
> > +        RARRAY(tmp)->len = argc + 1;
> >         for (i=0; i<RARRAY(ary)->len; i++) {
> > -           VALUE tmp = rb_ary_new2(argc+1);
> > -
> > -           rb_ary_push(tmp, rb_ary_elt(ary, i));
> > +            RARRAY(tmp)->ptr[0] = RARRAY(ary)->ptr[i];
> >             for (j=0; j<argc; j++) {
> > -               rb_ary_push(tmp, rb_ary_elt(argv[j], i));
> > +                RARRAY(tmp)->ptr[1+j] = rb_ary_elt(argv[j], i);
> >             }
> >             rb_yield(tmp);
> >         }
> 
> `tmp' can be modified or stored anywhere while yielding, so it
> should not be reused.  You can't assume that the size is
> constant, at least.

I see. What about this?


diff -ru ruby.orig/array.c ruby/array.c
--- ruby.orig/array.c	2005-04-20 02:09:10.000000000 +0200
+++ ruby/array.c	2005-04-20 02:22:22.000000000 +0200
@@ -2074,16 +2074,31 @@
 	argv[i] = to_ary(argv[i]);
     }
     if (rb_block_given_p()) {
-	for (i=0; i<RARRAY(ary)->len; i++) {
+	if (rb_multiple_asgn_in_block_p()) {
 	    VALUE tmp = rb_ary_new2(argc+1);
 
-	    rb_ary_push(tmp, rb_ary_elt(ary, i));
-	    for (j=0; j<argc; j++) {
-		rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+	    RARRAY(tmp)->len = argc + 1;
+	    for (i=0; i<RARRAY(ary)->len; i++) {
+		RARRAY(tmp)->ptr[0] = RARRAY(ary)->ptr[i]; 
+		for (j=0; j<argc; j++) {
+		    RARRAY(tmp)->ptr[1+j] = rb_ary_elt(argv[j], i);
+		}
+		rb_yield(tmp);
 	    }
-	    rb_yield(tmp);
+	    return Qnil;
+	} 
+	else {
+	    for (i=0; i<RARRAY(ary)->len; i++) {
+		VALUE tmp = rb_ary_new2(argc+1);
+
+		rb_ary_push(tmp, rb_ary_elt(ary, i));
+		for (j=0; j<argc; j++) {
+		    rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+		}
+		rb_yield(tmp);
+	    }
+	    return Qnil;
 	}
-	return Qnil;
     }
     len = RARRAY(ary)->len;
     result = rb_ary_new2(len);
diff -ru ruby.orig/eval.c ruby/eval.c
--- ruby.orig/eval.c	2005-04-20 02:09:09.000000000 +0200
+++ ruby/eval.c	2005-04-20 02:07:07.000000000 +0200
@@ -4569,6 +4569,30 @@
     return rb_block_given_p();
 }
 
+int
+rb_multiple_asgn_in_block_p()
+{
+    unsigned long arg_len;
+    NODE *list;
+
+    if (!rb_block_given_p())
+	return Qfalse;
+ 
+    if (nd_type(ruby_block->var) != NODE_MASGN)
+	return Qfalse;
+   
+    arg_len = 0;
+    list = ruby_block->var->nd_head;
+    while (list) {
+	arg_len++;
+	list = list->nd_next;
+    }
+
+    if (arg_len > 1)
+	return Qtrue;
+    return Qfalse;
+}
+
 /*
  *  call-seq:
  *     block_given?   => true or false
diff -ru ruby.orig/intern.h ruby/intern.h
--- ruby.orig/intern.h	2005-04-20 02:09:10.000000000 +0200
+++ ruby/intern.h	2005-04-20 01:59:14.000000000 +0200
@@ -174,6 +174,7 @@
 void rb_load _((VALUE, int));
 void rb_load_protect _((VALUE, int, int*));
 NORETURN(void rb_jump_tag _((int)));
+int rb_multiple_asgn_in_block_p _((void));
 int rb_provided _((const char*));
 void rb_provide _((const char*));
 VALUE rb_f_require _((VALUE, VALUE));



The point is allowing efficient parallel iteration in pure Ruby, that
is without things like Joel VanderWerf's enum or callcc-based external
iterators.


batsman@tux-chan:/tmp/ruby$ cat /tmp/bench.rb

(2..6).each do |i|
    a = [10] * 10**i
    b = [20] * 10**i
    t = Time.new
    a.zip(b){|x,y|}
    puts "%9d: %8.5f" % [10**i, Time.new - t]
end

a = [100] * 5
b = [1000] * 5
a.zip(b){|x| p x; x.replace([])}
batsman@tux-chan:/tmp/ruby$ ruby ../bench.rb
      100:  0.00010
     1000:  0.00083
    10000:  0.02530
   100000:  0.15629
  1000000:  4.81401
[100, 1000]
[100, 1000]
[100, 1000]
[100, 1000]
[100, 1000]
batsman@tux-chan:/tmp/ruby$ ./ruby ../bench.rb
      100:  0.00006
     1000:  0.00042
    10000:  0.00711
   100000:  0.05811
  1000000:  0.46238
[100, 1000]
[100, 1000]
[100, 1000]
[100, 1000]
[100, 1000]


-- 
Mauricio FernŠŌdez