Myt*_*ium 17 perl shift subroutine
我正在学习perl并且理解使用shift解包子例程参数是一种常见且公认的做法.我也理解省略函数参数以使用默认@_数组是常见且可接受的做法.
考虑到这两件事,如果你调用一个没有参数的子程序,就@_可以改变can(并且如果使用shift).这是否意味着使用默认参数调用另一个子例程,或者事实上,@_在此之后使用数组,这被认为是不好的做法?考虑这个例子:
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
}
Run Code Online (Sandbox Code Playgroud)
我的直觉告诉我,使用shift来解包参数实际上是不好的做法,除非你的子程序实际上应该改变传递的参数,但我已经在多个地方读过,包括Stack Overflow,这不是一个坏习惯.
所以问题是:如果使用shift是常见的做法,我是否应该总是假设传递的参数列表可以更改,作为子例程的副作用(如&total引用示例中的子例程)?有没有办法按值传递参数,所以我可以确定参数列表不会被更改,所以我可以再次使用它(比如&avg引用文本中的子例程)?
amo*_*mon 20
一般来说,shift从参数中确定是正确的 - 使用&sigil来调用函数不是.(除非在一些非常具体的情况下,你可能永远不会遇到.)
你的代码可以重新编写,以便total不shift从@_.使用for循环甚至可能更有效.
sub total {
my $total = 0;
$total += $_ for @_;
$total;
}
Run Code Online (Sandbox Code Playgroud)
或者您可以使用以下sum功能List::Util:
use List::Util qw(sum);
sub avg { @_ ? sum(@_) / @_ : 0 }
Run Code Online (Sandbox Code Playgroud)
shift除了$self在面向对象的Perl中提取外,使用并不常见.但是,因为你总是把你的函数称为foo( ... ),如果s或者不是参数数组也没关系.
(关于函数唯一值得注意的是它是否分配给元素,因为这些是作为参数给出的变量的别名.分配给元素通常是坏的.)foo shiftshift@_@_
即使您无法更改实现total,使用显式参数列表调用sub也是安全的,因为参数列表是数组的副本:
(a)&total- total使用相同的@_和覆盖原型的调用.
(b)total(@_)- total带有副本的电话@_.
(c)&total(@_)- total使用副本@_和覆盖原型的调用.
表格(b)是标准的.不应该看到形式(c),除非在极少数情况下对于sub具有原型(并且不使用原型)的同一个包内的subs,并且由于某些不明原因它们必须被覆盖.证明设计不佳.形式(a)仅适用于尾调用(@_ = (...); goto &foo)或其他形式的优化(过早优化是所有邪恶的根源).