in reply to Just when you thought you'd got it: 'foreach $f (@foo)' vs 'foreach (@foo)'

I would recommend the idiom:

See: Auto-localization of $_ in while(<>)
  • Comment on Re: Just when you thought you'd got it: 'foreach $f (@foo)' vs 'foreach (@foo)'
  • Download Code

Replies are listed 'Best First'.
Re^2: Just when you thought you'd got it: 'foreach $f (@foo)' vs 'foreach (@foo)'
by gone2015 (Deacon) on Oct 16, 2008 at 10:28 UTC
    I would recommend the idiom:
    while (local $_ = <$FH>)

    This is certainly best practice -- it stops the while from mangling the caller's precious $_.

    Unfortunately, being a good citizen doesn't prevent some mad person from running amuck with an Uzi and blowing away the firstborn.

    Consider:

    use strict ; use warnings ; my $mock = "I\nII\nIII\nIV" ; open my $FH, '<', \$mock ; $_ = 'firstborn' ; while (local $_ = <$FH>) { chomp ; print "$_=" ; tr/IV/iv/ ; amuck() ; print "$_ " ; } ; if (!defined($_)) { $_ = '*undef*' ; } ; print " -- $_\n" ; sub amuck { $_ = '!!!' } ;
    which gives:
      I=!!! II=!!! III=!!! IV=!!!   -- firstborn
    
    so we managed to keep the firstborn safe, but the loop is littered with bodies.

    Of course, without the local $_, ie: the common or garden:

    while (<$FH>)
    everybody gets it:
       I=!!! II=!!! III=!!! IV=!!!   -- *undef*

    Better is to lexically scope the $_, thus:

    while (my $_ = <$FH>)
    so that we can still implicitly use $_ in the loop, AND it now works (hurrah!):
       I=i II=ii III=iii IV=iv   -- !!!
    OK. The firstborn has gone, but that serves you right for leaving it unattended outside in the push-chair.

      The use of my on Perl special variables didn't come till a recent version of Perl, I believe.

      Nevertheless, I think it is a matter of usage whether to lexically scope my $_ within while or dynamically scope it local $_. I can foresee having an amuck() function wanting to read $_ instead of clobbering it.

      When I see code like $_ = ... alarm bells in me start ringing! ... and I drone into the need to localize ... :)
Re^2: Just when you thought you'd got it: 'foreach $f (@foo)' vs 'foreach (@foo)'
by blazar (Canon) on Oct 16, 2008 at 10:18 UTC

    I was about to tell you that I'm not sure whether I would recommend it because the

    while (<>) {...}

    is associated with quite a lot of dwimmy magic that would would vanish if you amend the construct that way, but when I was about to give you an example it turned out that it was actually a counter-example showing I was plainly wrong:

    pilsner:~ [11:58:42]$ perl -MO=Deparse -e 'while (<>) {}' while (defined($_ = <ARGV>)) { (); } -e syntax OK pilsner:~ [11:59:05]$ perl -MO=Deparse -e 'while (local $_=<>) {}' while (defined(local $_ = <ARGV>)) { (); } -e syntax OK

    Thus perl is smart enough to still add the definedness test even in that case: I stand corrected, and you taught me something new. I am fairly sure this was not the case with all perls, but I have now tested with 5.8.8 which is the oldest one I have currently access to, and it supports this feature. I don't know when it was actually introduced. I used to believe that it would only work with a variable assignment, but not if a local (or anything else) was preprended to it...

    I still feel like claiming that in most cases using while (<>) {...} responsibly would be enough.

    --
    If you can't understand the incipit, then please check the IPB Campaign.
      Yes, the dwimmy magic that adds the definedness test is still present. That's one thing I noticed, much to my relief.

      The issue with while (<>) { ... } is that the while clobbers $_. This makes debugging harder especially when it is used in some function/module somewhere else.

      Sins of Perl Revisited explains that module authors must be educated to localize $_ before changing it.

      If by "most cases", you mean not using while (<>) { ... } in a function/module, then yeah, I suppose it would be enough. But who's to say that some day, the existing code won't be packaged up wholesale and used elsewhere, causing interesting bugs ;-)