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 general,
shifting 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
totaldoesn’tshiftfrom@_. Using a for-loop may even be more efficient.Or you could use the
sumfunction fromList::Util:Using
shiftisn’t that common, except for extracting$selfin object oriented Perl. But as you always call your functions likefoo( ... ), it doesn’t matter iffooshifts or doesn’tshiftthe 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— callstotalwith the identical@_, and overrides prototypes.(b)
total(@_)— callstotalwith a copy of@_.(c)
&total(@_)— callstotalwith 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).