Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Modifying values by reference: what works and what doesn't

by markjugg (Curate)
on Aug 25, 2005 at 16:49 UTC ( [id://486628]=perlmeditation: print w/replies, xml ) Need Help??

So I learned something about modifying values by reference today. I thought this would work:
sub foo { my $ref = shift; # I expect this change to be visible outside of foo(). $ref = { all => new }; }
I think I see why that doesn't work: I have recreated what "$ref" is, so it no longer is related to the data structure I passed in. However, modifying a value inside of $ref will still be visible outside of the subroutine:
sub foo { my $ref = shift; # Modified value is now visible outside of foo() $ref->{all} = 'new'; }

(Yes, I know explicit return values are generally better style, but modify-by-reference is very handy in some cases).

Replies are listed 'Best First'.
Re: Modifying values by reference: what works and what doesn't
by Joost (Canon) on Aug 25, 2005 at 17:04 UTC
    I take it you've never programmed in C or assembly :-)

    What you're doing in the first version is overwriting a copy of the reference:

    my %hash = ( all => 'old' ); # simple hash value foo(\%hash); # pass reference to hash sub foo { # recieve reference aliased as $_[0 +] my $ref = shift; # copy reference to $ref (and throw + away the $_[0] alias) $ref = { all => 'new' }; # overwrite the copy of $ref # this creates a *new* hash and a n +ew reference in $ref # %hash is not modified }
    In the second version you're overwriting a key in the hash pointed to by the reference:
    my %hash = ( all => 'old' ); # simple hash value foo(\%hash); # pass reference to hash sub foo { # recieve reference as $_[0] my $ref = shift; # copy reference to $ref (and throw + away $_[0]) $ref->{ all } = 'new'; # overwrite the value to the key 'a +ll' # in the hash pointed to by $ref (w +hich is %hash) }

    The key concept is that a reference only points to a value (under the hood, a reference in perl contains only slightly more information than the memory address of the data structure it's referring to) - it does not "contain" the value itself - so modifying the reference does not change the value it's pointing to - you can use it to modify the referenced data only as long as it's actually pointing to that data.

    update: by the way: the fact that parameters to a function are aliased in @_ means you *can* change parameters even without passing them by reference (by assigning to $_[0] et al), but that will a) only work if the parameters are non-constant scalars, b) confuse the heck out of your coworkers.

    updated code: diotalevi's right.

      A nit, my %h = { ... } with braces is wrong. Use parentheses instead.
Re: Modifying values by reference: what works and what doesn't
by phaylon (Curate) on Aug 25, 2005 at 17:02 UTC
    I'd formulate it so: If you pass a reference, you get a copy of that reference. So you have two references pointing at the same thing. If you overwrite the received ref with something else (a hashref in your first example), you only have one of the two references left and the var that held the second reference before now has a reference to the hashref.


    Ordinary morality is for ordinary people. -- Aleister Crowley

      This code shows that you were not that precise :-) (Update: See below reply from phaylon, sounds like I have misunderstood what he said, and that was my fault. I up voted both of his posts.)

      use strict; use warnings; my $ref = {"a" => 1}; print $ref, "\n"; foo($ref); sub foo { my $ref = shift; print $ref, "\n"; $ref = { "all" => "new" }; print $ref, "\n"; }

      Run it, you will see that the first two print statements print the same addresses, but not the third one.

        That's actually what I've meant ;) Though I'm not a native speaker, so I may have screwed it up a byte. I meant it copies the contents of the scalar, the reference. That results in a second reference (in counting refcounts) to the same address.

        Sorry to confuse, if I have :)

        Ordinary morality is for ordinary people. -- Aleister Crowley
      Passing doesn't make a copy of the reference; it's the assignment to $x that makes a copy.
Re: Modifying values by reference: what works and what doesn't
by halley (Prior) on Aug 25, 2005 at 18:29 UTC
    Since nobody else mentioned it, there's a key difference between your two code snippets. In your first, you appeared to be interested in making the caller's hash empty except for one key/value pair. Of course, you see now why that attempt failed in modifying the caller's hash at all. In your second, you simply added or replaced a single key/value pair, and left any other contents of the caller's hash alone.
    sub foo { my $ref = shift; %$ref = ( all => new ); }
    This alternative will keep the caller's hash intact, but replace ALL of its contents with a completely new set of contents, consisting of one key/value pair. This will also work even if the caller's hash is actually a blessed reference; the existing blessing is not broken (desecrated?) by this method.

    --
    [ e d @ h a l l e y . c c ]

Re: Modifying values by reference: what works and what doesn't
by ysth (Canon) on Aug 25, 2005 at 19:09 UTC
    The parameters in @_ are aliased to what was actually passed, so this works:
    sub foo { $_[0] = { all => "new" }; }
    but your my $ref = copies the reference, and the copy in $ref is not aliased. This is just like the difference between:
    my %x = (foo => 0, bar => 1); my $z = $x{foo}; $z = 2;
    which doesn't change the original value in %x and
    my %x = (foo => 0, bar => 1); $x{foo} = 2;
    which does.
Re: Modifying values by reference: what works and what doesn't
by demerphq (Chancellor) on Aug 26, 2005 at 11:38 UTC

    Just to rephrase this a little. Scalar variables are containers that hold a value. When you assign to a scalar you throw away the value previously contained and overwrite it with a new value.

    References are a scalar value that point at (or refer to) other containers. This should be distinguished from an alias which is just a different textual name for the same container. For instance if you modify the first sub:

    sub foo { for my $ref ($_[0]) { $ref = { all => new }; } } my $hash={}; print $hash,"\n"; foo($hash); print $hash,"\n";

    $ref is now an alias to $_[0] which is itself automagically an alias to whatever scalar was passed into the subroutine which in this case is the scalar $hash. In this case assigning to $ref inside of the sub is the same as assigning to $hash outside of the sub.

    On rereading this it may just cause more confusion, if so I apologise.

    ---
    $world=~s/war/peace/g

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (5)
As of 2024-04-19 15:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found