On Nov 28, 2007, at 6:00 AM, Austin Ziegler wrote:

>   a.zip(b, c, d, ...)


this iterates a *single* freshly allocated array:

static VALUE
rb_ary_zip(argc, argv, ary)
     int argc;
     VALUE *argv;
     VALUE ary;
{
     int i, j;
     long len;
     VALUE result;

     for (i=0; i<argc; i++) {
	argv[i] = to_ary(argv[i]);
     }
     if (rb_block_given_p()) {
	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;
     }
     len = RARRAY(ary)->len;
     result = rb_ary_new2(len);
     for (i=0; i<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_ary_push(result, tmp);
     }
     return result;
}


when a, b, c, and d are large it's most undesirable to use zip,  
forcing one to use

lists = a,b,c,d
n = lists.map{|list| list.size}.max

(0 ... n).each do |i|
   ai, bi, ci, di = lists.map{|list| list[i]}
   # ...
end


and, when b, c, and d are heterogeneous containers the above code is  
most often impossible - you will first need to convert all the  
containers to lists and then iterate.  for me this is a major gripe  
about ruby that i work around nearly every day as it turns out many  
algorithms, such as those in image processing, revolve around  
iterating multiple containers at once: often in non-linear ways.  one  
way around, without callcc, is just to use threads:



cfp:~ > cat a.rb
require 'thread'
module Enumerable
   def self.each *enumerables, &block
     n = enumerables.size
     done = Object.new
     qs = Array.new(n){ SizedQueue.new 1 }
     ts = Array.new(n){ |i|
       Thread.new{
         Thread.current.abort_on_exception = true
         e, q = enumerables[i], qs[i]
         e.each{|x| q.push x}
         q.push done
       }
     }
     dead = Array.new(n){ false }
     loop do
       values = []
       finished = true
       qs.each_with_index do |q,i|
         if dead[i]
           values << nil
           next
         end
         value = q.pop
         if value == done
           dead[i] = true
           values << nil
         else
           values << value
         end
       end
       return enumerables if dead.all?
       block.call *values
     end
   end
end

a = 0,1,2
b = 2,3,4
c = 5,6,7

lists = a,b,c

Enumerable.each(a,b,c){|x,y,z| p [x,y,z]}



cfp:~ > ruby a.rb
[0, 2, 5]
[1, 3, 6]
[2, 4, 7]


but this, i think we can all agree, is slow and fugly.  esp compared  
to something like this

   http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/doc/vigra/ 
group__ImageIterators.html

external iterators are quite useful imho.

kind regards.


a @ http://codeforpeople.com/
--
it is not enough to be compassionate.  you must act.
h.h. the 14th dalai lama