将@_转换为Perl中的参数传递有什么好处?

时间:2014-01-30 09:06:39

标签: arrays perl function arguments shift

sub foo {
 $arg1 = shift @_;
 $arg2 = shift @_;
 # ...
}

这个成语有什么好处?与明确使用$_[0]$_[1]相比,我认为只有缺点...数组必须移位,这非常耗时。它被破坏了,所以在稍后的时间点,参数已经消失了(如果你再次需要它们并且用不同的值覆盖你的$ arg1,那就很难过。)

4 个答案:

答案 0 :(得分:9)

OO perl中移动@_很常见,因此参数可以与类的实例分开,该类自动添加为@_的第一个元素。

在为输入参数指定默认值时,它也可以用来少写,虽然我个人觉得它没什么吸引力,

sub foo {
  my $arg1 = shift // 2;
  my $arg2 = shift // 7;
  # ...
}
  

与明确使用$ [0],$ 1相比,我认为只有缺点,...

不是使用$_[0], $_[1],而是一次性分配整个@_数组更好/更不容易出错/更具可读性。

my ($arg1, $arg2) = @_;

另请注意,@_元素的别名是传递变量,因此可能会发生意外更改,

sub foo {
  $_[0] = 33;
  $_[1] = 44;
}

my ($x, $y) = (11,22);

print "($x, $y)\n";
foo($x, $y);
print "($x, $y)\n";

输出

(11, 22)
(33, 44)

答案 1 :(得分:5)

直接访问$_[0]等比使用命名参数 [1] 更快,但是有很多理由更喜欢命名参数。

  1. 到目前为止,最重要的原因是使用命名参数作为一种文档形式。

  2. 直接使用@_很难阅读,因为很难跟踪哪个参数具有什么价值。

    例如,“$_[4]包含哪些内容?是行吗?还是$_[5]?”

  3. 偶然使用错误的索引很容易。

    • 输入错误的号码很容易。
    • 当你想要另一个号码时,很容易想到你想要一个号码。
    • 更改子参数时很容易忽略索引。

  4. 由于符号数量的原因,直接使用@_很难阅读。

    比较

    grep { $_[0]{$_} =~ /.../ } keys %{$_[0]}
    

    grep { $foos->{$_} =~ /.../ } keys %$foos
    
  5. 提供默认值非常有用。

    sub f {
       my $x = shift // "abc";
       my $y = shift // "def";
       ...
    }
    
  6. 复制到标量有效地引入了逐个拷贝的语义,这可能很有用。

    $ perl -le'my $x=123; sub f {                  $x=456; print $_[0]; } f($x);'
    456
    
    $ perl -le'my $x=123; sub f { my $arg = shift; $x=456; print $arg;  } f($x);'
    123
    

  7. 注意:

    1. 是否是我的首选

      sub f {
         my (...) = @_;
         ...
      }
      

      sub f {
          my ... = shift; 
          my ... = shift;
          ...
      }
      

答案 2 :(得分:4)

  

我同意所有观点。但基本问题仍然存在:我为什么要转移而不是进行列表赋值或一系列标量赋值?

由于我做了转移,我会解释为什么要这样做。

有三种方法可以处理子程序的参数:

方法1:列表分配

 sub foo {
    my ( $user, $date, $system ) = @_;   # No shifting, and @_ preserved

方法2:来自@_

的个人作业
 sub foo {
    my $user   = $_[1];  
    my $date   = $_[2];   # Must be YYYY-MM-DD
    my $system = $_[3];   # Optional

方法3:使用“shift”

sub foo {
    my $user    = shift;
    my $date    = shift;    # Must be YYYY-MM-DD
    my $system  = shift;    # Optional

方法1 很受欢迎。 Ikegami使用它,许多其他高级Perl开发人员也是如此。它为您提供了一个参数列表,并为您提供了一种方式来说明这些是我的参数而没有其他

但是,方法2 方法3 为您提供了一个简单易读的参数列表,您可以在该行的末尾添加注释。但是,方法2 还具有保持@_值的优势,那么为什么要使用shift

再次查看方法2 。看到问题?我从@_[1]而不是@_[0]开始 - 这是一个常见错误。另外,在开发子程序时,您可能决定重新排序参数。使用方法2 表示重新编号。 方法3 不需要重新编号,因此您最终不会这样做:

 sub foo {
    my $date   = $_[1];   # Must be YYYY-MM-DD
    my $user   = $_[0];  
    my $system = $_[2];   # Optional
嗯,再次参数的顺序是什么?

那么,保留@_的价值呢?如果您编写一个更改参数值的程序,则可能不会从@_恢复它。而且,如果你这样做,你可能会产生一些容易出错的代码。如果你需要munge一个参数,把它放在另一个变量中并进行操作。这不是1975年计算机只有4千字节的内存。

而且,谈到20世纪70年代,计算机足够快,以至于少数(十二,一百,几千)个班次操作的时间量不会在总运行时间上产生那么大的差异。如果您的程序效率低下,请在效率低下的地方进行优化,而不是在几毫秒内消除轮班。维护程序所花费的时间比实际运行时间要长。


Damian Conway(Perl Best Practices的作者)建议方法#1 方法#3 。他声明方法#1 “...更简洁,它将参数保存在一个水平列表中,这增强了可读性,只要参数的数量是小。(重点是我的)。

Damian说明了方法#3 “无论何时,只要一个或多个参数必须经过健全检查或需要记录尾随,基于班次的版本更可取评论“。

我一直只使用方法#3 。这样,如果我的参数列表增长,我不必担心重新格式化,我只是认为它看起来更好。

答案 3 :(得分:0)

在对象类中编写方法时,我总是首先shift调用@_调用,然后将剩余的参数解包,从而保留所有其他非调用参数。这允许呼叫站点看起来更像是方法定义本身,就其中的内容而言。

sub method
{
  my $self = shift;
  my ( $x, $y, $z ) = @_;
}

$obj->method( "x", "y", "z" );

这样,如果我必须将方法调用委托给别处,我可以简单地传递@_本身:

sub do_thing_with_whatsit
{
  my $self = shift;
  $self->{whatsit}->do_thing( @_ );
}