可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to pass parameters to a perl subroutine and for whatever reason inside the subroutine the parameters are coming out empty.
...
...
...
print "Passing arguments $a, $b, $c, $d \n";
beforeEnd($a, %b, $c, $d);
sub beforeEnd() {
my ($a, %b, $c, $d) = @_;
print "a is $a, b is $b, c is $c, d is $d \n";
}
The output of the print statements give me an idea that something is wrong. The weird part? The first 2 parameters are passing properly.
> Passing arguments 1, (1,2,3), 2, 3
> a is 1, b is (1,2,3), c is , d is
Any help would be greatly appreciated.
回答1:
Because when you pass arguments into or out of a subroutine, any hashes and arrays are smashed flat.
You are assigning into %b
which will gobble up any arguments.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub test1 {
my ( $first, @rest, $last ) = @_;
print Dumper \@rest;
print "First = $first, last = $last, rest = @rest\n";
}
sub test2 {
my ( $first, $second ) = @_;
print "@$first ; @$second";
}
test1 ( 1, 2, 3, 4 );
test2 ( [1,2], [ 3,4] );
my @list1 = ( 1,2,3,4 );
my @list2 = ( 5,6,7,8 );
test1 ( @list1, @list2 );
test2 ( \@list1, \@list2 );
If you want to keep arrays or hashes intact, you need to either pass them by reference or as the last argument.
You would also probably get a warning if you turned on strict
and warnings
here - which is one of the reasons it's strongly recommended - because $b
and %b
are not the same. You'd also get a warning about an odd number of assignments:
Odd number of elements in hash assignment at line 5.
Use of uninitialized value $b in print
回答2:
When arguments are passed to a Perl subroutine, they are flattened into a single List represented by @_
. Conceptually, this means that if you are not passing references to arrays or hashes, you will "lose" some data. "Lose" is not exactly correct, because all of the data is still there; it is just not in the variable you expect. An example of this could be:
sub f {
my (@a, @b) = @_;
say 'a: ' . join(', ', @a);
say 'b: ' . join(', ', @b);
}
f( qw(1 2 3), qw(a b c) );
You will get the following output:
a: 1, 2, 3, a, b, c
b:
This is happening because the first array @a
consumes all of the values from @_
and there a no more left to be stored in @b
. The same thing is happening with the hash in your beforeEnd subroutine. The values of $c
and $d
are getting stored inside of %b
. As an example since I can't see the variables values, if you passed
beforeEnd(1, ( a => 1, b => 2 ), 'c', 3);
inside your sub, you get something like this:
$a = 1
%b = ( a => 1, b => 2, c => 3 )
$c = undef
$d = undef
You can solve this by passing a reference to your hash %b:
beforeEnd($a, \%b, $c, $d);
回答3:
Subroutines accept a list of scalar as arguments. If you pass an array or a hash, the contents of the array or hash is passed instead. That means that
f($a, %b, $c, $d)
is the same as
f($a, $b_key_1, $b_val_1, $b_key_2, $b_val_2, $b_key_3, $b_val_3, $c, $d);
How many of the scalars in @_
should be assigned to %b
? Perl keeps it simple and assigns all remaining scalars, so
my ($a, %b, $c, $d) = @_;
is really no different than
my $a = $_[0]; # The first argument
my %b = @_[1..$#_]; # All but the first argument
my $c;
my $d;
It's best if you pass a reference to the hash. That avoids the problem, and it's far more efficient.
use Data::Dumper qw( Dumper );
sub beforeEnd {
my ($a, $b, $c, $d) = @_;
local $Data::Dumper::Terse = 1;
print "a is $a, b is ".Dumper($b).", c is $c, d is $d \n";
}
beforeEnd($a, \%b, $c, $d);
Off-topic comments about your code:
You had a prototype indicating no arguments are expected ()
, but you expect four. Rid yourself of that prototype.
You should avoid using $a
and $b
as variables as it can issues with sort
.
回答4:
The arguments can be passed in subroutine only as list of scalar variables.
We need to pass the reference of the hash (or an array, object) whenever passing the arguments to subroutine.
...
...
...
print "Passing arguments $a, $b, $c, $d \n";
beforeEnd($a, \%b, $c, $d);
sub beforeEnd() {
my ($a, $b, $c, $d) = @_;
print "a is $a, b is %$b, c is $c, d is $d \n";
}