Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Modify a hash via its reference

by Philippe (Novice)
on Jan 23, 2008 at 11:38 UTC ( [id://663776]=perlquestion: print w/replies, xml ) Need Help??

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

Hi!

I've been unsucessfully trying to modify a hash via its reference: it have only found a lame workaround. Can anyone enlighten me?

Thanks!

Philippe

PS: here's a reduced testcase

#!/bin/perl -W # use strict; # my ( %Fruits ) = ( "Apples", 3, "Oranges", 6 ); &Print_Hash; # &Modify_Hash_1 ( \%Fruits ); &Print_Hash; # This doesn't seem to have any effect... # &Modify_Hash_2 ( \%Fruits ); &Print_Hash; # But this does... # sub Print_Hash { my ( $Fruit ); foreach $Fruit ( sort ( keys ( %Fruits ) ) ) { printf "\n%-7s: %d", $Fruit, $Fruits{$Fruit}; }; print "\n\n"; }; # sub Modify_Hash_1 { my ( $Ref_to_Hash ) = @_; $Ref_to_Hash = { "Pears", 5, "Peaches", 7 }; }; # sub Modify_Hash_2 { my ( $Ref_to_Hash ) = @_; my ( $Fruit ); foreach $Fruit ( keys ( %$Ref_to_Hash ) ) { delete ( $$Ref_to_Hash{$Fruit} ); }; $$Ref_to_Hash{"Pears"} = 5; $$Ref_to_Hash{"Peaches"} = 7; }; # # __END__

Replies are listed 'Best First'.
Re: Modify a hash via its reference
by roboticus (Chancellor) on Jan 23, 2008 at 11:50 UTC
    Philippe:

    The problem is that your Modify_Hash_1 routine is accepting the hash ref, and then throwing the reference away by assigning it a new value. By modifying your Modify_Hash_1 routine to the following, you can change the values in %Fruits.

    sub Modify_Hash_1 { my ( $Ref_to_Hash ) = @_; $Ref_to_Hash->{Apples}=5; # This line would discard the reference to %Fruits, and create a *new* + # hash with the specified values # $Ref_to_Hash = { "Pears", 5, "Peaches", 7 }; };
    ...roboticus

    Update: s/discards/discard/, s/taking/accepting/, added code tags in explanatory text.

      Good work.
      # This line would discard the reference to %Fruits, and create a *new* # hash with the specified values # $Ref_to_Hash = { "Pears", 5, "Peaches", 7 };
      The solution is to not change the reference, but the dereferenced hash:
      %$Ref_to_Hash = ( "Pears", 5, "Peaches", 7 );
Re: Modify a hash via its reference
by moritz (Cardinal) on Jan 23, 2008 at 11:57 UTC
    When you pass an argument to a function, @_ is an alias to these arguments.

    So the following works:

    use strict; my $x = 1; modify_args($x), print $x, "\n"; sub modify_args { $_[0] = "modified"; } __END__ modified
    But as soon as you assign the values to a new variable, you create a copy:
    my $x = 1; modify_args($x), # doesn't work print $x, $/; sub modify_args { my ($copy) = @_; $copy = "modified"; } __END__ 1

    Which means that you can change any arguments passed to a sub as long as you stick to manipulating them in @_.

    You can circumvent this restriction by passing references , using a module like Data::Bind or Data::Alias), or by creating a new hash and returning that.

    If you don't like the last solution (returning a new hash) you should stick to the first one, because of the principle of least surprise: When you call a sub like this: sub_name %hash you don't expect %hash to be modified.

    Another possible options are prototypes (notable (\%)), but they come with many problems, and you shouldn't use them until you understood all of these problems

Re: Modify a hash via its reference
by RMGir (Prior) on Jan 23, 2008 at 12:25 UTC
    What you're looking for is %{$Ref_to_Hash}:
    sub Modify_Hash_1 { my ( $Ref_to_Hash ) = @_; # Note, _NOT_ {} - must be assigning a hash, # not a hash ref %{$Ref_to_Hash} = ( "Pears", 5, "Peaches", 7 ); };
    That way, you're saying "update the hash pointed to by $Ref_to_Hash".

    I recommend the perlref and perldsc perldoc pages - they'll help you wrap your mind around references.


    Mike
Re: Modify a hash via its reference
by bradcathey (Prior) on Jan 23, 2008 at 12:54 UTC

    Agree with the above, but as a sidebar a couple of notes on coding style that might help readability (none critical, just some things I've picked up bashing around the Monastery):

    • No need for the "&" in front of your subroutine calls, or the space just preceding the opening parens. Include the empty set of parens if not passing a value.
    • No need for the "#" on the blank lines (visual noise)
    • No need for the "()" around a single my variable declaration</code>
    • Might want to consider an alternative to spelling out your hash values with the 'corresponds to' arrow =>
    • And personally, I like my variable and subroutine names in all lowercase (I save the title case for my module names.

    So,

    my %fruits = ( "Apples" => 3, "Oranges" => 6 ); print_hash(();

    A great book: Perl: Best Practices.

    Good luck!

    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
      No need for the "()" around a single my variable declaration
      This is true in most situations but not all. From the OP's code:
      my ( $Ref_to_Hash ) = @_;
      In this style of parameter initialization, the parentheses are in fact required. Otherwise $Ref_to_Hash will be assigned the number of arguments, not the value of the first one.

        I stand corrected (which is why I love this place). Thanks, I learned something.

        —Brad
        "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
Re: Modify a hash via its reference
by Philippe (Novice) on Jan 23, 2008 at 13:54 UTC

    Thanks a lot all for your help!

    Philippe

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-25 21:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found