Hi --

On Sat, 16 Nov 2002, Florian Frank wrote:

> On 2002-11-16 06:07:50 +0900, dblack / candle.superlink.net wrote:
> > > module Enumerable
> > >
> > > 	def zip(&block)
> > > 		block ||= lambda { |*tuple| tuple }
> > > 		size = nil
> > > 		map { |x| x.size }.each do |s|
> > > 			size ||= s
> > > 			s == size or raise "zip with unequal length lists"
> > > 		end
> > > 		result = []
> > > 		i = 0
> > > 		catch(:stop) do
> >
> > (Don't you like regular Ruby iterators? :-)
>
> I like them, but I can't use an iterator here. I needed to get the i-th
> element of every object l in the enumerable self. It would be better to
> use while instead.

If you want the i-th element of each object, just iterate through the
objects and grab the i-th element :-)  In fact you can save yourself a
lot of typing.  Here's a rewrite:

  module Enumerable
    def zip(&block)
      block ||= lambda { |*tuple| tuple }
      if map {|e| e.size}.uniq.size > 1
	raise "zip with unequal length lists"
      else
	(0...size).map {|i| block.call(*map {|l| l[i]})}
      end
    end
  end

Mind you, I'm not currently a fan of the exception for unequal
lengths, and I actually prefer this:

  ary.zip(ary2, ary3)

to this:

  [ary1, ary2, ary3].zip

but anyway, you can definitely get things more concise, for your
version, by letting Ruby doing the work for you.

> > What would the effect be of calling #zip on a non-Array Enumerable,
> > for example a String?
> >
> >   "abcde".zip   # => ?
>
> "abcde".zip
>     ==>[[97], [98], [99], [100], [101]]
>
> The index of a string is a number. This only works because numbers
> happen to have a "size" method. This nice property doesn't hold for your
> example either:
>
> [[1,2,3],[2,3,4]].zip.unzip == [[1,2,3],[2,3,4]]
>     ==>true
>
> You can get strange results if you use "\n" in a string because "each"
> iterates over the lines of a string:
>
> "abcdef\nABCDEFG".zip
>     ==>[[97, 65], [98, 66], [99, 67], [100, 68], [101, 69], [102, 70], [10, 71]]
>
> Strings aren't a good example of a collection anyway. ;)

Heh -- maybe not, but that might also indicate that Enumerable-ness
isn't a good sign of zip-ableness.  Actually one reason I prefer:

  ary.zip(ary2, ary3)

is that there's an equivalent for other Enumerables:

  "abc".zip("def","ghi")

       # => [[97, 100, 103], [98, 101, 104], [99,102, 105]]

Yes, you can do this:

  [ "abc","def","ghi" ].zip

but then you're calling zip on an array, not a string -- which then
re-raises the question of why bother creating Enumerable#zip instead
of Array#zip.


David

-- 
David Alan Black
home: dblack / candle.superlink.net
work: blackdav / shu.edu
Web:  http://pirate.shu.edu/~blackdav