http://www.perlmonks.org?node_id=236433

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

From perlsub:
Variables declared with "my" are not part of any package and are therefore never fully qualified with the package name.
and again:
A note about "local()" and composite types is in order. Something like "local(%foo)" works by temporarily placing a brand new hash in the symbol table. The old hash is left alone, but is hidden "behind" the new one.
and:
It's also worth taking a moment to explain what happens when you "local"ize a member of a composite type (i.e. an array or hash element). In this case, the element is "local"ized by name. This means that when the scope of the "local()" ends, the saved value will be restored to the hash element whose key was named in the "local()", or the array element whose index was named in the "local()".
So, if I say my @foo = (0..9); then say local $foo[3] = 'bar'; that a symbol table entry is created? that it is still ignored for every other element of @foo, but that it overrides it my() equivelent element since it, specifically, was local()ized? Ok, this is deeply wierd. Somebody help me get this one? And please feel free to email me at ydbxmhc@yahoo.com, okay? This one's hurting my brain. :) Paul

Replies are listed 'Best First'.
Re: local element of my() array????
by Abigail-II (Bishop) on Feb 19, 2003 at 01:16 UTC
    Well, you basically can localize anything that you cannot my(). You can my() arrays, but you can't my() array *elements*. So, it shouldn't come as a surprise you can localize array elements.

    Alternatively, think of it this way. my() is a compile time thing. local() is a run time thing. (Some handwaving is done). Are the array elements created at compile time? No, although the array itself can. Hence, the array can be my()ed, but the elements can only be local()ized.

    Abigail

Re: local element of my() array????
by BrowserUk (Patriarch) on Feb 18, 2003 at 23:27 UTC

    This demonstrates that your interpretation is correct...

    #! perl -slw use strict; my @foo = 1..9; { local $foo[3] = 'modified'; print "@foo"; } print "@foo"; __END__ C:\test>temp 1 2 3 modified 5 6 7 8 9 1 2 3 4 5 6 7 8 9

    ...now what's the problem:).

    It's really quite clever and very useful in a few situations.


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

      My only problem is a lack of full understanding. If I only sort of think I get it, I'll screw something up. :)

      For example, I can't say

      
        my @x;
        local @x;
      
      
      because local requires a package variable such as @::x to localize, and my @x isn't in any package, because it's lexical. But I *can* say
      
        my @x;
        local $x[0];
      
      
      What's going on there? I just need an explanation of the magic that's handling that, because otherwise I have no clue what's happening, and the world becomes a dark and mysterious place. :)
Re: local element of my() array????
by steves (Curate) on Feb 18, 2003 at 23:27 UTC

    From The Camel, section 29.2.87 local

    You can use local to give temporary values to individual elements of arrays and hashes, even lexically scoped ones:
    if ($protected) { local $SIG{INT} = 'IGNORE'; precious(); # no interrupts during this function } # previous handler (if any) restored

    The example is not lexically scoped but the note applies to your situation, as I found when I wrote this test:

    my @foo = (0..9); print "[1] ", join('|', @foo), "\n"; local $foo[3] = 100; print "[2] ", join('|', @foo), "\n";

    I learn something new here every day ...

Re: local element of my() array????
by xmath (Hermit) on Feb 18, 2003 at 23:30 UTC
    First you should realize that localizing vars and localizing array/hash elements work differently. The former uses the package symbol table (and can therefore not be used on 'my' vars, or referenced vars, etc). The latter simply uses the index/name of the element.

    Doing local $foo[3] = 'bar' will override element 3 of @foo, regardless of whether @foo is a my-var or not. You can even do it on array refs: local $ref->[3] = 'bar';

    What local $foo[3] = 'bar' does, basically, is:

    my $saved_foo_3 = $foo[3]; $foo[3] = 'bar'; # .. rest of the block here .. $foo[3] = $saved_foo_3;
    except with special provisions to make sure the value is always restored, even if the code dies or whatever.

    I hope this makes things a bit more clear

      I'd be curious what the exact thought behind this was. It makes sense to me: localizing most lexicals has no real value -- you can either redefine them via new lexical definitions of the same name in enclosed blocks; or you can pass specific overrides to functions further down the calling stack. That's a lot clumsier with single elements of arrays and hashes. So this use of local fits IMHO. I just wonder if it was designed that way of it it's an accident.

        The basic reason is that local doesn't actually alter the contents of a variable. It allocates a new one and makes sure that the access path to the original variable now temporarily leads to the new variable. This means however that there has to be a primary access path to the variable, or the trick won't work. You also have to be sure that the original location will not disappear before the restore.

        For package vars, this primary access path is the glob. Each package contains a glob for each name that occurs in it. This glob can contain a scalar variable, a hash variable, an array variable, an IO handle etc.

        When you do local $Foo; (where $Foo is a package var) perl will save the original scalar variable (not its contents) on stack, create a new scalar variable, and insert it into the glob's SCALAR field. It will also increment the refcount on the glob so it will surely not be deallocated.

        •(Update: a local doesn't get initialized with the original value... I thought it was..)

        On return, the localized variable is discarded, the original variable is placed back in the glob, and the glob's refcount is decremented. Everything is back to normal.

        Note that you can easily see this behavior: just take a reference to $Foo before and after localizing it. You'll see they reference different and independent variables. (Keeping a reference to the localized var will ofcourse keep it alive, even if the local has already been restored

        The procedure is identical for local %Foo and local @Foo, except they operate on the HASH and ARRAY fields of the glob. When you do local *Foo, it does essentially the same procedure but for all fields of the glob at once. They're put together in a structure to which the glob keeps a pointer, so it doesn't need to save each var separately. Note that it can't just allocate a new glob and place it into the package, since all existing code would still use the old glob, which they reference directly.

        Now for lexical variables... they have no glob! :-)
        That doesn't mean it's truly impossible to localize them (I haven't examined how my-vars are implemented exactly), but it surely means that the mechanism described above can not be reused for them.

        As I mentioned in my original note, localizing array and hash elements works completely different. Here you specify an array or hash in any way you like, and the index or key of an element. local $foo[3] then takes the scalar variable (again, not its contents) which is the element at that index (or key), saves it on the stack, along with a reference to the array/hash (which also means the array/hash will surely not get destroyed) and the index/key of the element. It creates a new scalar variable and places in the original location. When the block ends, it places the saves element back, discarding the variable that was there.

        Again, you can check this by taking a reference. You'll see that the localized $foo[3] is a different and independent variable than the original.

        Ok, that's it I think... any questions? :-)

        •Update: Here's some example code with output:

        I'm with you, lol....
      Clearer, if not quite yet entirely transparent. Thanks!
Re: local element of my() array????
by extremely (Priest) on Feb 19, 2003 at 06:22 UTC
    Try this one:
    #!/usr/bin/perl -w use strict; my @k = (1..9); my $m; { local $k[5]="middle"; $m = \@k; print "@k\n"; my $m = [ 'a'..'z' ]; print "@$m\n"; } print "@$m\n"; ## Returns: #1 2 3 4 5 middle 7 8 9 #a b c d e f g h i j k l m n o p q r s t u v w x y z #1 2 3 4 5 6 7 8 9

    Pretty ugly, huh?

    --
    $you = new YOU;
    honk() if $you->love(perl)