for-loopperlvariablesscope

The scope for `my` var in the non C-style for loop


While recently trying to get one algorithm for "Getting indices of matching parentheses". I can get what that algorithm means although with some problems with perl language.

The perl syntax is not obscure and can be got much with man perl... doc.

But I am a bit confused about my behavior with for loop. man perlsyn says:

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 localization occurs only for non C-style loops.

I know that "non C-style loops" mean those not like for (...;...;...){...}.

The 1st sentence can be shown by this which is similar to the example shown in the doc:

$i = 'samba';
# If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop.
for (my $i = 1; $i <= 4; $i++) {
  print "$i\n";
}
print "$i\n";
# 1
# 2
# 3
# 4
# samba

But I can't understand what the 2nd means:

$inner = 'samba';
for ($i = 1; $i <= 4; $i++) {
  $inner = $i + 1;
}
print "inner: $inner\n";
# inner: 5

Here the alleged "local" var $inner seems to modify the outside var, and the "former value" 'samba' can't be "regain"ed.

For the 3rd, we can do some small tweaks for the above example:

$inner = 'samba';
for ($i = 1; $i <= 4; $i++) {
  my $inner = $i + 1;
}
print "inner: $inner\n";
# inner: samba

This works expectedly for "instead of the global one".

How to understand my behavior in for loop, especially for the above 2nd sentence in the quote?


Follow-up clarification with choroba's answer hints: When using the correct "non C-style loop" the 1st and 2nd sentence seem to mean that whether my is used for var has the same effect. But actually it is not that case.

sub foo { print "foo: $x\n"; }

$x = 7;
for $x (1 .. 3) {  # Implicit localisation happens here.
  print "$x\n";
  print "global $::x\n";  # Prints 1 .. 3 correspondingly.
  foo(); # Prints 1 .. 3 correspondingly.
}
print $x;  # Prints 7.

$x = 7;
for my $x (1 .. 3) {  # Implicit localisation happens here.
  print "$x\n";
  print "global $::x\n";  # Always prints 7.
  foo(); # Always prints 7.
}
print $x;  # Prints 7.

This is just the difference between local and my which just means dynamic scope vs lexical scope as the top answer there shows which is also said in the doc.

A local just gives temporary values to global (meaning package) variables. It does not create a local variable. This is known as dynamic scoping. Lexical scoping is done with my ...

The 3rd sentence example can be updated:

$i = 'former';
my $i = 'samba';
for $i (1 .. 4) {
  print "$i\n";
}
# > still localized to the loop
# i.e. uses the outside variable value but maybe there are more than one choices. The doc says to choose "my $i = 'samba';".
print "$i\n"; # not use 'former'
# 1
# 2
# 3
# 4
# samba

follow-up question for ikegami's answer:

If we prepend:

my $x = 7;
for $x (1 .. 3) {  # Implicit localisation happens here.
  print "$x\n";
  print "global $::x\n"; # Prints nothing for $::x.
  foo(); # Prints nothing for $x.
}
print $x;  # Prints 7.

to the above sub foo { print "foo: $x\n"; } ... example, then the latter $::x also can't access the latter defined global var $x = 7;. IMHO my creates one new var which should not influence that global var.

But if we define the latter defined var as our $x = 7; which is one "lexical alias to a package (i.e. global) variable" as the doc says. Then all works as before. What is the reason for that?


Solution

  • Let me start by saying you always want my except when using $_.


    If the variable is preceded with the keyword "my", then it is lexically scoped, and is therefore visible only within the loop.

    Demo:

    our $x = 9;
    
    sub f { say $x; }         # Accesses outer `$x`
    
    for my $x ( 1 .. 3 ) {    # Creates a new `$x` scoped to the loop
       say $x;                # Accesses loop `$x`
       f();
    }
    
    say $x;                   # Accesses outer `$x`
    

    Output:

    1
    9
    2
    9
    3
    9
    9
    

    Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop.

    Demo:

    our $x = 9;
    
    sub f { say $x; }         # Accesses the only `$x`
    
    for $x ( 1 .. 3 ) {       # Backs up and restores the SV associated with `$x`.
       say $x;                # Accesses the only `$x`
       f();
    }
    
    say $x;                   # Accesses the only `$x`
    

    Output:

    1
    1
    2
    2
    3
    3
    9
    

    In other words, the code is functionally equivalent to the following:

    use experimental qw( declared_refs defer refaliasing );
    
    our $x = 9;
    
    sub f { say $x; }              # Accesses the only `$x`.
    
    {
       my \$backup = \$x;          # Backs up the SV associated with `$x`.
       defer { \$x = \$backup; }   # Restores the SV associated with `$x`.
    
       for my $ele ( 1 .. 3 ) {
          \$x = \$ele;             # `$x` is associated with SV from list.
    
          say $x;                  # Accesses the only `$x`.
          f();
       }
    }
    
    say $x;                        # Accesses the only `$x`.
    

    Output:

    1
    1
    2
    2
    3
    3
    9