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

i'm havng trouble passing a hash to a function. i made this little script to figure it out:

<p><pre> #!/usr/bin/perl -w use CGI qw(:standard); use strict; my %yummy = ( "banana" => 'yellow', "apple" => 'green', "tangerine" => 'orange' ); print_hash(\%yummy); sub print_hash { my $food = $_[0]; my %hash = %$food; my $key; my $value; foreach $key (keys %hash) { print $value = $hash{$key}; print "fruit: $key is $value\n\n"; } }

and this is what i get back:

yellowfruit: banana is yellow

greenfruit: apple is green

orangefruit: tangerine is orange

what in the world is going on?

Replies are listed 'Best First'.
Re: passing hashes to a function
by chromatic (Archbishop) on May 09, 2000 at 22:26 UTC
    You might remove that first print statement in your foreach loop. It's printing $value.

    I would handle things a little differently, but this assumes you're more comfortable with references than you might be at this point. Untested:

    sub print_hash { my $hash_ref = shift; my $value; foreach $key (keys %$hash_ref) { $value = $hash_ref->{$key}; print "fruit: $key is $value\n\n"; } }
    Instead of dereferencing the reference and copying the resulting hash (what you did with $food), this accesses the underlying hash through the reference. If you're concerned about potential modification of the hash in the subroutine, be aware that this technique could allow that.

    For more details, see perlref, as it describes the %$ and $->{} syntaxes in sufficient detail. (Dereferencing)

Re: passing hashes to a function
by btrott (Parson) on May 09, 2000 at 22:31 UTC
    As chromatic said, w/ a bit of further explanation: the first print statement in your loop
    print $value = $hash{$key};
    is setting $value equal to $hash{$key}, which is probably what you want; and is then printing the value of that statement. And the value is $value.

    So, when $value is "green", for example, that's why you're seeing "greenfruit". "green" and "fruit" are actually coming from different print statements.

    Does that make sense? Is that the problem you were having?

    To fix that, of course, just take out print, leaving the

    $value = $hash{$key};
    Or, of course, you could just get rid of that line altogether and put $hash{$key} into the real print statement:
    print "fruit: $key is $hash{$key}\n\n";
    Your choice.
Re: passing hashes to a function
by BBQ (Curate) on May 10, 2000 at 08:54 UTC
    Why the need for a reference at all? Can't you just pass the hash through the @_? As in
    my %hash = ( 'John' => 'Burbridge', 'Anna' => 'Almeida', 'Izabel' => 'Murat', 'Victor' => 'Hugo' ); print_hash(%hash); sub print_hash { my %newhash = @_; foreach (keys %newhash) { print "Name $_,\t Lastname: $newhash{$_}\n"; } }
    As a matter fact, I don't even know what use for references are since I've never used them (at least not in that context).

    #!/home/bbq/bin/perl
    # Trust no1!
      A little off topic, but references are also the only way you can create a perl object or multi dimensional data structures.
      Perl doesnt really have 2D arrays, but we can still make them by create an array of array references.
      And with objects the object is really a reference to a data structure of some kind (usually a hash), so when you pass an object to a function you are really only passing a reference to a memory address. The actual data of the object stays in a fixed memory location and is not copied anywhere.
      BBQ asked for some code to help explain so I hope this helps:
      #!/usr/bin/perl use strict; # create some table rows my @a = qw(a b c); my @b = qw(c d e); my @c = qw(f g h); # create the 2D structure. my @table = (\@a, \@b, \@c); # @table is really a 1D structure, of references # but conceptually you can treat it as a 2D structure # (see the print_cube function) # this prints just the references print join(" ", @table), "\n\n"; # this function prints out the table print_cube(@table); # but since @table holds only references, you can change # the data being referenced and @table will in turn change # because they are really pointing the same thing @a = qw(x y z); print_cube(@table); # that is why multi dimensional structures are usually create # with anonymous structures: $table[0] = [1, 2, 3]; $table[1] = [4, 5, 6]; $table[2] = [7, 8, 9]; # that way to access the first row you have to go through # the table structure because the first row has not other # name than $table[0] (unlike @a above) print_cube(@table); sub print_cube { my @cube = @_; foreach my $i (0..2) { foreach my $j (0..2) { print $cube[$i][$j], " "; } print "\n"; } print "\n"; }
      And the code results:
      ARRAY(0x80e4ac0) ARRAY(0x80e4afc) ARRAY(0x80e8bc0) a b c c d e f g h x y z c d e f g h 1 2 3 4 5 6 7 8 9
      You *can* do that, but then end up copying a bunch of memory around, whereas when you use a reference you're just copying, well, the reference. So personally I think it's generally better to just use references as arguments. It's faster.

      In addition, what if you later decided that you needed to call the subroutine with two hash arguments instead of just one? Now you've got a problem, because you can't say:

      foo(%bar, %baz);
      because the hashes get flattened into a list (as all subroutine arguments do). So instead you've got to pass in references to the hashes, and now you'll have to change all mentions of the hash inside the subroutine to use the reference instead.

      So it'll save you possible future work to use references.

Re: passing hashes to a function
by skazat (Chaplain) on May 10, 2000 at 00:33 UTC
    oh man, i feel dumb ;)
    i didn't see that first print statement, i was in a bit of a rush, my g/f just got into the paper for work she did for nasa and we decided to go to Indian to celebrate, oh well, guess its just Perl laziness, next timei'll dbl check.

    print -justin simoni
    !skazat!