[精讚] [會員登入]
8531

[PERL] 12- 副程式

Perl 的副程式就是所謂的函數

分享此文連結 //n.sfs.tw/11707

分享連結 [PERL] 12- 副程式@新精讚
(文章歡迎轉載,務必尊重版權註明連結來源)
2019-10-25 08:49:25 最後編修
2017-08-25 22:50:43 By 張○○
 

自動目錄

PERL的副程式就是所謂的函數。

副程式的寫法

以sub 開頭接著副程式的名稱

sub function_name {
   # 副程式內容
   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這個函式庫

執行

$ ./run.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-變數的視界

END

你可能感興趣的文章

[PERL] 使用CPAN安裝模組 在Linux 上,CPAN 可以用來安裝或管理 perl 的模組,此文教你怎麼做。

[PERL] 中文字字串拆解,把中文字串逐字拆開 中文字字串拆解是門學問

[PERL]] find 和 perl 的結合--大量檔案中文字的取代 利用PERL作大量的文件取代

[PERL] 23-多執行緒 而多執行緒的程式,可在一次執行程式時間,同時進行多線程的計算,在效率上可獲得即大的提升。

[PERL] 11- 雜湊的範例 Perl 的幾個雜湊範例

[PERL] 24-呼叫系統程式及評估 Perl 如何呼叫系統程式並取回結果?

我有話要說

>>

限制:留言最高字數1000字。 限制:未登入訪客,每則留言間隔需超過10分鐘,每日最多5則留言。

訪客留言

[無留言]

隨機好文

談借錢 人借錢時手心向上頭向下,人還錢時手心向下頭向上

[Freebsd] 定時測試 ADSL 是否斷線並重連 中華電信 ADSL 雖有固定 ip,可是他卻會不定時「斷線」, 使用以下的 方法可以定時測試是否斷線,以及重新撥接。

好用的3+2碼郵遞區號查詢系統推薦 網路上找到用地址輸入判斷3+2碼郵遞區號的辨識率不高,除了這個網站…

[CodeIgniter 3] 資料庫的使用方法整理1/2 --Select的使用 [CodeIgniter 3] 資料庫的使用方法整理:Select的使用

[Wildfly10] 發佈war檔 deploy war file onto wildfly10