Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

hash question

by duyet (Friar)
on May 26, 2016 at 10:46 UTC ( [id://1164183]=perlquestion: print w/replies, xml ) Need Help??

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

I've been working with hash for a while now, but there is something i really don't understand.
my $hash = {}; do_something( $hash ); print Dumper( $hash ); sub do_something { my $hash = shift; $hash = { a => 'alpha', b => 'beta', }; print Dumper( $hash ); }
when exectuted:
{ 'a' => 'alpha', 'b' => 'beta' } {}
but if using:
do_something_else( $hash ); print Dumper( $hash ); sub do_something_else { my $hash = shift; $hash->{g} = 'gamma'; $hash->{d} = 'delta'; print Dumper( $hash ); }
it shows:
{ 'd' => 'delta', 'g' => 'gamma' } { 'd' => 'delta', 'g' => 'gamma' }
To me the initialization of $hash is the same in both subs, but apparently it is not. It's probably something very trivial, but i just miss it. Would you please explain why? TIA.

Replies are listed 'Best First'.
Re: hash question
by GrandFather (Saint) on May 26, 2016 at 11:01 UTC

    In do_something you replace the passed in hash reference you copied into $hash with a new hash so the original hash doesn't get altered.

    In do_something_else you use the copied reference to access the original hash and update it.

    In both cases $hash starts out with a reference to the original hash.

    Premature optimization is the root of all job security
Re: hash question
by BrowserUk (Patriarch) on May 26, 2016 at 11:00 UTC

    In your first case, you initialise the scalar variable $hash with a reference to an empty anonymous hash: my $hash = {};

    You then pass that reference into the sub: do_something( $hash );

    Where is value (the reference) gets copied into another scalar my $hash = shift; (also called $hash, but a completely different piece of memory).

    Then you assign another (completely different) reference to a different anonymous hash to that variable:

    $hash = { a => 'alpha', b => 'beta', };

    And then print the contents of that new hash; before returning to the main program and printing the contents of the first $hash, that hasn't changed since you initialised it.

    In the second case in the sub, you assign (through the copy of the reference you passed in) to the original anonymous hash.

    Thus, both print statements are printing the contents of the same anonymous hash, albeit that they are doing so through different copies of a reference to that hash.

    Does that help?


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: hash question
by AnomalousMonk (Archbishop) on May 26, 2016 at 15:48 UTC

    As an example of what BrowserUk and GrandFather are talking about, print the addresses of the hash references you're passing around:

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dumper -le "my $hash = {}; print 'Ma: ', $hash; ;; do_something($hash); ;; print 'Mb: ', $hash; print Dumper($hash); ;; sub do_something { my $hash = shift; print 'Sa: ', $hash; ;; $hash = { a => 'alpha', b => 'beta', }; print 'Sb: ', $hash; print Dumper( $hash ); } " Ma: HASH(0x9cd06c) Sa: HASH(0x9cd06c) Sb: HASH(0x9cd16c) $VAR1 = { 'a' => 'alpha', 'b' => 'beta' }; Mb: HASH(0x9cd06c) $VAR1 = {};
    Note that the address of the  $hash reference variable in the subroutine changes between point Sa and point Sb when you assign another anonymous hash reference to it, but the  $hash reference variable in the "main" part of the code never changes its address (or its contents).

    Replace the
        $hash = { a => 'alpha',  b => 'beta', };
    statement in the subroutine with
        $hash->{a} = 'alpha';
        $hash->{b} = 'beta';
    statements that assign by reference to the referent and see what happens to all the hash reference addresses: they never change.


    Give a man a fish:  <%-{-{-{-<

      Thanks all for your prompt responses. I don't understand why the address has been changed between "Sa:" and "Sb:" by assigning another anonymous hash. I expected "Sb:" would be the same as "Sa:", since i was using the same var. I changed the code a bit:
      sub do_something { my $h = shift; print '1. do_something(): ' . $h . "\n"; %{ $h } = ( a => 'alpha', b => 'beta' ); print '2. do_something(): ' . $h . "\n"; print Dumper( $h ); }
      and it shows:
      hash = HASH(0x490d30) 1. do_something(): HASH(0x490d30) 2. do_something(): HASH(0x490d30) { 'a' => 'alpha', 'b' => 'beta' } { 'a' => 'alpha', 'b' => 'beta' }
      The address is not changed ... the behaviour is not consistent to me :)

        You are missing one slight catch. When you do

        my $hash = shift;
        you get pointer to the object passed to the function. When you do
        $hash = {a => "alpha"};
        you create new object and store its pointer in the variable that previously contained pointer to another object. Finally, when you do
        %{ $hash } = ( a => 'alpha', b => 'beta' );
        then you take object pointed by the variable and store in that object new set of keys.

        You have to distinguish "objects" and "pointers to objects", the latter are called "references" in perl, then things may become more consistent for you.

        You are printing the content of the variable. It's two different variables. If you print the variables directly instead of via Dumper,
        my $hash = {}; do_something( $hash ); print Dumper( $hash ); sub do_something { my $hash = shift; $hash = { a => 'alpha', b => 'beta', }; print Dumper( $hash ); }
        you'll get two different addresses:
        HASH(0x3c4a4)HASH(0x3c39c)
        Update: actually, this is also only the contents. Best answer so far to your OP is this by AnomalousMonk
        I don't understand why the address has been changed between "Sa:" and "Sb:" by assigning another anonymous hash. I expected "Sb:" would be the same as "Sa:", since i was using the same var. [Emphasis added]

        That's like saying

        In the code
            $x = 4;
            $x = 7;
        I don't understand why the value of  $x is 7 after the second assignment to $x: I'm using the same variable.

        As others have noted, it's vital to distinguish between a variable, the content of the variable, and, if the content of the variable is a reference, the referent of the reference. References are tricky.


        Give a man a fish:  <%-{-{-{-<

Re: hash question
by AnomalousMonk (Archbishop) on May 27, 2016 at 18:41 UTC

    Somewhat belatedly, it occurs to me to wonder if you have some confusion between the notions of reference and alias. Of course, an alias is just another name for a single thing. Neo is the alias in the Matrix of Thomas Anderson in the "real" world — whatever that is! Whatever happens to Neo in the Matrix happens to Anderson in the real world. *

    Using aliasing, I can come up with a code example that behaves rather as you seem to expect the OPed code to behave. (Note that in Perl, the function argument list is always a list of aliases.)

    c:\@Work\Perl>perl -wMstrict -MData::Dumper -le "my $hash = {}; print 'Ma: ', $hash; ;; do_something($hash); ;; print 'Mb: ', $hash; print Dumper($hash); ;; sub do_something { print 'Sa: ', $_[0]; ;; $_[0] = { a => 'alpha', b => 'beta', }; print 'Sb: ', $_[0]; print Dumper( $_[0] ); } " Ma: HASH(0x92d06c) Sa: HASH(0x92d06c) Sb: HASH(0x92d16c) $VAR1 = { 'a' => 'alpha', 'b' => 'beta' }; Mb: HASH(0x92d16c) $VAR1 = { 'a' => 'alpha', 'b' => 'beta' };
    In this example, the value of the (aliased) reference changes as one would expect between points Sa and Sb in the subroutine, and also changes in exactly the same way between points Ma and Mb in the "real" world (as does the referent).

    Update: * I think I got that wrong. Thomas Anderson is the alias in the Matrix for Neo, the real person extracted from his pod by Morpheus. Remember that Morpheus says to Neo "welcome to the desert of the real." (Jeeze, I gotta get a life...)


    Give a man a fish:  <%-{-{-{-<

      Thanks! I guess i was confused between reference and alias.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-04-24 03:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found