Reputation: 3538
Is the order in which parameters are passed to a Perl subroutine significant? When I debugged the following code:
my $loopCounter = 1;
foreach(@authors) {
my @splitAffiliations = splitAffiliations($params);
my @foundItems = findAffiliationForAuthor($_, @splitAffiliations, $loopCounter);
# Process @foundItems
$loopCounter++;
}
...
sub findAffiliationForAuthor {
my ($author, @affiliations, $aIndex) = @_;
...
}
I found that the variable $loopCounter
had a value immediately before the subroutine findAffiliationForAuthor
was called, but was undefined after the call. So, the subroutine received values for the first two parameters and nothing for the third. But, when I changed the order of the parameters to:
my @foundItems = findAffiliationForAuthor($loopCounter, $_, @splitAffiliations);
The value of $loopCounter
was retained and passed the subroutine as expected.
It looks like I have to put all my scalar variables before my array variables, or maybe not mix them. Does that sound right?
Upvotes: 1
Views: 455
Reputation: 386541
Subroutines take a list of scalars as arguments. If you build the argument list from an array, the contents of the array will be passed.
$ perl -E'sub f { say "$_: $_[$_]" for 0..$#_; } f("a", "b", "c", "d", "e")'
0: a
1: b
2: c
3: d
4: e
$ perl -E'sub f { say "$_: $_[$_]" for 0..$#_; } @a = ("b"); f("a", @a, "c", "d", "e")'
0: a
1: b
2: c
3: d
4: e
$ perl -E'sub f { say "$_: $_[$_]" for 0..$#_; } @a = ("b", "c"); f("a", @a, "d", "e")'
0: a
1: b
2: c
3: d
4: e
Now you assign the argument list (the contents of @_
) to
($author, @affiliations, $aIndex)
How many of the scalars should it assign to @affiliations
? You could say all but the first and the last, but then you have to come up for a new rule for
my (@a, @b) = @_;
To keep it simple, Perl always assigns all of the remaining scalars to the first array in the list. This works fine if the array is the last element, but not otherwise.
If you want to pass an array to a sub, the best way is to pass a reference to it.
$ perl -E'sub f { say "$_: $_[$_]" for 0..$#_; } @a = ("b", "c"); f("a", \@a, "d", "e")'
0: a
1: ARRAY(0x14ff8d0)
2: d
3: e
Upvotes: 2
Reputation: 263577
Certainly the order matters.
Arguments are passed in the order in which you pass them, and an array argument is flattened into the argument list. The argument list appears inside the subroutine as the contents of the @_
list.
You can't actually pass an array as an argument; its elements are extracted and shoved into the list with any other arguments.
The problem you're having is because of the way you're processing the arguments:
my ($author, @affiliations, $aIndex) = @_;
$author
gets the first element of @_
, as you intended, but @affiliations
slurps up all the rest of the list. The value undef
is then stored in $aIndex
.
You can pass a single array to a subroutine as long as it's the last argument:
my($first_scalar, $second_scalar, @array) = @_;
If you pass multiple arrays to a subroutine:
foo(@array1, @array2);
the elements of both arrays are flattened into a single list, and there's no way for the subroutine to know which elements came from which array argument.
You can pass references to arrays:
sub foo {
my ($scalar1, $array_ref1, $scalar2, $array_ref2) = @_;
# ...
}
and then call it like this:
foo(42, \@array1, $some_scalar, \@array2);
Upvotes: 5