AndyJ has asked for the wisdom of the Perl Monks concerning the following question:

Hello.

So the iterator in a foreach loop is localised; if the variable is previously declared with my "that one instead of the global one is used, but it's still localized to the loop" (according to the Camel book).

So I have the following code:
my $value = "four"; my @array = ( "one", "two", "three" ); foreach $value (@array) { print "foreach value: $value\t"; function(); } sub function { print "function value: $value\n"; }
Why is $value = "four" when my subroutine is called? If it's localised by the foreach loop, shouldn't it get set to the value it is inside the foreach loop? (As is what happens if it's my declaration)

It seems that if both a global and local version of $value exists, then the global one is chosen.

What's happening?

Replies are listed 'Best First'.
Re: foreach localizing variables
by perrin (Chancellor) on Feb 06, 2008 at 14:13 UTC
    Your subroutine is a closure. It continues to point to the $value that existed when the sub was compiled. Instead, you should pass $value to your sub. Most of the time you should pass all needed variables to subs, because you want them to operate independently of their context.
Re: foreach localizing variables
by cdarke (Prior) on Feb 06, 2008 at 15:34 UTC
    $value in the foreach loop is not a real variable, it is an alias to each element in the array. As perrin said, pass the value as a parameter to the subroutine. Global variables are A Bad Thing, except when you really need them (which is not often).

    By the way, things may be clearer if you:
    use strict; use warnings; my $value = "four"; my @array = ( "one", "two", "three" ); foreach my $value (@array) { print "foreach value: $value\t"; function(); } sub function { print "function value: $value\n"; }
    Spot the difference.
      The spotting is easier if you eliminate unnecessary junk...
      $value = "four"; @array = ( "one", "two", "three" ); foreach my $value (@array) { print "foreach value: $value\t"; function(); } sub function { print "function value: $value\n"; }
Re: foreach localizing variables
by shmem (Chancellor) on Feb 07, 2008 at 00:56 UTC
    Scoping, compile time / run time, and global / my issues. my variables are located in space, local variables in time. Package variables are "grandfather local" variables - see my/local, space/time (was: Re: The difference between my and local).

    Your code commented:

    my $value = "four"; # lexical variable my @array = ( "one", "two", "three" ); foreach $value (@array) { # variable $value inside the loop is set # to each element of @array at runtime print "foreach value: $value\t"; function(); } # outer scope lexical $value visible here sub function { # outer scope lexical $value visible here # with its initial value print "function value: $value\n"; }

    Had you used a package global throughout, the outcome would have been different:

    $value = "four"; my @array = ( "one", "two", "three" ); foreach $value (@array) { print "foreach value: $value\t"; function(); } sub function { print "function value: $value\n"; } __END__ foreach value: one function value: one foreach value: two function value: two foreach value: three function value: three

    What about allocating a sub in a block's scope? Let's look at this weird construct:

    my $value = "four"; my @array = ( "one", "two", "three" ); foreach $value (@array) # note outer $value variable { print "foreach value: $value\t"; function(); sub function { print "function value: $value\n"; } } __END__ foreach value: one function value: four foreach value: two function value: four foreach value: three function value: four

    The result is the same, although the sub is allocated inside the for() block scope. Why? Because the sub, on compilation, gets the outer $value, whereas the loop iteration variable is an alias to the outer.

    But what about that?

    my $value = "four"; my @array = ( "one", "two", "three" ); foreach my $value (@array) { print "foreach value: $value\t"; function(); sub function { print "function value: $value\n"; } } __END__ foreach value: one function value: foreach value: two function value: foreach value: three function value:

    How come? Declaring the loop variable as a my variable to the loop block, the inner loop gets that at compile time, and closes over it (it's a closure), and this variable is initialized empty (assignment happens at run time). The inner sub variable is different from the aliased loop variable.

    All this is presented here with a lot of handwaving. The perl sources might provide a clue about what's really happening. Pass arguments to your subroutines to be save.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Thanks for all the explanations.

      Should have mentioned it's not actually my code! I've fixed it up, but just wanted to understand why it's doing what it's doing.

      I'm still a bit confused though...

      I get the fact that the compile time of the subroutine means it will use the initial variable. But does the localised variable not use the same location as the my variable?

      Andrew
        My previous post wasn't quite accurate. The my $value marks that variable as lexical for the current scope, but a for() loop argument context gets its own store ("scratchpad", see perlguts), as is the case for subroutines. So, although at compilation the my() declaration extends into the sub, the variable inside the sub gets allocated on a different scratchpad, but is also marked as lexical (PADMY):
        use Devel::Peek; my $value = "four"; my @array = ( "one" ); warn "(1):\n" ; Dump($value); foreach $value (@array) { warn "(2):\n" ; Dump($value); print "foreach value: $value\t"; function(); sub function { warn "(3):\n" ; Dump($value); print "function value: $value\n"; } } __END__ (1): SV = PV(0x8b83b00) at 0x8b83768 REFCNT = 1 FLAGS = (PADBUSY,PADMY,POK,pPOK) PV = 0x8b992c8 "four"\0 CUR = 4 LEN = 8 (2): SV = PV(0x8b83c20) at 0x8b82c28 REFCNT = 2 FLAGS = (POK,pPOK) PV = 0x8b962c0 "one"\0 CUR = 3 LEN = 4 (3): SV = NULL(0x0) at 0x8b837b0 REFCNT = 2 FLAGS = (PADBUSY,PADMY) foreach value: one function value:

        As you can see, all addresses of $value are different. The my() just tells the compiler that the symbol $value is to be treated as a lexical variable for the scope in which it was declared, but that doesn't mean it has always the same storage (or only one address location). <update> The $value inside the sub inside the for() loop gets its own $value because the outer $value isn't visible in the scope in which it has been compiled.</update> Bare blocks are different:

        use Devel::Peek; my $value = "four"; warn "(1):\n" ; Dump($value); { $value = "five"; warn "(2):\n" ; Dump($value); } __END__ (1): SV = PV(0x8bcdb00) at 0x8bcd75c REFCNT = 1 FLAGS = (PADBUSY,PADMY,POK,pPOK) PV = 0x8bee7d0 "four"\0 CUR = 4 LEN = 8 (2): SV = PV(0x8bcdb00) at 0x8bcd75c REFCNT = 1 FLAGS = (PADBUSY,PADMY,POK,pPOK) PV = 0x8bee7d0 "five"\0 CUR = 4 LEN = 8
        Welcome to The Monastery, btw ;-)

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: foreach localizing variables
by lidden (Curate) on Feb 06, 2008 at 23:00 UTC
    While I agree with the above comments that you should pass the value as a parameter to the subroutine. You can get the behaviour you seem to want by making $value a 'our' variable:
    our $value = "four";