In Perl, why does the `while(<HANDLE>) {...}` construct not localize `$_`?
What was the design (or technical) reason for Perl not automatically localizing $_
with the following syntax:
while (<HANDLE>) {...}
Which gets rewritten as:
while (defined( $_ = <HANDLE> )) {...}
All of the other constructs that implicitly write to $_
do so in a localized manner (for/foreach
, map
, grep
), but with while
, you must explicitly localize the variable:
local $_;
while (<HANDLE>) {...}
My guess is that it has something to do with using Perl in "Super-AWK" mode with command line switches, but that might be wrong.
So if anyone knows (or better yet was involved in the language design discussion), could you share with us the reasoning behind this behavior? More specifically, why was allowing the value of $_
to persist outside of the loop deemed important, despite the bugs it can cause (which I tend to see all over the place on SO and in othe开发者_如何学Pythonr Perl code)?
In case it is not clear from the above, the reason why $_
must be localized with while
is shown in this example:
sub read_handle {
while (<HANDLE>) { ... }
}
for (1 .. 10) {
print "$_: \n"; # works, prints a number from 1 .. 10
read_handle;
print "done with $_\n"; # does not work, prints the last line read from
# HANDLE or undef if the file was finished
}
From the thread on perlmonks.org:
There is a difference between
foreach
andwhile
because they are two totally different things.foreach
always assigns to a variable when looping over a list, whilewhile
normally doesn't. It's just thatwhile (<>)
is an exception and only when there's a single diamond operator there's an implicit assignment to$_
.
And also:
One possible reason for why
while(<>)
does not implicitly localize$_
as part of its magic is that sometimes you want to access the last value of$_
outside the loop.
Quite simply, while
never localises. No variable is associated with a while
construct, so it doesn't have even have anything to localise.
If you change some variable in the while
loop expression or in a while
loop body, it's your responsibility to adequately scope it.
Speculation: Because for
and foreach
are iterators and loop over values, while while
operates on a condition. In the case of while (<FH>)
the condition is that data was read from the file. The <FH>
is what writes to $_
, not the while
. The implicit defined()
test is just an affordance to prevent naive code from terminating the loop on a read of false value.
For other forms of while
loops, e.g. while (/foo/)
you wouldn't want to localize $_
.
While I agree that it would be nice if while (<FH>)
localized $_
, it would have to be a very special case, which could cause other problems with recognizing when to trigger it and when not to, much like the rules for <EXPR>
distinguishing being a handle read or a call to glob
.
As a side note, we only write while(<$fh>)
because Perl doesn't have real iterators. If Perl had proper iterators, <$fh>
would return one. for
would use that to iterate a line at a time rather than slurping the whole file into an array. There would be no need for while(<$fh>)
or the special cases associated with it.
精彩评论