Perl流程控制語句

布爾值判斷

  • 若是是數字,0表示假,其它全部數字都是真。
  • 若是是字符串,空字符串('')爲假,其它全部字符串爲真(有例外,見下一條)。
  • 若是是字符串'0',perl是看成數值0來處理的,因此這是惟一的非空但爲假的字符串。
  • 若是既不是數字,也不是字符串,那麼先轉換爲數字或字符串再作判斷(也就是"undef"表示假,其它全部引用表示真)。
  • "!"表示取反。

perl有個技巧,將兩個"!"一塊兒用,至關於"負負得正",因此原來是真的仍然是真的,原來是假的仍然是假的。但實際上,perl會將它們轉換值"1"和"undef"php

$still_true = !!"fred";   # $still_true的值是1
$still_false1 = !!"0";     # $still_false1的值爲空(undef)
$still_false2 = !!"";      # $still_false2的值爲空(undef)

print "$still_true"."\n";
print "$still_false1"."\n";
print "$still_false2"."\n";

條件判斷:if和unless

它們都是條件判斷語句,都支持else、elsif子句。html

if(CONDITION){  # 或者 unless(CONDITION)
    command
}

if(CONDITION1){  # 或者 unless(CONDITION1)
    command1
}elsif(CONDITION2){
    command2
}elsif(CONDITION3){
    command3
}
    ...
    else{
    commandN
}

if(CONDITION){  # 或者 unless(CONDITION)
    command1
}else{
    command2
}

其中CONDITION能夠是任意一個標量值。布爾值的判斷很簡單,方式和bash shell有點相似,但有點相反。python

unless和if判斷方式相反,if的condition爲真則執行後面的代碼,不然執行else或者退出if結構。unless則是condition爲假時才執行後面的代碼,不然執行else或退出unless結構。因此,unless至關於if的else部分,或者至關於if (!condition)shell

通常來講,不會用到unless的else語句,由於它徹底能夠改編成if語句。之因此有時候會使用unless而不是if的否認形式,是由於有時候的條件語句用if來寫確實不方便。express

三目運算符?:

perl也支持三目運算符:若是expression返回真,則整個表達式返回if_true,不然返回if_false數組

expression ? if_true : if_false

例如,求平均值,若是$n=0,則輸出"------"。bash

$avg = $n ? $sum/$n : "------";

它等價於:less

if($n){
    $avg = $sum / $n;
}else{
    $avg = "------";
}

三目運算符能夠寫出更復雜的分支:ide

#!/usr/bin/perl
#
use 5.010;

$score = $ARGV[0];
$mark = ($score < 60) ? "a" : 
        ($score < 80) ? "b" : 
        ($score < 90) ? "c" : 
        "d";          # 默認值
say $mark;

執行結果:函數

$ perl test.plx
a
$ perl test.plx 33
a
$ perl test.plx 63
b
$ perl test.plx 83
c
$ perl test.plx 93
d

邏輯運算符:and(&&)、or(||)、//、not(!)

expr1 < and or && || // > expr2
<not !>expr
  • &&運算符只有兩邊爲真時才返回真,且短路計算:expr1爲假時直接返回false,不會評估expr2
  • ||運算符只要一邊爲真時就返回真,且短路計算:expr1爲真時直接返回true,不會評估expr2
  • andor基本等價於對應的&&||,但文字格式的邏輯運算符優先級很是低
  • not!求反,一樣文字格式的not的優先級很低
  • 由於符號格式的邏輯運算符優先級很高,因此每每左邊和右邊都會加上括號,而文字格式的優先級很低,左右兩邊不需加括號
  • //運算符見下文
if (($n >=60) && ($n <80)){
    print "...";
}

if ($n >=60 and $n <80){
    print "...";
}

or運算符每每用於鏈接兩個"成功執行,不然就"的子句。例如,打開文件,若是打開失敗,就結束該perl程序:

open LOG '<' "/tmp/a.log" or die "Can't open file!";

更常見的,還會分行縮進:

open LOG '<' "/tmp/a.log"
    or die "Can't open file!";

一樣,and運算符也經常使用於鏈接兩個行爲:左邊爲真,就執行右邊的操做(例如賦值)。

$m < $n and $m = $n;   # 以$m的姿態取出$m和$n之間較大值

如下是3個語句是等價語句:

if ($m<$n){$m=$n}
$m=$n if $m<$n;
$m=($m < $n) ? $n : $m;

關於perl的短路計算和//

perl的短路計算很是特別,它返回的是最後運算的表達式的值:

  • 若是這個返回值對應的布爾值爲真,則整個短路計算天然爲真
  • 若是這個返回值對應的布爾值爲假,則整個短路計算天然爲假

這個返回值有時候頗有用,每每經過邏輯或的操做來設置默認值,因此,這個返回值既保證短路計算的結果不改變,又能獲得返回值。

例如

my $name = $myname || "malongshuai"

$myname變量存在時,它返回真(0是例外,見下面),並將$myname賦值給$name;當$myname變量不存在時,它返回假,因而評估"malongshuai",由於是個字符串,因此返回真,因而將malongshuai賦值給$name。這樣一來,$myname變量就有了默認值。

可是,若是$myname變量存在,但值爲0(字符串的或數值的),因爲它也返回假,致使$name被賦以"malongshuai"。

這種行爲顯然並不是咱們所須要的。因而,改用下面這種先判斷,再賦值的行爲:

my $name = defined($myname) ? $myname : "malongshuai";

可是這樣的寫法比較複雜,perl 5.10版本提供了更方便的"邏輯定義或"(logical defined-or)操做符//:當發現左邊的值是已經定義過的,就直接進行短路計算,而無論該左邊的值評估後是真是假。

use 5.010;
my $name = $myname // "malongshuai";

這個操做符應對開啓了use warnings功能的perl程序頗有用,省得warnings發出煩人的警告。

while循環和until循環

while(CONDITION){
    commands;
}

until(CONDITION){
    commands;
}

until和其它某些語言的until循環有所不一樣,perl的until循環,內部的commands主體可能一次也不會執行,由於Perl會先進行條件判斷,當條件爲假時就執行,若是第一次判斷就爲真,則直接退出until。

for循環

Perl中的for循環採起C語言的for風格,例如:

for($i=1;$i<=10;$i++){
    print $i,"\n";
}
print $i,"\n";   # 輸出11

須要注意的是,上面的$i默認是全局變量,循環結束後還有效。可使用my關鍵字將其聲明爲局部變量:

for (my $i = 1;$i<=10;$i++ ){
    print $i,"\n";
}

for循環不只僅只支持數值遞增、遞減的循環方式,還支持其它類型的循環,只要能進行判斷便可。見下面的例子。

for關鍵字後面括號中的3個表達式均可以省略,但兩個分號不能省略:

  • 若是省略第三個表達式,則表示一直判斷,直到退出循環或者無限循環
  • 若是省略第二個表達式,則表示不判斷,因此會無限循環
  • 若是省略第一個表達式,則表示不作初始賦值

例如,下面分別省略第三個表達式和省略全部表達式:

for(my $str="malongshuai";$str =~ s/(.)//;){
    print $str,"\n";
}

for(;;){
    print "never stop";
}

對於無限循環,Perl中更好更優化的方式是使用:

while(1){
    command;
}

Perl中的for也支持成員測試性的遍歷,就像shell中的for i in ...的操做同樣,它期待一個列表上下文,表示遍歷整個列表。若是省略控制變量,表示使用$_。例如:

my @arr = qw(Shell Python Perl PHP);
for $i (@arr){ print "$i\n" }
for (@arr) {print "$_\n"}

像for遍歷列表元素的操做,可使用foreach來替代,大多數迭代列表的時候它們能夠互換。

foreach循環

(由於還沒介紹數組和hash,因此這裏能看懂就看,不能看懂等到介紹數組的時候再看裏面的foreach示例)

foreach更適合用於遍歷,全部foreach都能直接修改關鍵字爲for而轉換成for循環。當寫成for格式的時候,perl經過判斷括號中的分號來決定這是foreach循環仍是for的普通循環。但for能實現的循環功能,foreach不必定能實現,由於for中有初始變量,有條件判斷,而foreach則是簡單版的for循環。

先解釋下foreach的用法:

例如,迭代從1到10的列表:

foreach $i (1..10){
    print $i,"\n";
}

其中$i稱爲控制變量,每迭代一次都會從迭代列表中取出一個元素賦值給控制變量。能夠省略控制變量,這時將採用默認的變量$_

foreach (1..10){
    print $_,"\n";
}

foreach能夠改寫爲for:

for(1..10){
    print $_,"\n";
}

for ($i=1;$i<=10;$i++){
    print $i,"\n";
}

@arr=qw(malongshuai gaoxiaofang xiaofang longshuai wugui fairy);
for (@arr){
    print $_,"\n";
}

關於for循環和foreach循環,若是在遍歷過程當中修改了元素的值,它會直接修改原始值。換句話說,迭代時賦值給控制變量的元素的引用,而不是賦值元素再賦值給控制變量。

@arr=qw(perl python shell);
foreach $subject (@arr) {
    $subject .= "aaa";
}
print @arr;         # 輸出perlaaapythonaaashellaaa

當foreach/for遍歷結束後,控制變量將復原爲foreach/for遍歷前的值(例如未定義的是undef)。

$subject="php";
foreach $subject (qw(perl python shell)){
}
print $subject;     # 輸出"php"

each遍歷

each HASH
each ARRAY

each用來遍歷hash或數組,每次迭代的過程當中,都獲取hash的key和value,數組的index(數值,從0開始)和元素值。

each放在列表上下文,會返回key/value或index/element,放在標量上下文則只返回key或index。

遍歷hash:

#!/usr/bin/perl -w
use strict;

my %hash = (
    name1 => "longshuai",
    name2 => "wugui",
    name3 => "xiaofang",
    name4 => "woniu",
);

while(my($key,$value) = each %hash){
    print "$key => $value\n";
}

輸出結果:

name4 => woniu
name3 => xiaofang
name2 => wugui
name1 => longshuai

遍歷數組:

#!/usr/bin/perl -w
use strict;

my @arr = qw(Perl Shell Python PHP Ruby Rust);

while(my($key,$value) = each @arr){
    print "$key => $value\n";
}

輸出結果:

0 => Perl
1 => Shell
2 => Python
3 => PHP
4 => Ruby
5 => Rust

each放在標量上下文:

#!/usr/bin/perl -w
use strict;

my %hash = (
    name1 => "longshuai",
    name2 => "wugui",
    name3 => "xiaofang",
    name4 => "woniu",
);
my @arr = qw(Perl Shell Python PHP Ruby Rust);

while(my($key) = each %hash){
    print "$key\n";
}

while(my($key) = each @arr){
    print "$key\n";
}

輸出結果:

name2
name4
name3
name1
0
1
2
3
4
5

表達式修飾符(改寫流程控制語句)

perl支持單條表達式後面加流程控制符。以下:

command OPERATOR CONDITION;

例如:

print "true.\n" if $m > $n;
print "true.\n" unless $m > $n;
print "true.\n" while $m > $n;
print "true.\n" until $m > $n;
print "$_" foreach @arr;

不少時候會分行並縮進控制符:

print "true.\n"        # 注意沒有分號結尾
    if $m > $n;

改寫的方式幾個注意點:

  • 控制符左邊只能用一個命令。除非使用do語句塊,參見do語句塊
  • foreach的時候,不能自定義控制變量,只能使用默認的$_
  • while或until循環的時候,由於要退出循環,只能將退出循環的條件放進前面的命令中
    • 例如:print "abc",($n += 2) while $n < 10;
    • print "abc",($n += 2) until $n > 10;
  • 改寫的方式不能知足需求時,可使用普通的流程結構

執行一次的語句塊

使用大括號包圍一段語句,這些語句就屬於這個語句塊,這個語句塊實際上是一個循環塊結構,只不過它只循環一次。語句塊也有本身的範圍,例如能夠將變量定義爲局部變量。

{
    print "Enter a Num","\n";
    chomp(my $n = <STDIN>);
    $res = sqrt $n;
    print "$res","\n";
}
print $res,"\n";

循環控制:last、next、redo、LABEL(標籤)

  • last至關於其它語言裏的break關鍵字,用於退出當前循環塊(for/foreach/while/until/執行一次的語句塊都屬於循環塊),注意是隻退出當前層次的循環,不會退出外層循環
  • next至關於其它語言裏的continue關鍵字,用於跳入下一次迭代。一樣只做用於當前層次的循環
  • redo用於跳轉到當前循環層次的頂端,因此本次迭代中曾執行過的語句可能會再次執行
  • 標籤用於爲循環塊打上標記,以便那些循環塊控制關鍵字(last/next/redo)能夠指定操做的循環層次
use 5.010;
use strict;

foreach (1..10){
        say "startline...: $_";
        say "enter a word: last, next, redo?";
        chomp(my $choice = <STDIN>);
        last if $choice =~ /last/i;
        next if $choice =~ /next/i;
        redo if $choice =~ /redo/i;
        say "endline...: $_";
}
say "outside loop...";

如下是打標籤的示例(標籤建議採用大寫):

/usr/bin/perl
use 5.010;
use strict;

LINE: while(<>){
    foreach(split){
        last LINE if /error/i;
        say "$_";
    }
}

上面的標籤循環中,首先讀取一行輸入,而後進入foreach遍歷,由於split沒有參數,因此使用默認參數$_,這個$_所屬範圍是while循環,split以空格做爲分隔符分割這一行,同時foreach也沒有控制變量,因此使用默認的控制變量$_,這個$_所屬範圍是foreach循環。當foreach的$_能匹配字符串"error"則直接退出while循環,而不只僅是本身的foreach循環。這裏if語句後採用的匹配目標是屬於foreach的默認變量$_

例如,這個perl程序讀取a.txt文件,其中a.txt文件的內容以下:

$ cat a.txt
hello world
hello world Error
hello world Error heihei

執行這個perl程序:

$ perl -w test.plx a.txt
hello
world
hello
world

可見,只輸出了a.txt中第二行Error前的4個單詞。

附加循環代碼:continue

perl中還有一個continue關鍵字(http://perldoc.perl.org/functions/continue.html),它能夠是一個函數,也能夠跟一個代碼塊。

continue              # continue函數
continue BLOCK        # continue代碼塊

若是指定了BLOCK,continue可用於while和foreach以後,表示附加在循環結構上的代碼塊。

while(){
    code
}continue{
    attached code
}

foreach () {
    code
} continue {
    attached code
}

每次循環中都會執行此代碼塊,執行完後進入下一循環。

在continue代碼塊內部,也可使用redo、last和next控制關鍵字。因此,這幾個流程控制關鍵字更細緻一點的做用是:redo、last直接控制循環主體,而next是控制continue代碼塊。因此:

while(){
    # redo jump to here
    CODE
} continue {
    # next jump to here
    CODE
    # next loop
}
# last jump to here

實際上,while和foreach在沒有給定continue的時候,邏輯上等價於給了一個空的代碼塊,這時next能夠跳轉到空代碼而進入下一輪循環。

例如:

#!/usr/bin/env perl
use strict;
use warnings;

$a=3;
while($a<10){
    if($a<6){
        print '$a in main if block: ',$a,"\n";
        next;
    }
} continue {
    print '$a in continue block: ',$a,"\n";
    $a++;
}

輸出結果:

$a in main if block: 3
$a in continue block: 3
$a in main if block: 4
$a in continue block: 4
$a in main if block: 5
$a in continue block: 5
$a in continue block: 6
$a in continue block: 7
$a in continue block: 8
$a in continue block: 9
相關文章
相關標籤/搜索