PerlcritiC语言 子程序原型



当我运行Perlcritic:

时,我得到这个错误

x列xx行使用的子程序原型。参见PBP第194页。(严重程度:5)

子程序为:

sub zFormatDate() {
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = shift;
  return sprintf("%04d%02d%02d%02d%02d%02d",
    $year + 1900, $mon+1, $mday, $hour, $min, $sec);
}

如果我从函数中删除关键字'sub',它就会消失。

这是可以的,还是我应该看一个不同的解决方案?

不,删除sub关键字绝对是不是解决方案。如果你改变这个:

sub func($@) {
    # ...
}

:

func($@) {
    # ...
}

perlcritic不再抱怨原型——但我认为这只是perlcritic的一个小故障。没有sub关键字,那就不再是子例程定义;这是一个语法错误,如果你尝试运行它或用perl -cw检查它,你会看到。检查你的代码是否是有效的Perl并不是Perl评论家的工作;它显然是这样假设的,然后警告你关于风格的问题。如果您为它提供无效的Perl,那么所有的赌注都将取消。

现在普遍认为使用Perl原型通常不是一个好主意。

Perlcritic基于Damian Conway所著的《Perl最佳实践》一书。从第194页开始的部分标题是"不要使用子程序原型"。

这本书不是公开的,所以我不能引用或链接到这里的部分,但是chromatic有一个博客条目"原型的问题",其中说:

原型的主要问题是它们的行为不同于大多数人在第一次见到他们时都有这样的期待。原型可以改变的解析和它们可以强制的类型参数。它们不能作为数字或类型的文档子例程所期望的参数,也不将参数映射到named参数。

很容易假设Perl的原型类似于C的原型,它们声明函数期望的参数的数量和类型(以及可选的名称)。事实上,它们是完全不同的。它们的主要目的是编写模仿内置函数行为的Perl子例程,例如,不将数组平铺成列表。

参见perldoc perlsub:

这些都是非常强大的,当然,应该只在节制让世界更美好。

如消息所示,您已经使用了子例程原型。你很可能不需要它们。

当前你的子程序定义可能类似于:

sub foo()

改为:

sub foo

注意删除()以及介于两者之间的任何内容。

Perl::Critic认为子例程原型不好。这不是关于"sub"关键字,而是函数参数定义。删除"sub"会欺骗Perl::Critic使其不报告错误,但您的代码将无法运行

您可能希望在以下两种场景之一中使用子例程原型:

  1. 你想对你的函数参数执行一些"魔法",例如:

    sub fun1(@) {我的($ar_ref) = @_}

所以像

这样的调用
fun1(@args)  

不会将@args slurp到@_,而是将@args作为数组ref传递到@_[0]

  1. 你想清楚地说明函数签名

    sub fun2($$%) {}

fun2参数是一个标量,另一个标量和一个哈希ref

Perl最佳实践书给出了这两种情况的使用场景,您很容易被自己的代码搞砸。PBP给出的修复方法是:不要使用原型。

如果你还想使用它们,你可以告诉Perl::批评家不要报告原型的使用:

## no critic
# Perl::Critic will ignore any problems it sees with your code
sub func_With_prototypes ($$$)
{
return undef
}
## use critic
# Perl::Critic will report any problems it sees within your code

较新的perl有一个签名特性,虽然它仍然被标记为实验性的,但被这个规则标记为非常流行。签名是在最初的帖子之后引入的,可能是En-Motion想要的。

因为Catalyst广泛使用原型,所以原型不能被弃用。Catalyst破解了原型,提供了一个动态路由系统。

如果您使用签名特性(或Catalyst),最好的解决方案是通过~/.perlcriticrc禁用该规则。

将以下内容添加到~/.perlcriticrc

(子程序::ProhibitSubroutinePrototypes)

虽然签名在PerlCritic看来像原型,但它们是现代Perl编程的基本元素。

考虑:

subadd2nums{我的$num1 = shift;我的$num2 = shift;返回$num1 +num2美元;}

与签名:

使用特征'signatures';

subadd2nums ($num1, $num2){返回$num1 + $num2;}

我猜

sub zFormatDate() {
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = shift;
  return sprintf("%04d%02d%02d%02d%02d%02d",
    $year + 1900, $mon+1, $mday, $hour, $min, $sec);
}

打算读取

sub zFormatDate($$$$$$$$$) {
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @_;
  return sprintf("%04d%02d%02d%02d%02d%02d",
    $year + 1900, $mon+1, $mday, $hour, $min, $sec);
}

即:提供9个参数的原型,并正确设置my变量

总的来说,我认为这篇文章很有启发性:https://www.effectiveperlprogramming.com/2015/04/use-v5-20-subroutine-signatures/

tldr;

use v5.20; # or newer, currently it is v5.34
use feature qw(signatures);
no warnings qw(experimental::signatures);
sub my_func($param, $another_param, $an_optional_param=1) {
   say "$param is so $another_param"
       if $an_optional_param";
}
my_func('Chocolate', 'sweet');

最新更新