Perl入門學習筆記——子程序

Learning Perl 

第四章 子程序
系統內置函數,chomp,reverse和print等。Perl也能夠建立子程序,就是用戶自定義函數。
子程序屬於獨立的名字空間,不會將同一段代碼中的子程序&fred和標量$fred混淆。

定義子程序
子程序由關鍵字sub,子程序名以及花括號封閉起來的代碼塊組成。
sub marine {
  $n += 1;
  print "hello, number $n!\n" ;
}
子程序被定義在程序中的任意位置,不須要事先聲明,是全局的,不存在所謂的私有子程序。
定義了兩個子程序,則後面的會覆蓋前面的。

調用子程序
能夠在表達式中使用子程序(前面加上與號)調用:
&marine;
&marine;

hello, number 1!
hello, number 2!

返回值
子程序被調用時必定是做爲表達式的某個部分,即便該表達式的求指結果不會被用到。
須要調用子程序並對它的返回值作進一步的處理,全部的子程序都有返回值,但不是全部的子程序都包含有用的返回值。
Larry簡化了"return",子程序執行過程當中不斷運算,而最後一次運算結果被自動看成子程序的返回值。
sub sum_of_fred_and_barnay {
  print "hi, you called the sum of fred and barnay!\n";
  $fred + $barnay;
}
$fred = 4;
$barnay = 5;
$wilma = &sum_of_fred_and_barnay;
print "\$wilma is $wilma.\n";
$betty = 3 * &sum_of_fred_and_barnay;
print "\$betty is $betty.\n";

hi, you called the sum of fred and barnay!
$wilma is 9.
hi, you called the sum of fred and barnay!
$betty is 27.

print語句只用於協助調試,程序完成後能夠刪除,假設在子程序結尾新增一條print語句
則表達式最後並不是加法運算,而是print語句,返回值一般是1,表示成功輸出信息。
這種狀況術語是"void context"(空上下文),表示運算結果沒有存儲在變量裏面,未被任何函數使用。

sub larger_of_fred_or_barnay {
  if ($fred > $barnay) {
    return $fred;
    } else {
    return $barnay;
    }
}

larger of fred and barnay is 5.

必須等到執行階段獲得變量內容後,纔會知道返回值究竟是$fred仍是$barney。

參數
子程序不強制使用全局變量。
Perl子程序能夠有參數,要傳遞參數列表到子程序裏,只要在子程序調用的後面加上被括號圈引的列表表達式。
$n = &max(20, 15);
參數列表被傳遞到子程序中,能夠隨意調用,Perl會自動將參數列表化名爲特殊的數組變量@_,該變量在子程序執行期間有效。子程序能夠訪問數組,以判斷數組的個數以及參數的值。
子程序的第一個參數存儲在$_[0],第二個參數存儲在$_[1],以此類推。這些變量與$_毫無關聯。參數列表老是存在某個數組變量裏,可讓子程序調用,Perl將這個數組稱爲@_。
sub max {
  if ($_[0] > $_[1]) {
    $_[0];
    } else {
    $_[1];
    }
  }
$qing = &max(20, 12);
print "the larger argument is $qing.\n";

the larger argument is 20.

子程序只能接受兩個參數,多餘的參數會被忽略,參數若是不足也會被忽略——若是用到超過@_數組邊界的參數,只會獲得undef。
@_變量是子程序的私有變量。若是已經有了全局變量@_,則該變量在子程序調用是存起來,並在子程序返回時恢復本來的指。因此在當前的子程序調用中,@_老是包含了它的參數列表。

子程序中的私有變量
Perl裏面全部的變量都是全局變量,可是隨時可使用my操做符來建立私有變量,稱爲詞法變量(lexicak variable):
sub max {
  my($m, $n);
  ($m, $n) = @_;
  if ($m > $n)
    {$m}
  else {$n};
}
$a = &max(1,4);
print "$a";
這些變量屬於封閉語法塊的私有變量,能夠稱做有限做用域(scope)變量。
語法塊以外的任意地方的$m和$n都徹底不受這兩個私有變量的影響。外部變量也沒法影響內部的私有變量。
Perl容許省略語法塊的最後一個分號,通常只有簡單到整個語句塊內只有一行時,才能夠省略分號。

變長參數列表
在Perl代碼中,經常把任意長度的列表做爲參數傳遞給子程序。
習慣於強類型子程序,只容許必定個數的參數而且預訂限制它們的類型。不過子程序超過預期個數被調用時,也會形成問題。
檢查@_數組的長度也很容易肯定參數個數是否正確。能夠寫出這樣檢查參數列表:
sub max {
  if (@_ != 2) {
    print "WARNING! &max should get exactly two arguments!\n"};
  }
  .
  .
  .
}
if判斷使用Perl中在標量上下文中直接使用數組名稱來獲取數組元素的個數。

改進&max子程序
$maxmum = &max(1, 12, 3, 24, 5, 36);
sub max {
  my($max_so_far) = shift @_;
  foreach (@_) {
    if ($_ > $max_so_far) {
      $max_so_far = $_;
    }
  }
  $max_so_far;
}
print $maxmum

36

上述程序代碼使用稱爲高水線(high-watermark)算法。
第一行代碼會對參數數組@_進行shift操做並返回1並存入$max_so_far變量,如今@_如今的內容爲(12, 3, 24, 5, 36),由於1已經被移走,也就是第一個參數。
foreach循環會遍歷參數列表@_裏剩餘的元素,循環的控制變量默認爲$_。

空列表參數
假如傳入的參數爲@number爲空列表。
第一行對參數數組@_進行shift操做,以此做爲$max_so_far的值。由於數組爲空,因此shift會返回undef。
foreach循環空數組,因此自己不會執行。
Perl會將$max_so_far的值undef做爲子程序的返回值。

關於詞法變量my
詞法變量能夠在任何語句塊內,而不只限於子程序的語法塊。能夠在if,while和foreach的語句塊內使用。
foreach (1..10) {
  my ($squre) = $_ * $_;
  print "$_ squred is $squre.\n "
}

1 squred is 1.
 2 squred is 4.
 3 squred is 9.
 4 squred is 16.
 5 squred is 25.
 6 squred is 36.
 7 squred is 49.
 8 squred is 64.
 9 squred is 81.
 10 squred is 100.
 
 $squre對於所屬的語句塊來講是私有的。若是變量定義並未出如今任何語句塊內,則該變量對於整個程序源文件來講是私有的。詞法變量的做用域受限於定義它的最內層語句塊或文件。只有語句塊上下文做用域的程序代碼才已$squre使用該變量。
 my操做符並不會改變變量賦值時的上下文:
 my($num) = @_;#列表上下文
 my $num = @_; #標量上下文
my操做符在不加括號時,只能用來聲明單個詞法變量:
my $cao ,$qing;        #erroe
my($cao, $qing);    #right
能夠用my來定義私有數組:my @numbers;
標量被設定爲undef,數組被設定爲空數組。

use strict編譯指令
編譯指令,就是給編譯器的某些提示,告訴它如何處理接下來的代碼。
從Perl5.12開始,使用編譯指令指定最低Perl版本號,就至關於隱式打開約束指令:
use 5.012;

return操做符
return操做符會當即中止執行並從子程序返回某個值:
my @names = qw/cao qing wang huan xiao bao/;
my $result = &which_element_is("huan", @names);
sub which_element_is {
  my($what, @array) = @_;
  foreach (0..$#array) {
    if ($what eq $array[$_]) {
      return $_;}
  }
  -1;
}
print "$result";

3

在子程序執行到中間部分就已經獲得結論的時候,就可使用return關鍵字當即返回.

省略與號
若是編譯器知道在調用子程序前看到子程序的定義,或者Perl經過語法規則判斷它只能是子程序調用,那麼對待該子程序就能夠像內置變量那種,在調用時省略與號。
只要將參數列表放在括號內,就必定是函數調用:
my @cards = shuffle(@deck_of_cards);
若是編譯器已經見過子程序的定義,也能夠省略與號。而且能夠省略參數列表兩邊的括號。
sub division {
  $_[0] / $_[1];
}
my $quotient = division 20, 4;
print $quotient;

5

子程序子因此能夠正確運行,是由於符合加不加括號都不會產生歧義的原則。
可是不要把子程序定義放在語句調用的後面。
加入子程序與Perl內置函數重名,必須使用&號,爲了不歧義。

非標量返回值
子程序不只能夠返回標量值,若是在列表上下文使用時,還能夠返回列表值。
sub list_from_cao_to_qing {
  if ($cao < $qing) {
    return $cao..$qing;
    }
  else {
  reverse $qing..$cao;
  }
}
$cao = 5;
$qing = 1;
@c = &list_from_cao_to_qing;
print "@c";

5 4 3 2 1

單寫一個return不加任何參數時,在標量上下文的返回值就是undef,在列表上下文返回空列表。

持久性私有變量
在子程序中使用my操做符來建立私有變量,每次調用子程序時,私有變量都會被從新定義。而使用state操做符聲明變量,就能夠在子程序調用期間保留變量以前的值,並將變量做用域侷限於子程序內部。
use 5.012;
sub cao {
  state $n = 0;
  $n += 1;
  print "Hello $n.\n";
}
&cao;
&cao;
&cao;

Hello 1.
Hello 2.
Hello 3.

相似標量變量,其餘任意類型的變量均可以被聲明爲state變量。
use 5.012;

sum(5, 6);
sum(1..4);
sum(3);
sub sum {
  state $sum = 0;
  state @numbers;
  foreach my $number (@_) {
    push @numbers, $number;
    $sum += $number;
  }
  say "The sum of (@numbers) is $sum";
}

The sum of (5 6) is 11
The sum of (5 6 1 2 3 4) is 21
The sum of (5 6 1 2 3 4 3) is 24

在使用數組和哈希的state變量時,仍是有限制的,沒法在列表上下文初始化這兩種類型的state變量。

習題
1. 寫一個名字爲total的子程序,能夠返回給定列表中數字相加的和。
2. 使用以前的程序,計算1到1000的值。
3. 寫一個名字爲above_average的子程序,給定一個包含多個數字的列表,返回大於這些數平均值的數。
4. 寫一個名爲greet的子程序,給定人名爲參數,打印歡迎信息。
5. 修改子程序,告訴新來的人,以前已經來過的人。

答案
1.
use 5.012;
sub total {
    my $sum;
    foreach (@_) {
        $sum += $_;
    }
    return $sum;
}

$a = &total(qw \1 2 3 4 5\);
print "$a";
2.
use 5.012;
sub total {
    my $sum;
    foreach (@_) {
        $sum += $_;
    }
    return $sum;
}
print "The sum of number form 1 to 1000", &total(1..1000),".\n";
3.
use 5.012;
sub total {
    my $sum;
    foreach (@_) {
        $sum += $_;
    }
    return $sum;
}
sub average {
  if (@_ == 0) { return}
  my $count = @_;
  my $sum = &total(@_);
  $sum / $count;
}
sub above_average {
  my $average = average(@_);
  my @list;
  foreach my $element (@_){
    if ($element > $average) {
      push @list, $element;
    }
  }
  @list;
}
my @fred = above_average(1..10);
print "\@myfred is @fred.\n";
4.
use 5.012;

&greet("caoqing");
&greet("xiaohuan");
&greet("baobao");
sub greet {
  state $last_person;
  my $name = shift;
  print "Hi, $name! ";
  if (defined $last_person) {
    print "$last_person is also here!\n";
  }
  else {
    print "You are the first one here!\n";
  }
   $last_person = $name;
   
5.
use 5.012;
&greet("caoqing");
&greet("xiaohuan");
&greet("baobao");
sub greet {
  state @names;
  my $name = shift;
  print "Hi, $name! ";
  if ( @names ) {
    print "I have seen: @names!\n";
  }
  else {
    print "You are the first one here!\n";
  }
   push @names, $name;
}
相關文章
相關標籤/搜索