1. 有關function和subroutine。本書中的function指的是Perl built-in的函數,subroutine指的是user-defined 函數,本質上來說,function和subroutine是一回事。和Pascal不一樣,Pascal中的function和subroutine是 不一樣的,function有傳回值,而subroutine是沒有傳回值的。
2. Defining a Subroutine. 很簡單:
-
Code: Select all
-
sub marine {
$n += 1; # Global variable $n
print "Hello, sailor number $n!\n";
}
用 關鍵字sub就定義了一個名為marine的subroutine,注意subroutine中的n變數是一個global variable,後面會介紹如何定義一個局部變數。Perl中的subroutine的代碼可以放置在代碼的任何地方,一般我們都會把 subroutine的代碼放在開頭部分(和C的習慣一樣)。如果我們在代碼中定義了兩個同名的subroutine,那麼後定義的subroutine 會覆蓋前面那個subroutine。
3. Invoking a Subroutine. 很簡單,直接用&就表示調用一個subroutine:
&marine; # says Hello, sailor number 1!
&marine; # says Hello, sailor number 2!
&marine; # says Hello, sailor number 3!
&marine; # says Hello, sailor number 4!
4. Return Values. 這一點Perl和其他的語言很不一樣。Perl預設會把subroutine中最後一句被執行的代碼的傳回值/執行結果作為整個subroutine的return value,除非我們顯式的用return語句。
-
Code: Select all
-
sub sum_of_fred_and_barney {
print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
$fred + $barney; # That's the return value
}
上例中,$fred + $barney的結果就是整個subroutine的傳回值。所以,千萬注意最後一句代碼,免得傳回值出錯哦,比如:
-
Code: Select all
-
sub sum_of_fred_and_barney {
print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
$fred + $barney; # That's not really the return value!
print "Hey, I'm returning a value now!\n"; # Oops!
}
最後多了一句print,所以,整個subroutine的return value就發生變化了,就變成print的傳回值了,print在成功的時候返回1(true),列印失敗返回0。
再看這個例子:
-
Code: Select all
-
sub larger_of_fred_or_barney {
if ($fred > $barney) {
$fred;
} else {
$barney;
}
}
subroutine的傳回值是最後一句被執行的代碼,不是最後一句代碼哦,所以上例中,subroutine會返回兩個變數中較大的那個。
5. Arguments. 本節講述如何定義和使用subroutine arguments。Perl中給subroutine傳遞參數是通過List的方式做的:
$n = &max(10, 15); # This sub call has two parameters
這 樣就把List (10, 15)傳遞給了max subroutine,此時Perl會把這個List存在Perl的預設Array @_中,然後我們用$_[0], $_[1]...這樣的方式就可以引用這些arguments。注意這裡其實變數是_,加上了@符號就表示引用整個List,用$符號就表示引用List 中的一個元素,這在第三章已經詳細討論過了。第三章中講foreach結構的時候我們還講述了$_這個default variable,意思和這裡的是一樣的。
-
Code: Select all
-
sub max {
# Compare this to &larger_of_fred_or_barney
if ($_[0] > $_[1]) {
$_[0];
} else {
$_[1];
}
}
於 是代碼就變成了上面的樣子。但是上面的代碼無疑是可讀性較差的,下一節會講述如何改變這種狀況。而且上面的代碼還有一個問題,就是調用max的時候給了三 個參數怎麼辦?很顯然,第三個參數會被ignore;如果調用max的時候給了一個參數的話,第二個參數就是undef。
OK, 本節最後講一個非常重要的東西:每個subroutine都有一個自己的@_,比如我們一個subroutine中調用另外一個subroutine(帶 參數的),那麼這兩個subroutine都有自己的@_,我們不用擔心調用了其他的subroutine會影響到自己的@_ array,Perl會為我們自動處理這些事情,所以不用擔心。
6. Subroutine中的局部變數(Private Variable)。如下, 使用my這個關鍵字:
-
Code: Select all
-
sub max {
my($m, $n); # new, private variables for this block
($m, $n) = @_; # give names to the parameters
if ($m > $n) { $m } else { $n }
}
上 例中,我們用my關鍵字定義了一個List,然後將參數array @_賦給了這個list,所以,後面我們可以用$m, $n來表示兩個參數了。而且$m和$n是max subroutine的局部變數,和別人沒關係。沒有使用my定義的變數,Perl都把他們認為是global variable。
上面的代碼中還有一個注意點,請注意最後一句代碼中,$m和$n後面都沒有分號結尾,這是Perl的一個規定,在if從句的最後一句代碼處,可以不寫分號,但是規範一點我們還是寫上比較好,上面的例子只是代碼非常簡單,而且可以寫在一行,所以就省略了分號。
將上面的代碼再精簡一些,就成了非常常用的代碼模板了:
-
Code: Select all
-
sub max {
my($m, $n) = @_; # Name the subroutine parameters
if ($m > $n) { $m } else { $n }
}
7. Variable-Length Parameter List. 本節講述如何處理可變長度的參數列表,也就是說,本節中實現的max不限參數個數的多少,一律給出這些參數中最大值的那個。首先來看如何沒有這種可變長度 的結構,我們規定max只能接受兩個參數的話,那麼max subroutine可能會這樣寫來增強容錯性:
-
Code: Select all
-
sub max {
if (@_ != 2) {
print "WARNING! &max should get exactly two arguments!\n";
}
# continue as before...
.
.
.
}
OK?把@_用在了Scalar Context中,@_就返回參數的個數。
然後看如何不限參數個數:
-
Code: Select all
-
$maximum = &max(3, 5, 10, 4, 6);
sub max {
my($max_so_far) = shift @_; # the first one is the largest yet seen
foreach (@_) { # look at the remaining arguments
if ($_ > $max_so_far) { # could this one be bigger yet?
$max_so_far = $_;
}
}
$max_so_far;
}
上述的代碼比較好理解,首先我們用shift取出第一個元素,同時List少掉了一個元素,然後用foreach迴圈遍曆Array,一個個的相 比,然後給出最大值的那個max_so_far。這裡看foreach中我們沒有明確定義control variable,所以,就可以用$_來表示control variable,這是第三章學的內容。用這樣的代碼就可以處理變長的參數列表了。
最後需要考慮的問題是,上述的代碼能應付empty parameter list嗎?來看一下,如果是empty list,那麼shift返回undef,foreach結構中的代碼一次都不會被執行,最後返回的result就是undef,OK,看來這也是期望的 結果。不過這隻是一個例子,我們在書寫任何subroutine的時候,都要考慮參數列表是空列表的情況哦。