http://qs321.pair.com?node_id=748833

roboticus has asked for the wisdom of the Perl Monks concerning the following question:

Hello, all--

I've got a question that I can't answer[1]. Given this code:

#!/usr/bin/perl -w use strict; use warnings; while (<DATA>) { if ($. % 10 == 1) { print; for my $cnt (0 .. 3) { <DATA>; # my $t=<DATA>; print; # print $t; } } } __DATA__ Line 0001 Line 0002 Line 0003 Line 0004 Line 0005 Line 0006 Line 0007 Line 0008 Line 0009 Line 0010 Line 0011 Line 0012 Line 0013 Line 0014 Line 0015 Line 0016

I expected to see this:

Line 0001 Line 0002 Line 0003 Line 0004 Line 0005 Line 0011 Line 0012 Line 0013 Line 0014 Line 0015

So imagine my surprise when I saw this:

Line 0001 Line 0001 Line 0001 Line 0001 Line 0001 Line 0011 Line 0011 Line 0011 Line 0011 Line 0011

By changing the two commented lines, I get the desired output, so my program works now. But I can't rest until I know what's causing this behavior. I expect that it has something to do with the nested while and for loops and some interaction with $_.

[1] I've tried to answer it myself, but have been unsuccessful.

Can someone whack me with a cluestick? I'd really appreciate it...

Update: Thanks to everyone who replied. I updated my script and added it to the Code Catacombs.

...roboticus

Replies are listed 'Best First'.
Re: Hmmm: while(), file handle and $_ gotcha
by moritz (Cardinal) on Mar 06, 2009 at 12:41 UTC
    Bare <DATA> outside a while doesn't assign to $_, which is why the output doesn't change in the inner loop. It's the while that does the rewriting trick to while (defined($_=<DATA>)) { ...}, as described in perlsyn.
      moritz:

      OK, that explains it. (I had an incorrect mental model: I was thinking that <FH> always put the data somewhere, using $_ unless otherwise specified. I don't know how I came to think that, as in hindsight that would be clearly stupid. Oh, well--live and learn!)

      So the correct fix to my program would is to change the inner loop from:

      for my $cnt (0 .. 3) { my $t=<DATA>; print $t; }

      to

      for my $cnt (0 .. 3) { $_=<DATA>; print; }

      so I can avoid unnecessary variables and clutter.

      Thank you!

      ...roboticus
        or
        for my $cnt (0 .. 3) { print scalar(<DATA>); }

        Hold up. How does one variable - two, if you did what I do and use a variable for the outer read as well - equate to clutter? I'd probably remove the unused $cnt if I was going to knock clutter out.

        use Modern::Perl; while (my $line = <DATA>) { if ( $. % 10 == 1 ) { print $line; } for (0 .. 3) { my $important_line = <DATA>; print $important_line; } }

        I realize this is a matter of personal aesthetics, of course. I was just a little confused how declaring and using a variable (and avoiding weird behavior from <DATA>) was better than declaring an unused variable and getting the weird behavior.

Re: Hmmm: while(), file handle and $_ gotcha
by repellent (Priest) on Mar 06, 2009 at 17:54 UTC
    Just want to point out that (nested) while (<WHATEVER>)'s clobber $_ because of no localization/lexicalization:
    while (<DATA>) { print; # $_ is "line 0001\n" open(my $FH, "<", "file") or die($!); while (<$FH>) { ... } # $_ was clobbered by: while (<$FH>) } __DATA__ line 0001

    For a better explanation, read Sins of Perl Revisited.