问题
#!/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.)
来源:https://stackoverflow.com/questions/51613242/perl-hashes-dont-work-as-expected