What kind of localization does occur in a “foreach

2019-06-28 08:56发布

问题:

From perldoc perlsyn on the topic of Foreach Loops:

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.

But consider this example:

use Devel::Peek;
my $x = 1;
Dump $x;
for $x ( 1 ) { Dump $x }

SV = IV(0x8117990) at 0x8100bd4
  REFCNT = 1
  FLAGS = (PADBUSY,PADMY,IOK,pIOK)
  IV = 1
SV = IV(0x8117988) at 0x8100bf8
  REFCNT = 2
  FLAGS = (IOK,READONLY,pIOK)
  IV = 1

It seems like these are not the same variables. Is it an error in docs, or am I missing anything?

回答1:

Every rule needs its exception, and this is one. In a for loop, if the loop variable is a lexical (declared with my), Perl will create a new lexical aliased to the current item in the loop. The OP code could be written as follows:

use Data::Alias 'alias';

my $x = 1;
for (2..3) {
    alias my $x = $_;
    # note that $x does not have dynamic scope, and will not be visible in 
    # subs called from within the loop
    # but since $x is a lexical, it can be closed over
}

Edit: previous example was in Perl pseudocode, answer revised for clarity.



回答2:

Properly, it should be called aliasing, to avoid confusion with local(). There are various modules on CPAN that let you do aliasing in other circumstances, too.



回答3:

First, realize that there are global (package scoped) variables and there are lexical variables. They can have the same name. From now own I'll refer to the global $x by its fully qualified name $::x which shorthand for "the global $x package main".

For backwards compatibility, for loops use localized globals unless you tell it otherwise, or $x has already been declared lexical (didn't realize that). So for $x (2..3) {} refers to a localized $::x. for my $x (2..3) {} refers to a lexical $x. In both cases they're scoped inside the loop, sort of like:

for (2..3) {
    my $x = $_;
    ...
}

Except $_ is aliased to $x, not copied.

I believe the above explains why you get different scalars when you use Devel::Peek. Since there is no equivalent of local() for a lexical it is probably declaring a new lexical on the lexical pad inside the for loop, just like the code above.

local($x) also changes the underlying SV because it is doing something like:

my $original = $x;
my $new;
*::x = \$new;

...

*::x = \$original;

ie. making a new scalar, plugging it into the $x slot on the symbol table, and then restoring the old scalar when when the scope is done.

But that's really just supposition. You'd have to dig into the code to find out.

The docs saying that a lexical is "localized to the loop" should not be taken to mean the literal local(). The term local is confusing because other languages, and even Perl programmers, use it interchangeably to mean local() and "lexical scope". local() would probably better be termed temp(). So I think the docs are just being a little sloppy with their terminology there.