为什么Perl :: Critic不喜欢使用shift来填充子程序变量?

时间:2010-02-16 18:33:05

标签: perl perl-critic

最近,我决定更频繁地在我的代码上使用Perl::Critic。在Perl编程近7年后,我已经习惯了大部分Perl最佳实践,但我知道总有改进的余地。但是有一件事让我烦恼的是,Perl::Critic不喜欢我为子程序解包@_的方式。举个例子:

sub my_way_to_unpack {
    my $variable1 = shift @_;
    my $variable2 = shift @_;

    my $result = $variable1 + $variable2;
    return $result;
}

这就是我一直这样做的方式,并且正如PerlMonks和Stack Overflow上所讨论的那样,它也是not necessarily evil

将上面的代码段更改为...

sub perl_critics_way_to_unpack {
    my ($variable1, $variable2) = @_;

    my $result = $variable1 + $variable2;
    return $result;
}

......也有效,但我发现它更难阅读。我还阅读了达米安康威的书Perl Best Practices,我不太明白我的首选解包方式是否属于他的建议,以避免直接使用@_,如Perl::Critic所暗示的那样。我总是认为康威在讨论如下的肮脏:

sub not_unpacking {
    my $result = $_[0] + $_[1];
    return $result;
}

以上示例很糟糕且难以阅读,我永远不会考虑在一段生产代码中编写它。

简而言之,为什么Perl::Critic认为我的首选方式不好?我是否真的犯了一个令人发指的罪行,使用轮班拆包?

这会是我自己认为应该与Perl::Critic维护者一起提出的吗?

5 个答案:

答案 0 :(得分:11)

简单的答案是Perl :: Critic在这里没有关注PBP。该 本书明确指出转换成语不仅可以接受,而且 实际上在某些情况下是首选。

答案 1 :(得分:9)

使用perlcritic运行--verbose 11说明了这些政策。但是,看起来这些解释都不适用于你。

Always unpack @_ first at line 1, near 
'sub xxx{ my $aaa= shift; my ($bbb,$ccc) = @_;}'.
  Subroutines::RequireArgUnpacking (Severity: 4)
    Subroutines that use `@_' directly instead of unpacking the arguments to
    local variables first have two major problems. First, they are very hard
    to read. If you're going to refer to your variables by number instead of
    by name, you may as well be writing assembler code! Second, `@_'
    contains aliases to the original variables! If you modify the contents
    of a `@_' entry, then you are modifying the variable outside of your
    subroutine. For example:

       sub print_local_var_plus_one {
           my ($var) = @_;
           print ++$var;
       }
       sub print_var_plus_one {
           print ++$_[0];
       }

       my $x = 2;
       print_local_var_plus_one($x); # prints "3", $x is still 2
       print_var_plus_one($x);       # prints "3", $x is now 3 !
       print $x;                     # prints "3"

    This is spooky action-at-a-distance and is very hard to debug if it's
    not intentional and well-documented (like `chop' or `chomp').

    An exception is made for the usual delegation idiom
    `$object->SUPER::something( @_ )'. Only `SUPER::' and `NEXT::' are
    recognized (though this is configurable) and the argument list for the
    delegate must consist only of `( @_ )'.

答案 2 :(得分:8)

重要的是要记住Perl Best Practices中的很多内容只是一个人对最好或最容易合作的看法,如果你以另一种方式做到这一点并不重要。达米安在书的介绍性文本中也说了很多。这并不是说它就像所有那样 - 那里有许多绝对必要的东西:例如使用strict

因此,在编写代码时,您需要自己决定自己的最佳实践,并且使用PB​​P是一个很好的起点。然后保持符合自己的标准。

我尝试跟踪PBP中的大部分内容,但Damian可以拥有我的子例程 - shift和我的unless es,当他从我冷酷的指尖撬开它们时。

至于评论家,您可以选择要强制执行的政策,如果它们尚不存在,甚至可以创建自己的政策。

答案 3 :(得分:2)

在某些情况下,Perl :: Critic无法准确地执行PBP指南,因此它可能会强制执行与Conway指南精神相匹配的近似值。我们完全有可能误解或误用了PBP。如果您发现无法闻到的东西,请将错误报告发送至bug-perl-critic@rt.cpan.org,我们会立即对其进行调查。

谢谢,

-Jeff

答案 4 :(得分:0)

我认为如果不是真的有必要,你应该避免转移!

刚刚遇到这样的代码:

sub way {
  my $file = shift;
  if (!$file) {
    $file = 'newfile';
  }
  my $target = shift;
  my $options = shift;
}

如果您开始更改此代码中的某些内容,您很可能会有意义地更改班次的顺序,或者可能会跳过一个并且一切都会向南移动。此外,它很难阅读 - 因为你不能确定你真的看到sub的所有参数,因为下面的某些行可能是某个地方的另一个转移......如果你在它们之间使用一些Regex,它们可能会替换$ _的内容怪异的东西开始发生......

使用解压缩我的(...)= @_的直接好处是你可以复制(...)部分并将其粘贴到你调用方法的地方并有一个很好的签名:)你甚至可以使用预先使用相同的变量名称,而不必更改任何内容!

我认为shift意味着列表操作,其中列表的长度是动态的,并且您希望一次处理一个元素,或者您明确需要没有第一个元素的列表。但是如果你只想将整个列表分配给x参数,你的代码应该用我的(...)= @_;没有人不得不怀疑。