Search code examples
perlhashsubroutine

Perl: can I skip the intermediate hash variable in this case?


At the moment, I use something like this:

my %tmpHash = routineReturningHash();
my $value = $tmpHash{'someKey'};

The only thing I need is $value, I do not need %tmpHash itself. So I am curious to know if there is a way to avoid declaring %tmpHash.

I tried

my $value = ${routineReturningHash()}{'someKey'};

but it doesn't work and outputs a strange error: "Can't use string ("1/256") as a HASH ref while "strict refs" in use".

Any ideas how it could be done?


Solution

  • Create a hashref out of the returned list, which you can then dereference

    my $value = { routineReturningHash() }->{somekey};
    

    In what you tried, the ${ ... } imposes the scalar context inside. From perlref (my emphasis)

    2.   Anywhere you'd put an identifier (or chain of identifiers) as part of a variable or subroutine name, you can replace the identifier with a BLOCK returning a reference of the correct type.

    In the scalar context the hash is evaluated to a string with fraction involving buckets; not a hashref.


    Update   I take it that there are design reasons for returning a hash as a flat list. If that isn't the case, then the clear solution is to just return a hashref from the sub.

    That also saves a data copy: When you return a hash the scalars (keys and values) need be copied, to provide a list to the caller; when you return a reference only that one scalar is returned.

    As for performance benefits ... if you can see a difference, you either have massive hashes which should be handled by reference anyway, or too many function calls what may need refactoring.

    To return by reference you can

    • form and work with a hash in the sub and then return \%hash;

    • form a hashref directly return { key => 'value', ... };

    • If you have a big hash to work with, pass its reference and work with that

      sub work_by_ref {    
          my ($hr) = @_;
          $hr->{key} = 'value';
          return 1;
      }
      
      my %hash;
      work_by_ref(\%hash);
      say "$_ => $hash{$_}" for sort keys %hash;
      

      Be careful with this C-style approach; it is not all that usual in Perl to directly change caller's data. If you only need to fill a hash in the sub then build it there and return \%hash;