-->

Perl hashes don't work as expected

2019-07-18 12:14发布

问题:

#!/usr/bin/perl

sub f { {
  a => 1,
  b => 2
} }

sub g { {
  %{f()},
  c => 3,
  d => 4,
} }

use Data::Dumper;
print Dumper g();

The above code outputs

$VAR1 = 'a';
$VAR2 = 1;
$VAR3 = 'b';
$VAR4 = 2;
$VAR5 = 'c';
$VAR6 = 3;
$VAR7 = 'd';
$VAR8 = 4;

despite in my understanding it should output

$VAR1 = {
          'a' => 1,
          'c' => 3,
          'b' => 2,
          'd' => 4
        };

What is my misunderstanding?

回答1:

The problem is that a pair of braces is ambiguous in Perl, and may be either a block or an anonymous hash

Because of the contents of the hash in your g (please use better names) perl has assumed that you are writing a code block, which is just a list of scalar values

Make it more explicit like this and your code will function as you expect

use strict;
use warnings 'all';

sub foo {
    {
        a => 1,
        b => 2,
    }
}

sub bar {
    my $href = {
        %{ foo() },
        c => 3,
        d => 4,
    }
}

use Data::Dump;
dd bar();

output

{ a => 1, b => 2, c => 3, d => 4 }


回答2:

The Perl language has ambiguities. Take for example

sub some_sub {
   {  }     # Is this a hash constructor or a block?
}

{ } is valid syntax for a block ("bare loop").
{ } is valid syntax for a hash constructor.
And both are allowed as a statement!

So Perl has to guess. Perl usually guesses correctly, but not always. In your case, it guessed correctly for f(), but not for g().

To fix this, you can give Perl "hints". Unary-+ can be used to do this. Unary-+ is a completely transparent operator; it does nothing at all. However, it must be followed by an expression (not a statement). { } has only one possible meaning as an expression.

+{ }   # Must be a hash constructor.

Similarly, you can trick Perl to guess the other way.

{; }   # Perl looks ahead, and sees that this must be a block.

So in this case, you could use

sub g { +{
  %{f()},
  c => 3,
  d => 4,
} }

or

sub g { return {
  %{f()},
  c => 3,
  d => 4,
} }

(return must also be followed by an expression if anything.)