What is the default scope of foreach loop in Perl?

2019-01-20 08:09发布

问题:

In Perl, does using 'my' within a foreach loop have any effect? It seems that the index variable is always local whether or not 'my' is used. So can you drop the 'my' within the foreach loop and still have private scope within the body of the loop?

As can be seen, using the 'for' loop there is a difference between using / not using 'my':

use strict; 
use warnings; 

my ($x, $y) = ('INIT', 'INIT'); 

my $temp = 0; 

for ($x = 1; $x < 10; $x++) {
 $temp = $x+1; 
}

print "This is x: $x\n";   # prints 'This is x: 10'. 

for (my $y = 1; $y < 10; $y++) {
 $temp = $y+1; 
}

print "This is y: $y\n";   # prints 'This is y: INIT'. 

But on foreach it does not seem to have an effect:

my ($i, $j) = ('INIT', 'INIT'); 

foreach $i (1..10){
    $temp = $i+1;
}

print "\nThis is i: $i\n";   # prints 'This is i: INIT'. 



foreach my $j (1..10){
    $temp = $j+1;
}

print "\nThis is j: $j\n";   # prints 'This is j: INIT'. 

回答1:

From http://perldoc.perl.org/perlsyn.html#Foreach-Loops:

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.



回答2:

Hans linked to the documentation describing how the variable in the foreach loop is scoped. The distinction is subtle but it could be important:

sub f { return $i }
$i = 4;
$m = $n = 0;

foreach    $i (1 .. 10) { $m += f() }
foreach my $i (1 .. 10) { $n += f() }

print "Result with localization:    $m\n";    #  ==>  55
print "Result with lexical scoping: $n\n";    #  ==>  40


回答3:

One of the most painful discoveries about Perl I made was that the "my" variable on a foreach loop is not a local copy. I found myself frustrated to discover my array was being trashed.

The only way around seems to be:


foreach ( @array ) {
  my $element = $_; # make a local copy
  ...
}

If you look at perldoc it states: "the foreach loop index variable is an implicit alias for each item in the list that you're looping over" and "If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop".



回答4:

I just did some extra research on this interesting issue.

foreach doesn't care if you do "my $y;" before the loop either. The iterator will still only change within the scope:

my $y;
foreach $y (1..10) {}

The $y will change within the foreach scope, but when it leaves, it reverts back to whatever it was before the loop.

I had thought that perhaps foreach automatically does a "my $y;" before it starts running, so I tried strict mode, but that explanation doesn't cut it because then it still demands:

foreach my $y (1..10) {}

as opposed to:

foreach $y (1..10) {}

...which one would think weren't necessary if "foreach $y" was functionally the same as "foreach my $y".



回答5:

The iterator in a foreach loop is effectively local to the loop. That said, it does directly hold the value from the list in what is effectively a call by reference as opposed to by value. Thus, if you operate on the value while in the loop it will change the value in the list. However, the iterator variable is only defined in reference to the loop:

@names = qw(Kirk Spock Bones);

$officer = "Riker";
print "@names $officer\n"; # will print Kirk Spock Bones Riker

foreach $officer (@names) {
     $officer .= "y";
}

print "@names $officer\n"; # will print Kirky Spocky Bonesy Riker

So, the iterator, in this case $officer, is effectively preceded by a 'my' in terms of retaining any value it had before the foreach loop. However, if the values it holds is changed that change will pass back to the list you are iterating over.