Perl Subroutine Prototype乍看之下是Perl提供了为Subroutine严格定义输入参数类型的一种途径。我们来看一下它的大致用法:
sub only_one_scalar_param($) { } only_one_scalar_param 1; #正确 only_one_scalar_param(1); #正确 only_one_scalar_param(1, 2); #编译期间报错
要让Perl Subroutine Prototype能够正常工作,有如下几个条件
#定义在调用之后 only_one_scalar_param(1); #正确 only_one_scalar_param(1, 2); #正确,prototype没起作用 only_one_scalar_param 1; #编译期间报错 sub only_one_scalar_param($) { }
sub only_one_scalar_param($) { } &only_one_scalar_param 1; #正确 &only_one_scalar_param(1); #正确 &only_one_scalar_param(1, 2) #正确,prototype没起作用
那Prototype的到底是用来做什么的呢? 在某些时候,Prototype确实可以用来检验输入参数(如最一开始的例子),并在编译期间就提示错误信息,但是实际上Prototype并不是如通常想象的那样,下面看一个例子
sub only_one_scalar_param($) { my( $value ) = @_; print "$value\n"; } only_one_scalar_param(10); #输出的是10 only_one_scalar_param(qw/1 2 3/); #传入的是个list。 ?!没报错,输出的是3
第一次调用的输出结果很容易让人理解,不过为什么第二次不仅没有错误,而且还输出的是3呢?
因为prototype定义的第一个参数是一个scalar,因此当遇上qw/1 2 3/这个list时,就将其转换成了scalar,即该list的长度。
那Prototype到底有什么用?
1. 省略括号
my @results = myfunc 3, 5; #相当于 my @results = myfunc(3, 5); #如果定义了prototype sub myfunc($); my @results = myfunc 3, 5; #相当于 my @results = (myfunc(3), 5);
sub no_param(); #不可以使用参数
2. 编写类似built-in function的函数样式
sub mypush(\@@) { my( $array_ref, @to_push ) = @_; push @{$array}, @to_push; } mypush @array, 1, 2, 3;
使用Prototype可以让我们写出类似built-in function的subroutine,这是使用prototype的主要目的
在其他情况下,使用Prototype是危险的!
再来看一个例子
use List::Util qw( min max ); sub clip_to_range($$@) { my ($min, $max, @data) = @_; return map { max( $min, min($max, $_) ) } @data; } clip_to_range(1, 2, (5..10)); #min是1, max是2,data是(5..10) my $range_ref = [1, 2]; #独立定义了一个range clip_to_range(@{$range_ref}, (5..10)); #使用这个range, 想表达和上面一次调用相同的意思,但是... #min是2, max是5,data是(6..10)
为什么min是2呢? 因为@{$range_ref}被当成了第一个scalar参数,被转换成了长度,所以是2。而max则应该是下一个参数,Perl就从(5..10)中把5当做了max,余下的(6..10)就作为data。
程序真正执行的和自己预期的不相同,往往容易导致产生了Bug但是查不出来,因此最好避免这种情况的发生。上面的这个例子正是《Perl Best Practices》中的一条:“Don't use subroutine prototypes.”