Sunday, April 3, 2011

Is %$var dereferencing a Perl hash?

I'm sending a subroutine a hash, and fetching it with my($arg_ref) = @_;

But what exactly is %$arg_ref? Is %$ dereferencing the hash?

From stackoverflow
  • $arg_ref is a scalar since it uses the $ sigil. Presumably, it holds a hash reference. So yes, %$arg_ref deferences that hash reference. Another way to write it is %{$arg_ref}. This makes the intent of the code a bit more clear, though more verbose.

    To quote from perldata(1):

     Scalar values are always named with '$', even when referring
     to a scalar that is part of an array or a hash.  The '$'
     symbol works semantically like the English word "the" in
     that it indicates a single value is expected.
    
         $days               # the simple scalar value "days"
         $days[28]           # the 29th element of array @days
         $days{'Feb'}        # the 'Feb' value from hash %days
         $#days              # the last index of array @days
    

    So your example would be:

         %$arg_ref           # hash dereferenced from the value "arg_ref"
    

    my($arg_ref) = @_; grabs the first item in the function's argument stack and places it in a local variable called $arg_ref. The caller is responsible for passing a hash reference. A more canonical way to write that is:

    my $arg_ref = shift;
    

    To create a hash reference you could start with a hash:

    some_sub(\%hash);
    

    Or you can create it with an anonymous hash reference:

    some_sub({pi => 3.14, C => 4}); # Pi is a gross approximation.
    
    chaos : Prototyping can be used to remove the reference-passing responsibility from the caller.
    Jon Ericson : I don't think teaching prototyping the new Perl programmers is a good idea. It was a big barrier coming from C and Pascal. I needed to learn the true value of passing lists before prototypes made any sense to me. It might be handy here, however.
    brian d foy : The $ sigil does not mean a scalar variable. It means a scalar *value*, as in $hash{key} and $array[$num]. In this case it's a scalar variable because it's a scalar value and there is no indexing chars after it.
    Jon Ericson : I don't think I said $ indicated a variable of any sort. But I went ahead and added quote from perldata that draws out the meaning of the $ sigil.
    Schwern : @chaos Perl prototyping bad. Avoid! Flee! Not only is it difficult to use and loaded with traps, but the user thinks they're passing a copy to a subroutine when they're really passing a reference which the routine might modify.
  • Instead of dereferencing the entire hash like that, you can grab individual items with

    $arg_ref->{key}
    
  • A good brief introduction to references (creating them and using them) in Perl is perldoc perfeftut. You can also read it online (or get it as a pdf). (It talks more about references in complex data structures than in terms of passing in and out of subroutines, but the syntax is the same.)

  • Since it's somewhat clear this construct is being used to provide a hash reference as a list of named arguments to a sub it should also be noted that this

    sub foo {
      my ($arg_ref) = @_;
      # do something with $arg_ref->{keys}
    }
    

    may be overkill as opposed to just unpacking @_

    sub bar {
      my ($a, $b, $c) = @_;
      return $c / ( $a * $b );
    }
    

    Depending on how complex the argument list is.

  • my %hash = ( fred => 'wilma',
                 barney => 'betty');
    my $hashref = \%hash;
    my $freds_wife = $hashref->{fred};
    my %hash_copy = %$hash # or %{$hash} as noted above.
    

    Soo, what's the point of the syntax flexibility? Let's try this:

    my %flintstones = ( fred => { wife => 'wilma',
                                  kids => ['pebbles'],
                                  pets => ['dino'],
                                }
                        barney => { # etc ... }
                      );
    

    Actually for deep data structures like this it's often more convenient to start with a ref:

    my $flintstones = { fred => { wife => 'Wilma',
                                   kids => ['Pebbles'],
                                   pets => ['Dino'],
                                  },
                       };
    

    OK, so fred gets a new pet, 'Velociraptor'

    push @{$flintstones->{fred}->{pets}}, 'Velociraptor';
    

    How many pets does Fred have?

    scalar @ {flintstones->{fred}->{pets} }

    Let's feed them ...

    for my $pet ( @ {flintstones->{fred}->{pets} } ) {
        feed($pet)
    }
    

    and so on. The curly-bracket soup can look a bit daunting at first, but it becomes quite easy to deal with them in the end, so long as you're consistent in the way that you deal with them.

0 comments:

Post a Comment