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

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

can anyone explain why this code acts the way it does? I can not figure out what is going on in perl's head with this example.
$test="scott"; $main::{$test} = "skot2"; print "As you see, ($scott) and ($main::scott) aren't here\n"; $$test = "surprise!"; print "Now ($scott) and ($skot2) and ($main::scott) have decided to sh +ow up\n";
  • Comment on question about variabies/references (ignore my previous botched entry)
  • Download Code

Replies are listed 'Best First'.
Re: question about variabies/references (ignore my previous botched entry)
by plaid (Chaplain) on Apr 05, 2000 at 12:31 UTC
    This has to be one of the best questions I've seen here, if only for the reson that it gave me a good chance to go digging around and try to figure this out. This explanation is far from perfect and any additions/corrections are welcome.

    The main problem comes with the line $main::{$test} = "skot2". This is a lookup into the symbol table of main::, with the key of $test. Perl stores its symbol table in the form of string keys, and typeglob values. $main::{$test} is not the same as the $$test a couple lines down, but rather is the same as $::{$test}.

    As for the weirdness in the last line, with $scott and $skot2 now being the same value, the $main::{$test} = "skot2" line again needs to be looked at. The main:: symbol table is expecting a typeglob or a reference on assignment, but when perl is expecting a typeglob and gets a string, it automatically creates a new typeglob of that name, so that line is functionally equivalent to having put $main::{$test} = *skot2. This is effect creates an alias between $scott and $skot2, so changes to either one will affect the other.

    What needs to be done to get any kind of sensible answer is to pass in a reference on the assignment to $main::{$test}. There are two ways to go about it. Either you can do $main::{$test} = \"skot2", which will make the value of $scott come out right, but will make it be a constant, and thus the assignment to $$test will fail. The better way to do it would be to assign the value "skot2" to another variable, and pass in the reference of that variable.

    Hope this helps.

Re: question about variabies/references (ignore my previous botched entry)
by chromatic (Archbishop) on Apr 05, 2000 at 19:04 UTC
    Here's a funny thing. $scott and $main::scott refer to the same variable, assuming you haven't declared that you're in another package. Another piece of the puzzle is that putting curly braces around anything that looks like a variable causes Perl to interpolate the value of that variable. Doing: $main::{$test} = "skot2"; first causes interpolation -- so that the interpreter now has: $main::scott = "skot2"; From there, I assume plaid is correct in that the interpreter decides that you really meant to do a typeglob assignment, and takes "skot2" to be a symbolic reference (that is, the name of another variable). Since it hasn't been declared before, $scot2 (and $main::scot2) are autovivified. Next, $main::scott is aliased to $scot2. The $$test line is even more tricky. You might also write it ${$test} = "surprise!"; to be consistent with my explanation. If $test were a normal reference, this would dereference it. Since it contains the name of a variable ($skot2 has just been created automatically), Perl considers it a symbolic reference, looks up $main::skot2 in the symbol table, and assigns the value of "surprise!" to it.

    Because $main::scott is aliased to $skot2, accessing it gets you the same value ("surprise!"). Because you're in package main, you can leave off the $main:: portion, and accessing $scott also gets you your "surprise!".

    These are three or four things that will eventually surprise any new Perl programmer. Using strict and -w will warn you when these things happen (except for autovivification in hashes, which is worth another page of explanation). Use them liberally!

      You (chromatic) wrote:
      > Another piece of the puzzle is that putting curly > braces around anything that looks like a variable > causes Perl to interpolate the value of that > variable. Doing: > > $main::{$test} = "skot2"; > > first causes interpolation -- so that the > interpreter now has: > > $main::scott = "skot2";
      No, I don't think this is true. When you do
      $main::{$test} = "skot2";
      you're manipulating the symbol table of package main; this is a very different thing than modifying a scalar variable ($main::scott). $main::scott and $main::{"scott"} are two different things.
        You're right, the post-interpolated line should read: *main::scott = "scot2"; Here's what I've been using to test my assumptions:
        #!/usr/bin/perl $test1 = "value of test1"; $test2 = "test1"; $test3 = "Why are you here?"; # refers to *main::test1 print "=>$main::{$test2}<=\n"; print "Original \$test1: ...$test1...\n"; $main::{$test2} = "test3"; # $main::{"test1"} = "test1"; print "\$test1 = >>", $test1, "<<\n"; print "Main package: ", $main::test1, "\n";
        As you can see, $main::{$test2} evaluates to a typeglob. Saying that $main::{$test2} = "test3" changes the scalar is technically incorrect. (Though it is what my previous answer would lead one to believe!)

        Dunno where that u came from. Maybe it's a mu?

Re: question about variabies/references (ignore my previous botched entry)
by btrott (Parson) on Apr 05, 2000 at 20:05 UTC
    You're seeing this weird behavior because you have several variables that point to the same value; at the time of the first print, you haven't yet set that value, so it's undefined. When you set the value in the next line, it sets that value, so when you do the second print, they have a value.

    Let's step through it:

    $test = "scott";
    This is self-explanatory--you're just setting a scalar variable. Later you'll use that value as a symbolic reference.
    $main::{$test} = "skot2";
    Here you're manipulating the symbol table; you're saying that $main::scott should point to the same thing that $main::skot2 points to. You can do this symbol table manipulation quite easily, and there's a definite potential for confusion--for example, there's a difference between $main::{"scott"} and $main::scott. The first is a symbol table entry and the second is a scalar variable.
    print "As you see, ($scott) and ($main::scott) " . "aren't here\n";
    $scott and $main::scott are the same variable, because $scott is found in package main, and the second is just a fully-qualified version of the first. You haven't set a value for this variable yet. You've modified $main::{"scott"} but not $main::scott. Perhaps that's the real source of your confusion?
    $$test = "surprise!";
    Here's where you set the value. $test is equal to "scott", so here you're just using symbolic references to change the value of $scott (which is the same as $main::scott and points to $skot2). So, it makes sense that, in the next line...
    print "Now ($scott) and ($skot2) and ($main::scott) " . "have decided to show up\n";
    you now have values for your variables.

    Does this make sense?

Re: question about variabies/references (ignore my previous botched entry)
by jbert (Priest) on Apr 05, 2000 at 14:00 UTC
    Its perl's way of reminding you that symbolic references are evil.

    Don't do that. (Unless of course you want to)

    -w and use strict, all the way.