PERL的副程式就是所謂的函數。
副程式的寫法
以sub 開頭接著副程式的名稱
# 副程式內容
return 1; # 副程式回傳,非必要
# 在return 後的指令是不會被執行的
}
和別的語言函式定義不太一樣,就是沒有小括號讓你傳參數進去。
要回傳結果可用 return,這個指令非必要可省略。在return 後面的指令不會被執行。
副程式的叫用
&function_name; # 前面加一個 '&' &function_name( 12, 34, 56); # 傳入值 &function_name( @arr ); # 傳入陣列
此外副程式擺放的叫用順序沒有關係,因為PERL是全部載完再編譯執行,下面兩個同義:
sub p { ... } &p; # 副程式在前
等同於
&p; # 副程式在後 sub p { ... }
副程式的取值
副程式中要怎麼取得傳入的引數?
可以使用二種方法:1shift 函數或2特殊陣列@_
使用shift
shift 在陣列中有提過,是從陣列的最開始取出一個項目,取出後引數少1,再執行一次再少1...
&p (10,24,32); sub p { $_ = shift; print "$_\n"; #10 $_ = shift; print "$_\n"; #24 $_ = shift; print "$_\n"; #32 $_ = shift; print "$_\n"; #Use of uninitialized value $_ in concatenation (.) or string at.. }
上面的範例中,傳入3個引數,理應只能取用(shift)3次,若取用到第4次時,就出現錯誤。眼尖的人也許會想到,shift這個是取出陣列最開頭的項目,後面應該接著陣列,可是範例中shift後並無接著陣列,為什麼能取出引數呢?原來預設傳入的引數會放到PERL的特殊變數 @_中
使用@_
@_是預設副程式傳入的參數,若傳入的引數有3個,可以直接使用清單的方式指定。
&p (10,24,32); sub p { ($a,$b,$c) = @_; print $a, $b, $c; #102432 print "\n"; }
某些情況你可能不知道使用者會傳入幾個參數,例如要算平均數,使用者可傳多個引數,這時用 @_就最方便了
$avg= &avg (10,24,32,-15); print $avg; sub avg { $sum=0; foreach (@_){ $sum += $_; } return $sum/ @_; }
第1行 叫用副程式 avg,並傳入4個引數,回傳的結果設定給變數 $avg,這裡故意把副程式的名稱和變數取名一樣,在PERL裡面這兩個是不相干的。
第6-8行 對傳入的引數進行加總,迴圈的寫法應該不陌生了。
第9行 直接把總和除以引數的個數,注意這裡把@當成純量時就是項目的個數(前面有提過)。
這個程式看似沒有問題,但還是有缺點,就是若使用者叫用時無傳入引數,在平均時因為@_=0 (分母為0)則會出錯。因此加了一個檢查而確保。修正後的程式如下:
$avg= &avg ; print $avg; sub avg { return 0 if @_==0; $sum=0; foreach (@_){ $sum += $_; } return $sum/ @_; }
第5行 檢查傳入引數為0,則直接回傳0。
傳入引數應該注意的問題
在PERL中會把傳入的所有引數都放到陣列@_之中,假如在引數中放入陣列會發生什麼事?來看看下面的範例:
傳入一個陣列
@a= (1,3,4); &avg(@a) ; sub avg { print "@_"; # 1 3 4 }
第5行 沒問題的印出傳入的@a
傳入二個陣列
如果傳入二個或多個陣列,結果就不會是你要的
@a= (1,3,4); @b= (6,7,8); &avg(@a,@b); sub avg { print "@_"; # 1 3 4 6 7 8 }
還記得在 08-陣列 #2 --操作 提過,PERL的陣列全是一元的,如果把多個陣列放到引數後,所有的引數都會被攤平成為一個 @_,如果使用 shift 取引數和你想的不一樣。
@a= (1,3,4); @b= (6,7,8); &avg(@a,@b); sub avg { @p= shift; @q= shift; print "@p\n"; # 1 print "@q\n"; # 3 }
第6-7行 原本以為可以取得原本傳入的2個陣列引數,結果竟然是被攤平了,只取取得了1和3。
所以知道多個陣列當成引數時,都自動被合成一個一維的陣列。
同時傳入變數和陣列
同時傳入變數和陣列的話,變數要先寫,陣列放最後,並用清單的方式就能取出資料。
下面的例子把變數$z放最後一個引數,並觀察它被陣列@r吃掉了。
$x= 123; $y=555; $z='ABC'; @b= (6,7,8); &avg($x,$y,@b,$z); sub avg { ($p,$q,@r,$z)= @_; print "$p\n"; # 123 print "$q\n"; # 555 print "@r\n"; # 6 7 8 ABC print "$z\n"; # Use of uninitialized value $z in concatenation (.) or string at .. }
第9行 @r陣列把後面的引數全部收攏當成他的。
第10行 在陣列@r後的$z 取不到資料,出了錯。
1. 無論傳入幾個變數或陣列,都會被合併成一個一維陣列@_
2. 傳入的引數有陣列的話,要放在最後一個引數
3. 使用清單取得傳入的引數,陣列要放最後一個,剩下的引數全會被陣列收走。
4. 用shift一次只能取一個項目,不是一個引數。
學會了副程式的寫法後,我們可能會寫很多副程式並重覆的利用,最好的方法就是存成一個個只有副程式存在的檔案,叫作「函式庫」。
使用函式庫
假設先把副程式存成一個檔案叫 mylib.pl作為函式庫,裡面暫時只放一個剛才寫的求平均數副程式。
mylib.pl
sub avg { return 0 if @_==0; $sum=0; foreach (@_){ $sum += $_; } return $sum/ @_; } 1;
接下來在我們要執行的主程式中,使用 require 保留字把我們函式庫 mylib.pl 載入
run.pl
#!/usr/local/bin/perl -w require "mylib.pl"; print &avg(3,5,6,8,11);
第2行 使用require 載入mylib.pl這個函式庫
執行
6.6
在上面的例子中,也許讀者注意到 mylib.pl這裡面最後多了一個數字1加上分號:1;
感覺非常的奇怪,這是什麼東西?如果好奇把他拿掉,就出現這樣的錯誤:
mylib.pl did not return a true value at ./run.pl line 2.
首先要說PERL在某方面是很怪異的語言,1; 的意思其實是 return 1; 說真的讓你回傳一個1不是很莫名其妙嗎?那我寫成 2;可不可以?這次我不和你說結果你自己試!
但為什麼要寫一個 1; 在最後面?這是因為require+檔案時它需要一個true的回應:好吧,那就給他一個1;,這個東西在後面講到 [PERL] 18-套件及模組 時會再次出現。
這篇先介紹副程式的基本操作,使用副程式可以讓你的程式看起來更加簡潔,下一篇會說明如何建立自己的函式庫及變數的「視界」。
上一篇 11-雜湊的範例
回到目錄 01-撰寫第一隻PERL程式
下一篇 13-變數的視界