Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Understanding typeglobs and local()

by whio (Beadle)
on Jan 13, 2006 at 01:05 UTC ( [id://522854]=perlquestion: print w/replies, xml ) Need Help??

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

I've been trying to understand why these give different results:
#!/usr/bin/perl *alias0 = *_; *alias1 = \$_; @F = qw(A B C D); s//Sample/; $x = '%'; do { local $_ = 999; print "do: ( $_ $alias0 $alias1 )\n"; }; foreach (@F) { print $_; *_ = *x; print " : ( $_ $alias0 $alias1 )\n"; }
My understanding was that foreach() implicitly did local $_; before starting the loop. But look at the output I get (from perl-5.8.0):
do: ( 999 999 Sample ) A : ( % A Sample ) % : ( % B Sample ) % : ( % C Sample ) % : ( % D Sample )
What's going on here? How does $alias0 manage to get a reference to the loop variable inside the foreach?

Replies are listed 'Best First'.
Re: Understanding typeglobs and local()
by Fletch (Bishop) on Jan 13, 2006 at 01:09 UTC

    It does localize $_, but it also aliases it to each item. Quoth perldoc perlsyn:

    The "foreach" modifier is an iterator: it executes the statemen +t once for each item in the LIST (with $_ aliased to each item in turn +).
      I suppose I knew that, although that didn't occur to me as an explanation.

      I guess I'm trying to understand "how it works" on the inside so that I know what behaviour to expect. From what I understand, local ($_); is roughly equivalent to:

      my $saved = $_; $_ = undef; ... $_ = $saved;
      so foreach saved a pointer to the original $_, which contains 'Sample' as a result of s///. This I understand; it's why $alias1 is unaffected. Yet $alias0 is affected, and I'm not quite sure that I understand why. (This is no different than the local() case.)

      Inside the foreach, we alias to $x. This doesn't affect the preexisting aliases, but on the next iteration through the loop our alias still exists. The behaviour changes if the line *_ = *x; is changed to *_ = \$x;, and this is what I was trying to understand.

      Hmm, maybe I'm starting to get it now. *x is a 'level higher' than \$x in the internal structure of the symbol table. So foreach is only localizing the scalar (and restoring it at exit - this changes what $_ is after the loop if that change is made), and changes to the typeglob take foreach() completely unawares.

      Update: It turns out what was confusing me was that foreach() apparently makes its own copy of the glob for *_, as the alias *_ = *x doesn't change where foreach() puts its alias. *_ = \$x leaves the glob alone, and just modifies the scalar portion; this does get overwritten by foreach()'s aliasing on each iteration (and at loop exit) provided the glob has not been changed.

        "Glob" is another name for "symbol table entry". Symbol table entries have multiple fields, one for each data type (scalar, hash, array, etc).

        $abc = 'xyz'; print(${*{abc}{SCALAR}}, "\n"); # xyz

        When you modify a package variable, you modify the data in the appropriate field of the appropriate symbol table entry.

        $glob = *def = *abc; $abc = 'xyz'; print($abc , "\n"); # xyz print($def , "\n"); # xyz print(${$glob }, "\n"); # xyz print(${*{abc }{SCALAR}}, "\n"); # xyz print(${*{def }{SCALAR}}, "\n"); # xyz print(${*{$glob}{SCALAR}}, "\n"); # xyz

        Same goes when you localize a package variable. You're localizing the SCALAR field of the symbol table entry. It doesn't matter how you get to the symbol table entry (be it $_ or $alias0, for example), because the value is not associated with the symbol ($_), but with the symbol table entry (${*_{SCALAR}}).

        $glob = *def = *abc; $abc = 'xyz'; print($abc , "\n"); # xyz print($def , "\n"); # xyz print(${$glob }, "\n"); # xyz print(${*{abc }{SCALAR}}, "\n"); # xyz print(${*{def }{SCALAR}}, "\n"); # xyz print(${*{$glob}{SCALAR}}, "\n"); # xyz local $def = 'uvw'; print($abc , "\n"); # uvw print($def , "\n"); # uvw print(${$glob }, "\n"); # uvw print(${*{abc }{SCALAR}}, "\n"); # uvw print(${*{def }{SCALAR}}, "\n"); # uvw print(${*{$glob}{SCALAR}}, "\n"); # uvw

        In short, *abc = *def; makes the symbols abc and def the same. Only changes to the globs can undo this. local $abc doesn't affect the glob *abc (or *def), only the scalar component of it.

        *x is a 'level higher' than \$x in the internal structure of the symbol table.
        Very close. *x is a glob, a variable (storage location) of type PVGV. It knows what its own name and package are, and holds a value, a GP. The GP is the value that is copied with assignments like *foo = *bar. The GP holds all the various possible types (SV, AV, HV, CV, PVIO, etc.).
        From what I understand, local ($_); is roughly equivalent to:
        my $saved = $_; $_ = undef; ... $_ = $saved;
        No, its actually equivalent to
        my $tmp; my $old = \$_; *_ = \$tmp; ... *_ = \$old;
        as can be seen from the following:
        $ perl -le '$x = 1; $r = \$x; local $x=2; print "$$r $x"' 1 2 $
        In your example they both would have printed 2.

        Dave.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://522854]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (11)
As of 2024-03-28 09:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found