4

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?

Georg
  • 1,078
  • 2
  • 9
  • 18

1 Answers1

9

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;

zdim
  • 64,580
  • 5
  • 52
  • 81
  • 2
    That. Or return a hashref in the first place. – Holli Nov 12 '17 at 22:41
  • 1
    It seems artificial to create a reference only to dereference it... If that's the only way then so be it. I wonder what the overhead could be. – Georg Nov 14 '17 at 00:56
  • @Georg Well, I agree. But your sub returns a flat list, while you don't want to create another hash, to avoid later copying. So then you need a reference, but then you need a value from the structure as well. The solution is to return a hashref to start with. Other than being "better" this may actually save a data copy, since the function returns the pointer while the way you have it it returns the whole list. I say "may" since I am not entirely sure whether there are some optimizations in place, but I don't think that there can be. – zdim Nov 14 '17 at 03:31
  • @Georg I added a comment on returning by reference. – zdim Nov 14 '17 at 04:15