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

Hi fellow monks! I just stumbled across a pehaviour of perl 5.8.0 I can't explain to myself.

I first wanted to write:

while(<>) { chomp; push(@{$para{$_}}, $ARGV); }
but then wrote instead
chomp,push(@{$para{$_}}, $ARGV) for (<>);
because it's shorter.

Unfortunately both versions are not equivalent. $ARGV is always the $ARGV[$#ARGV] in the for loop, while it changes to the current filename in the first loop.

Is this a documented feature?

Replies are listed 'Best First'.
Re: while or for?
by broquaint (Abbot) on Jul 21, 2003 at 10:24 UTC
    The construct while(<>) is special as it iterates on the magical ARGV filehandle and sets $ARGV to the current filename, checking for definedness all the while. Whereas for(<>) just slurps ARGV and iterates over that (which is similar to the while construct, but not quite the same). Also you probably mean to use $_ instead of $ARGV (unless of course you've cheekily abused the global nature of *ARGV and used the scalar slot)1. The while behaviour is well documented in Programming Perl (ver 3, page 82) and in the I/O Operators section of perlop.
    HTH

    _________
    broquaint

    1 addendum - as Skeeve points out $ARGV holds the name of the current filename that is being iterated over, so is not in fact abuse of the scalar slot of *ARGV :)

    update: added clarifications and fixed mistakes

      Okay. Understood.

      > Also you probably mean to use $_ instead of $ARGV

      I really meant $ARGV. I wanted to store, in a hash, for each line the names of the files they occure in.

Re: while or for?
by edan (Curate) on Jul 21, 2003 at 10:57 UTC

    Okay, here's what I found:

    $ perl -mO=Deparse -le 'print "$ARGV $_" for (<>)' foreach $_ (<ARGV>) { print "$ARGV $_"; }

    COMPARE:

    $ perl -mO=Deparse -le 'print "$ARGV $_" while (<>)' print "$ARGV $_" while defined($_ = <ARGV>);

    Since $ARGV is set every time <ARGV> is evaluated, when you do it in a while loop, $ARGV is set correctly for each file in the loop. But using a for loop, you slurp <ARGV> in one shot, then iterate over $_, so $ARGV is set to the last file that was iterated over the last time <ARGV> was evaluated...

    Hope that makes sense...

    Update: I guess I just repeated what broquaint already said, but since I didn't understand what he wrote, I posted this, thinking I had something new to offer. Perhaps someone else will find it easier to understand, too...

    --
    3dan
(jeffa) Re: while or for?
by jeffa (Bishop) on Jul 21, 2003 at 13:34 UTC
    This does not relate directly to your question, but i noticed that you use a hash named %para. Are you wanting to seperate the paragraphs? If so then you simply change $/ to 2 newlines. Here is an example that you may modify to meet your needs:
    use Data::Dumper; my @para = do {local $/ = "\n\n"; <DATA>}; print Dumper \@para; __DATA__ blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Thanks for the hint, but no, I'm not trying to seperate paragraphs. And yes, %para is because I meant paragraph, or to say it completely: ParagraphDef. My files contain a list of (XML-)ParagraphDef names, which I wanted to sort out: Which one is used in which file(s).
Re: while or for?
by antirice (Priest) on Jul 21, 2003 at 13:23 UTC

    There isn't any special case as some may have argued. It is merely the context of <> when it's called. In your for statement, the contents of ARGV are slurped (the list foreach iterates over is built when the loop is constructed...don't believe me? perl -le '$_=4;$_++,print foreach (1..$_)'). Thus $ARGV is set to $ARGV[-1] for the duration of the loop. If you were to change your for statement to the following:

    for (;<>;) { chomp; push(@{$para{$_}}, $ARGV); }

    It will DWYM and work as expected. Now before anyone with B::Deparse tries to deparse it and notices that it optimizes this loop to while (defined($_ = <>)) ..., if you change the loop to for (my $i=0;<>;$i++) ... it will deparse to a for statement but still DWYM.

    Hope this helps.

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

Re: while or for?
by tilly (Archbishop) on Jul 21, 2003 at 21:11 UTC
    Doing something because it's shorter is not a good reason outside of a golf game. Particularly if the two versions have the same number of operations. If you are performing operations, why not make it understandable and take 3 lines to do it? Whitespace is cheap - in fact cheaper than the extra time it takes to mentally parse an unnecessarily complicated line.

    In this vein, see The path to mastery.

      Hey... I'm on training for the next minigolf ;-)

      If I do scripts that should last longer than, say 2 days, I normaly write a clean, commented loop with use strict and use warnings and all the bells and whistles ;-)

      But this was just a quick shot :-) I won't use the script any longer as soon as it did it's work.

      But you are right and I hope that some perl beginners will read this and your comment and learn from it.