From: Florian Frank Date: 2002-11-17T05:18:40+09:00 Subject: Re: ruby-dev summary 18613-18710 On 2002-11-16 23:18:47 +0900, Massimiliano Mirra wrote: > You also needed the #[] method, that's why... I needed #[] only for the elements of the enumerable not for the enumerable that Enumerable#zip iterates over aka self. > > You can get strange results if you use "\n" in a string because "each" > > iterates over the lines of a string: [...] > Tried that with hashes already? :-) I just did: h = Hash[*(1..10)] ==>{5=>6, 1=>2, 7=>8, 3=>4, 9=>10} h.zip ==>[[5, 1, 7, 3, 9], [6, 2, 8, 4, 10]] h.zip == [h.keys, h.values] ==>true > For zip to work as an Enumerable's method, you have to rely on #each > only. I did rely on Enumerable#each only. The reason I used #each at all is that I wanted to zip an arbitrary amount of "lists". In Haskell its only possible to zip two or three "lists" with the standard library functions. In my Ruby zip it's in addition possible to zip over every object that understands #[]. There is a difference between the collection of "lists" and the "lists". > Just use Enumerable#to_a and you'll have the elements like > #each would have passed them to you. I know. > module Enumerable > def zip(*enumerables) > zipped = [] > # was like this: > # all = [self, *enumerables] > # now is: > all = [self.to_a, *enumerables.collect {|e| e.to_a}] > max_length = all.collect {|a| a.length}.max > > 0.upto(max_length-1) do |i| > zipped << all.collect {|a| a[i]} > end > zipped > end > end That's another possibility. The construction of "all" wastes a lot of memory in some cases, but it is perhaps more flexible because the arguments only have to be enumerables. I don't know what happens more often that an object can #[] or includes Enumerable and/or can #each. If it can't #[] you can always do #to_a to them before using zip. Your implementation would probably be better for a module method: def Enumerable.zip(*enumerables) zipped = [] all = *enumerables.collect { |e| e.to_a } max_length = all.collect { |a| a.length }.max || 0 0.upto(max_length - 1) do |i| zipped << all.collect { |a| a[i] } end zipped end > You needn't obey String#each default blindly, either: > > $/ = 'o' You can't shock me -- I confess: I'm a perl programmer. *g* > p "one\ntwo\n".zip("three\nfour\n") > #=> [["o", "three\nfo"], ["ne\ntwo", "ur\n"], ["\n", nil]] To pass another iterator has perhaps an advantage, this comes to my mind: tree.zip(:preorder) tree.zip(:inorder) tree.zip(:postorder) -- It makes no difference who you vote for - the two parties are really one party representing four percent of the people. -- Gore Vidal