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能夠正常工作,有如下幾個條件
- subroutine的申明或者定義需要在編譯期間可見,通俗點講就是要在調用之前。
- 舊的Perl subroutine的調用是在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.”