考虑到这两件事情,如果你调用一个没有参数的子程序,那么@_可以(如果使用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 }
我的直觉感觉告诉我,使用转换解压缩参数实际上是不好的做法,除非你的子例程实际上应该改变传递的参数,但是我已经在多个地方阅读过,包括Stack Overflow,这不是一个糟糕的做法。
所以问题是:如果使用shift是普遍的做法,我应该总是假设传递的参数列表可能会被改变,作为子例程的副作用(如引用的例子中的& total子例程)?有没有办法通过值来传递参数,所以我可以确定参数列表没有改变,所以我可以再次使用它(例如引用的文本中的& avg子例程)?
解决方法
你的代码可以被重写,所以总数不会从@_转移。使用for循环甚至可以更有效率。
sub total { my $total = 0; $total += $_ for @_; $total; }
或者您可以使用List :: Util中的sum函数:
use List::Util qw(sum); sub avg { @_ ? sum(@_) / @_ : 0 }
使用shift不是常见的,除了在面向对象的Perl中提取$ self。但是,当你总是称之为foo(…)的函数时,如果foo移动或者不移动参数数组,这并不重要。
(值得注意的一个函数的唯一值是它是否分配给@_中的元素,因为它们是您给出的变量的别名作为参数。分配给@_中的元素通常是不好的。)
即使您无法更改总计的实现,也可以使用明确的参数列表调用该子类,因为参数列表是数组的副本:
(a)& total – 呼叫总数与相同的@_,并覆盖原型。
(b)总计(@_) – 电话总数与@_的副本。
(c)& total(@_) – 带有@_的副本,并覆盖原型。
表格(b)是标准的。不应该看到格式(c),除非在同一个包装中的sub的很少的情况下,sub具有原型(并且不使用原型),并且由于某些模糊的原因而必须被覆盖。证明设计不佳。形式(a)仅适用于尾部呼叫(@_ =(…); goto& foo)或其他形式的优化(并且过早优化是所有邪恶的根源)。