For Loop and Lexically Scoped Variables
Version #1
use warnings;
use strict;
my $count = 4;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
if (not defined($count)) {
print "Count not defined\n";
}
else {
print "Count = $count\n";
}
This prints:
1
2
3
4
5
6
4
Why? Because the for
loop creates its own lexically scoped version of $count
inside its block.
Version #2
use warnings;
use strict;
my $count;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
if (not defined($count)) {
print "Count not defined\n";
}
else {
print "Count = $count\n";
}
1
2
3
4
5
6
Count not defined
Whoops! I wanted to capture the exit value of $count
, but the for
loop had it's own lexically scoped version of $count
!. I just had someone spend two hours trying to track down this bug.
Version #3
use warnings;
use strict;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
print "That's all folks!\n";
This gives me the error Global symbol "$count" requires explicit package name at line 5.
But, I thought $count
was automatically lexically scoped inside the for
block. I开发者_Go百科t seems like that only occurs when I've already declared a lexically scoped version of this variable elsewhere.
What was the reason for this behavior? Yes, I know about Conway's dictate that you should always use my
for the for
loop variable, but the question is why was the Perl interpretor designed this way.
In Perl, assignment to the variable in the loop is always localized to the loop, and the loop variable is always an alias to the looped over value (meaning you can change the original elements by modifying the loop variable). This is true both for package variables (our
) and lexical variables (my
).
This behavior is closest to that of Perl's dynamic scoping of package variables (with the local
keyword), but is also special cased to work with lexical variables (either declared in the loop or before hand).
In no case though does the looped over value persist in the loop variable after the loop ends. For a loop scoped variable, this is fairly intuitive, but for variables with scope beyond the loop, the behavior is analogous to a value localized (with local
) inside of a block scope created by the loop.
for our $val (1 .. 10) {...}
is equivalent to:
our $val;
my @list = 1 .. 10;
my $i = 0;
while ($i < @list) {
local *val = \$list[$i++];
# loop body
}
In pure perl it is not possible to write the expanded lexical version, but if a module like Data::Alias
is used:
my $val;
my @list = 1 .. 10;
my $i = 0;
while ($i < @list) {
alias $val = $list[$i++];
# loop body
}
Actually, in version #3 the variable is "localized" as opposed to lexically scoped.
The "foreach" loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn. If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with "my", it uses that variable instead of the global one, but it's still localized to the loop. This implicit localisation occurs only in a "foreach" loop.
In any case, you will not be able to access the loop variable from that stlye of for
-loop outside the loop. But you could use the other style (C-style) for
-loop:
my $count;
for ($count=1; $count <= 8; $count++) {
last if $count == 6;
}
... # $count is now 6.
Why? Because the
for
loop creates its own lexically scoped version of$count
inside its block.
This is wrong. If you had written for my $count (...) { ... }
it would be true, but you didn't. Instead, if $count
is already a global, it's localized -- the global that already exists is set to new values during the execution of the loop, and set back when it's done. The difference should be clear from this:
our $x = "orig";
sub foo {
print $x, "\n";
}
foo();
for $x (1 .. 3) {
foo();
}
for my $x (1 .. 3) {
foo();
}
The output is
orig
1
2
3
orig
orig
orig
The first for
loop, without my
, is changing the value of the global $x
that already exists. The second for
loop, with my
, is creating a new lexical $x
that isn't visible outside the loop. They're not the same.
This is also why example #3 fails -- since there isn't a lexical $count
in scope and you haven't declared that you intend to touch the package global $count
, strict 'vars'
stops you in your tracks. It's not really behaving any differently for a for
loop than anything else.
精彩评论