在perl中,用默认参数调用多个子程序是不好的做法吗?

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来调用函数不是.(除非在一些非常具体的情况下,你可能永远不会遇到.)

你的代码可以重新编写,以便totalshift@_.使用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)或其他形式的优化(过早优化是所有邪恶的根源).

  • `goto&foo`和`&foo`不是一回事.前者用于堆栈操作(例如,使`AUTOLOAD`透明).后者有时是一个小便利(例如`sub warn {unshift @_,'warn';&log}`). (3认同)
  • 有一个强大的反和阵营,但他们的主要论点是你可能会意外地离开括号并在你不想要它时获得@ _-传播(和丑陋因素).我已经在代码库上工作,其中所有非CPAN函数都用&调用; 当你习惯它时,它并没有那么糟糕.与其他一切一样,一致性通常比您的一致性更重要. (3认同)