Mark
Mark

Reputation: 1650

Subroutine that returns hash - breaks it into separate variables

I have a subroutine that returns a hash. Last lines of the subroutine:

print Dumper(\%fileDetails);
return %fileDetails;

in this case the dumper prints:

$VAR1 = {
          'somthing' => 0,
          'somthingelse' => 7.68016712043654,
          'else' => 'burst'
}

But when I try to dump it calling the subroutine with this line:

print Dumper(\fileDetailsSub($files[$i]));

the dumper prints:

$VAR1 = \'somthing';
$VAR2 = \0;
$VAR3 = \'somthingelse';
$VAR4 = \7.68016712043654;
$VAR5 = \'else';
$VAR6 = \'burst';

Once the hash is broken, I can't use it anymore. Why does it happen? And how can I preserve the proper structure on subroutine return?

Thanks, Mark.

Upvotes: 6

Views: 2396

Answers (5)

friedo
friedo

Reputation: 66978

There's no such thing as returning a hash in Perl.

Subroutines take lists as their arguments and they can return lists as their result. Note that a list is a very different creature from an array.

When you write

return %fileDetails;

This is equivalent to:

return ( 'something', 0, 'somethingelse', 7.68016712043654, 'else', 'burst' );

When you invoke the subroutine and get that list back, one thing you can do is assign it to a new hash:

my %result = fileDetailsSub();

That works because a hash can be initialized with a list of key-value pairs. (Remember that (foo => 42, bar => 43 ) is the same thing as ('foo', 42, 'bar', 43).

Now, when you use the backslash reference operator on a hash, as in \%fileDetails, you get a hash reference which is a scalar the points to a hash.

Similarly, if you write \@array, you get an array reference.

But when you use the reference operator on a list, you don't get a reference to a list (since lists are not variables (they are ephemeral), they can't be referenced.) Instead, the reference operator distributes over list items, so

\( 'foo', 'bar', 'baz' );

makes a new list:

( \'foo', \'bar', \'baz' );

(In this case we get a list full of scalar references.) And this is what you're seeing when you try to Dumper the results of your subroutine: a reference operator distributed over the list of items returned from your sub.

So, one solution is to assign the result list to an actual hash variable before using Dumper. Another is to return a hash reference (what you're Dumpering anyway) from the sub:

return \%fileDetails;

...

my $details_ref = fileDetailsSub();
print Dumper( $details_ref );

# access it like this:
my $elem = $details_ref->{something};
my %copy = %{ $details_ref };

For more fun, see:

Upvotes: 11

mob
mob

Reputation: 118605

Perl functions can not return hashes, only lists. A return %foo statement will flatten out %foo into a list and returns the flattened list. To get the return value to be interpreted as a hash, you can assign it to a named hash

%new_hash = fileDetailsSub(...);
print Dumper(\%new_hash);

or cast it (not sure if that is the best word for it) with a %{{...}} sequence of operations:

print Dumper( \%{ {fileDetailsSub(...)} } );

Another approach, as TLP points out, is to return a hash reference from your function.

Upvotes: 3

Steve Sanbeg
Steve Sanbeg

Reputation: 887

You can't return a hash directly, but perl can automatically convert between hashes and lists as needed. So perl is converting that into a list, and you are capturing it as a list. i.e.

Dumper( filedetail() ) # list
my %fd = filedetail(); Dumper( \%fd ); #hash

Upvotes: 1

Mark Reed
Mark Reed

Reputation: 95267

In list context, Perl does not distinguish between a hash and a list of key/value pairs. That is, if a subroutine returns a hash, what it really returns is an list of (key1, value1, key2, value2...). Fortunately, that works both ways; if you take such a list and assign it to a hash, you get a faithful copy of the original:

my %fileDetailsCopy = subroutineName();

But if it wouldn't break other code, it would probably make more sense to have the sub return a reference to the hash instead, as TLP said.

Upvotes: 0

TLP
TLP

Reputation: 67918

Why not return a reference to the hash instead?

return \%fileDetails;

As long as it is a lexical variable, it will not complicate things with other uses of the subroutine. I.e.:

sub fileDetails {
    my %fileDetails;
    ... # assign stuff
    return \%fileDetails;
}

When the execution leaves the subroutine, the variable goes out of scope, but the data contained in memory remains.

The reason the Dumper output looks like that is that you are feeding it a referenced list. Subroutines cannot return arrays or hashes, they can only return lists of scalars. What you are doing is something like this:

print Dumper \(qw(something 0 somethingelse 7.123 else burst));

Upvotes: 8

Related Questions