在另一个 Stack Overflow 问题中, Leon Timmermans断言:
我建议你不要使用原型。它们有它们的用途,但在大多数情况下不是,而且绝对不是在这种情况下。
为什么这可能是真的(或其他)?我几乎总是为我的 Perl 函数提供原型,而且我以前从未见过其他人说使用它们不好。
在另一个 Stack Overflow 问题中, Leon Timmermans断言:
我建议你不要使用原型。它们有它们的用途,但在大多数情况下不是,而且绝对不是在这种情况下。
为什么这可能是真的(或其他)?我几乎总是为我的 Perl 函数提供原型,而且我以前从未见过其他人说使用它们不好。
如果使用得当,原型还不错。困难在于 Perl 的原型并不像人们通常期望的那样工作。具有其他编程语言背景的人倾向于期望原型提供一种机制来检查函数调用是否正确:也就是说,它们具有正确数量和类型的参数。Perl 的原型并不适合这项任务。滥用是不好的。Perl 的原型有一个独特且非常不同的目的:
原型允许您定义行为类似于内置函数的函数。
例如,您可以定义这样的函数:
sub mypush(\@@) { ... }
并将其称为
mypush @array, 1, 2, 3;
无需编写\
即可引用数组。
简而言之,原型让您可以创建自己的语法糖。例如 Moose 框架使用它们来模拟更典型的 OO 语法。
这非常有用,但原型非常有限:
有关所有血腥细节,请参阅perlsub 中的原型。
问题是 Perl 的函数原型并不像人们认为的那样做。它们的目的是允许您编写将像 Perl 的内置函数一样被解析的函数。
首先,方法调用完全忽略原型。如果你在做 OO 编程,你的方法有什么原型并不重要。(所以他们不应该有任何原型。)
其次,原型没有严格执行。如果用 调用子例程&function(...)
,原型将被忽略。所以它们并没有真正提供任何类型安全。
第三,它们是令人毛骨悚然的远距离动作。(特别是$
原型,它导致在标量上下文中评估相应的参数,而不是默认的列表上下文。)
特别是,它们使从数组传递参数变得困难。例如:
my @array = qw(a b c);
foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);
sub foo ($;$$) { print "@_\n" }
foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);
印刷:
a b c
a b
a b c
3
b
a b c
以及 3 个关于main::foo() called too early to check prototype
(如果启用了警告)的警告。问题是在标量上下文中评估的数组(或数组切片)返回数组的长度。
如果您需要编写一个像内置函数一样的函数,请使用原型。否则,不要使用原型。
注意:Perl 6 将有完全改进和非常有用的原型。此答案仅适用于 Perl 5。
我同意以上两张海报。一般来说,$
应该避免使用。原型仅在使用块参数 ( &
)、glob ( *
) 或引用原型 ( \@
, \$
, \%
, \*
)时有用
有些人在查看 Perl 子例程原型时,认为它的含义并非如此:
sub some_sub ($$) { ... }
对于 Perl,这意味着解析器需要两个参数。这是 Perl 让您创建行为类似于内置程序的子程序的方式,所有这些程序都知道从后续代码中可以期待什么。你可以在perlsub中阅读原型
如果不阅读文档,人们会猜测原型指的是运行时参数检查或他们在其他语言中看到的类似内容。就像人们猜测 Perl 的大多数事情一样,事实证明他们是错误的。
然而,从 Perl v5.20 开始,Perl 有一个特性,在我写这篇文章时是实验性的,它提供的东西更像用户的期望和什么。Perl 的子程序签名确实运行时参数计数、变量分配和默认设置:
use v5.20;
use feature qw(signatures);
no warnings qw(experimental::signatures);
animals( 'Buster', 'Nikki', 'Godzilla' );
sub animals ($cat, $dog, $lizard = 'Default reptile') {
say "The cat is $cat";
say "The dog is $dog";
say "The lizard is $lizard";
}
如果您正在考虑原型,这可能是您想要的功能。