Enumerable#zip with File IO

Hello,

I found strange behavior of Enumerable#zip, which I could not understand
why.
Appreciate if anyone can explain.

I wrote a short script below to see how Enumerable#zip method works with
File IO.

  file = open( ARGV[0], "r" )
  [1].zip( file.each ) do |n,l| puts "#{n}: #{l}" end
  [2].zip( file.each ) do |n,l| puts "#{n}: #{l}" end

Then, I provided it with a text file:

111
222
333

I expected that its output would be:

1: 111
2: 222

but actually got:

1: 111
2: 333

It looked like the second line of input was accidentally discarded.

If I replaced the arrays with ranges like:

  (1..1).zip( file.each ) do |n,l| puts "#{n}: #{l}" end
  (2..2).zip( file.each ) do |n,l| puts "#{n}: #{l}" end

then I got the expected result.

Ruby version is ruby 1.9.3p327 (2012-11-10 revision 37606)
[x86_64-darwin12.2.1].

Thanks in advance,
Hayashi

Hi,

this simply makes no sense. When you go through each line "by hand", the
"zip" is completely useless. You might as well write

file = open( ARGV[0], "r" )
puts "#{1}: #{file.readline}"
puts "#{2}: #{file.readline}"
...

I guess what you actually want is something like this:

File.foreach(ARGV[0]).with_index do |line, i|
  puts "#{i + 1}: #{line}"
end

···

--
Posted via http://www.ruby-forum.com/.

Yes I agree that it makes no sense, but I wanted to see how zip method
works with File IO with a simple experimental code.
My point is why the second line of input is discarded.

···

2012/12/12 Jan E. <lists@ruby-forum.com>

Hi,

this simply makes no sense. When you go through each line "by hand", the
"zip" is completely useless. You might as well write

file = open( ARGV[0], "r" )
puts "#{1}: #{file.readline}"
puts "#{2}: #{file.readline}"
...

I guess what you actually want is something like this:

File.foreach(ARGV[0]).with_index do |line, i|
  puts "#{i + 1}: #{line}"
end

--
Posted via http://www.ruby-forum.com/\.

That might be an artifact of unhealthy using of the Enumerator because
you do not really iterate through the whole file but you use it twice
on the same file. Chances are that the Enumerator fetches the next
item before it is actually uses and #zip just breaks out of the loop
prematurely. In your example you'd rather want

[1, 2].zip( file.each ) do |n,l| puts "#{n}: #{l}" end

Or you want to reverse order and do

file.zip([1, 2]) do |l, n| puts "#{n}: #{l}" end

Kind regards

robert

···

On Wed, Dec 12, 2012 at 12:51 PM, 林彰史 <hayashi.akifumi.sub@gmail.com> wrote:

Yes I agree that it makes no sense, but I wanted to see how zip method works
with File IO with a simple experimental code.
My point is why the second line of input is discarded.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Jan, Robert, yes, but that's not the point.

The same happens with a StringIO:

irb(main):010:0> require 'stringio'
=> true
irb(main):014:0> io = StringIO.new "a\nb\nc\nd\ne"
=> #<StringIO:0x10aee00>
irb(main):015:0> [1].zip(io.each){|a,b| p a,b }
1
"a\n"
=> nil
irb(main):016:0> io.read
=> "c\nd\ne"

I suggest you report a bug on http://bugs.ruby-lang.org/, as this
doesn't look like the correct behavior.

-- Matma Rex

I won't do that because first it's an abuse of #zip and second I am
not convinced yet that the behavior is caused by the way streams work.

Kind regards

robert

···

On Wed, Dec 12, 2012 at 7:11 PM, Bartosz Dziewoński <matma.rex@gmail.com> wrote:

Jan, Robert, yes, but that's not the point.

The same happens with a StringIO:

irb(main):010:0> require 'stringio'
=> true
irb(main):014:0> io = StringIO.new "a\nb\nc\nd\ne"
=> #<StringIO:0x10aee00>
irb(main):015:0> [1].zip(io.each){|a,b| p a,b }
1
"a\n"
=> nil
irb(main):016:0> io.read
=> "c\nd\ne"

I suggest you report a bug on http://bugs.ruby-lang.org/, as this
doesn't look like the correct behavior.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Thanks all,

I have tried the same script with JRuby 1.7.0 also, and I got:

1: 111
2:

Not only the second line but also the third line disappears.
Replacing arrays with ranges did not change the output.
I checked StringIO also and got the same.

So, my conclusion is that Enumerable#zip can be applied to IO
Enumerable object only once, as Robert pointed out. If I accidentally
do it twice, the result of the second application is implementation
dependent.
This is what I did not know.

Regards,
Hayashi

···

2012/12/13 Robert Klemme <shortcutter@googlemail.com>

On Wed, Dec 12, 2012 at 7:11 PM, Bartosz Dziewoński <matma.rex@gmail.com> > wrote:
> Jan, Robert, yes, but that's not the point.
>
> The same happens with a StringIO:
>
> irb(main):010:0> require 'stringio'
> => true
> irb(main):014:0> io = StringIO.new "a\nb\nc\nd\ne"
> => #<StringIO:0x10aee00>
> irb(main):015:0> [1].zip(io.each){|a,b| p a,b }
> 1
> "a\n"
> => nil
> irb(main):016:0> io.read
> => "c\nd\ne"
>
> I suggest you report a bug on http://bugs.ruby-lang.org/, as this
> doesn't look like the correct behavior.

I won't do that because first it's an abuse of #zip and second I am
not convinced yet that the behavior is caused by the way streams work.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/