I am learning perl and understand that it is a common and accepted practice to unpack subroutine arguments using shift. I also understand that it is common and acceptable practice to omit function arguments to use the default @_
array.
Considering these two things, if you call a subroutine without arguments, the @_
can (and will, if using shift) be changed. Does this mean that calling another subroutine with default arguments, or, in fact, using the @_
array after this, is considered bad practice? Consider this example:
sub total { # calculate sum of all arguments
my $running_sum;
# take arguments one by one and sum them together
while (@_) {
$running_sum += shift;
}
$running_sum;
}
sub avg { calculate the mean of given arguments
if (@_ == 0) { return }
my $sum = &total; # gets the correct answer, but changes @_
$sum / @_ # causes division by zero, since @_ is now empty
}
My gut feeling tells me that using shift to unpack arguments would actually be bad practice, unless your subroutine is actually supposed to change the passed arguments, but I have read in multiple places, including Stack Overflow, that this is not a bad practice.
So the question is: if using shift is common practice, should I always assume the passed argument list could get changed, as a side-effect of the subroutine (like the &total
subroutine in the quoted example)? Is there maybe a way to pass arguments by value, so I can be sure that the argument list does not get changed, so I could use it again (like in the &avg
subroutine in the quoted text)?
In practice, many languages do both, but in such a way that it's indistinguishable from always using the stack, because the stack is needed to handle recursion (and, these days, reentrancy), and executing a subroutine without using the stack is treated purely as an optimisation (often, "inlining").
You can pass various arguments to a Perl subroutine like you do in any other programming language and they can be accessed inside the function using the special array @_. Thus the first argument to the function is in [0],thesecondisin_[1], and so on.
Using the Parameter Array (@_) Perl lets you pass any number of parameters to a function. The function decides which parameters to use and in what order.
A Perl subroutine or function is a group of statements that together performs a task. You can divide up your code into separate subroutines. How you divide up your code among different subroutines is up to you, but logically the division usually is so each function performs a specific task.
A Perl function or subroutine is a group of statements that together perform a specific task. In every programming language user want to reuse the code. So the user puts the section of code in function or subroutine so that there will be no need to write code again and again.
There is even Perl::Critic policy that will check your code and point out every function that does not have an explicit return call at the end of the function declaration. If there is nothing to return just call return; without any argument.
Inside the function Hello, Perl! Outside the function Hello, World! The local is mostly used when the current value of a variable must be visible to called subroutines. A local just gives temporary values to global (meaning package) variables.
This still works in the newest versions of Perl, but it is not recommended since it bypasses the subroutine prototypes. Let's have a look into the following example, which defines a simple function and then call it. Because Perl compiles your program before executing it, it doesn't matter where you declare your subroutine. Hello, World!
In general, shift
ing from the arguments is ok—using the &
sigil to call functions isn't. (Except in some very specific situations you'll probably never encounter.)
Your code could be re-written, so that total
doesn't shift
from @_
. Using a for-loop may even be more efficient.
sub total {
my $total = 0;
$total += $_ for @_;
$total;
}
Or you could use the sum
function from List::Util
:
use List::Util qw(sum);
sub avg { @_ ? sum(@_) / @_ : 0 }
Using shift
isn't that common, except for extracting $self
in object oriented Perl. But as you always call your functions like foo( ... )
, it doesn't matter if foo
shift
s or doesn't shift
the argument array.
(The only thing worth noting about a function is whether it assigns to elements in @_
, as these are aliases for the variables you gave as arguments. Assigning to elements in @_
is usually bad.)
Even if you can't change the implementation of total
, calling the sub with an explicit argument list is safe, as the argument list is a copy of the array:
(a) &total
— calls total
with the identical @_
, and overrides prototypes.
(b) total(@_)
— calls total
with a copy of @_
.
(c) &total(@_)
— calls total
with a copy of @_
, and overrides prototypes.
Form (b) is standard. Form (c) shouldn't be seen, except in very few cases for subs inside the same package where the sub has a prototype (and don't use prototypes), and they have to be overridden for some obscure reason. A testament to poor design.
Form (a) is only sensible for tail calls (@_ = (...); goto &foo
) or other forms of optimization (and premature optimization is the root of all evil).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With