Perl学习笔记6——其他控制结构

unless控制结构

在if控制结构中,如果条件表达式返回真,就执行代码块。而unless控制结构与if相反,只有当条件表达式为假时,才执行代码块。即除非条件表达式为真,否则就执行代码块。unless可以用在一些正向条件表述很困难的情况下。

unless也可伴随else语句,从语法上没有错误,但语义上很容易造成混淆,故一般不推荐这种写法。例:

unless($_==1){print}    #除非$_的值等于1,否则执行代码块

until控制结构

until控制结构是Perl五大循环结构之一。在while循环结构中,当条件表达式返回真时,会不断循环执行代码块,直到条件表达式返回假时跳出循环。而until循环的条件与while循环相反,当条件表达式返回假时,会不断循环执行代码块,直到条件表达式返回真时跳出循环。即直到条件表达式为真,否则不断执行循环。与unless类似,其可用于一些正向条件表述很困难的情况下。例:

until($_==1){$_-=1}    #直到$_的值为1,否则不断执行代码块

控制结构的修饰符写法

为了进一步简化代码书写,在Perl的控制结构语句中,可以将控制结构的关键词作为一个代码块的后置修饰符来书写,修饰符的功能就是控制代码块的执行行为。这样的写法只能在代码块中的语句只有一句时使用。当使用修饰符写法时,修饰符及其条件表达式应后置到代码块语句之后,且代码块的花括号和条件表达式的小括号均可省略。除了for控制结构以外(见下文),几乎所有不会引起歧义的控制结构语句均可以使用这种修饰符写法。例:

print if$_!=0;

print unless$_==0;

$_-=1 while$_!=0;

$_-=1 until$_==0;    #各种控制结构的修饰符写法,这些语句均与传统的控制结构语句作用一致,但这样的修饰符写法大大简化了代码书写

在使用foreach的修饰符写法时,无法指定其控制变量,而只能使用其默认的控制变量$_。所以如果对控制变量有要求,则可以使用传统的写法。例:

print for 1..10;    #foreach的修饰符写法,控制变量只能为$_

for控制结构

for控制结构也是Perl五大循环结构之一。在Perl中,for实质上是一种变相的while循环。完整的for控制结构组成如下:

for (初始化;测试;递增) {代码块}

在Perl中,for实质上相当于如下while循环:

初始化;

while(测试) {代码块;递增}

首先进行变量初始化,然后进入while循环的测试、执行代码块过程,代码块全部执行完成后,执行递增操作,即递增操作相当于加在代码块最后的语句。然后再进入下一次的while循环。for循环结构的三个循环控制语句(初始化、测试、递增)共同写在for关键词后面的括号中,以分号隔开。

由此可见,for循环结构本质上就是一个变相的while循环结构,所以行输入操作符的简写形式也适用于for的第二个循环控制语句,即测试语句。

for循环结构常用于控制循环次数,可以初始化一个表示循环次数的变量,在每一次循环结束后对其加1,当次数达到需要时跳出循环。例:

for($i=1;$i<=10;$i+=1){print}    #将$_的值输出10次

for循环结构的三个循环控制语句均可省略,但表示分隔的两个分号在任何情况下均不能省略。如果省略测试语句,则会一直返回布尔真。

在Perl解释器中,foreach和for这两个关键词等价,即foreach关键词可以简写为for。而Perl可以根据圆括号中的内容来判断是哪一个关键词。如果圆括号内没有分号,就说明这是foreach关键词,如果有两个分号,就说明这是for关键词。例:

print for 1..10;     #foreach关键词

for(1..10){print}    #foreach关键词

for(;;){print}       #for关键词

裸块控制结构

裸块是Perl中最后一种循环块,其定义为:没有任何关键字或条件表达式的代码块。即将while等循环结构的关键词和小括号中的内容全部拿去,只剩下代码块,这就是一个裸块。

裸块是只进行一次循环的循环块,也可以理解为,裸块就是一个用花括号围住的代码块而并非一个真正的循环。裸块有很多独特的作用,如临时限定词法变量(详见“子程序”)等。以下就是一个常见的裸块:

{

      $_=1;

      print;

}          #一个裸块

循环控制操作符

在执行循环语句的过程中,有时候需要对循环的过程进行一定的控制,最常见的如立即跳出循环或直接进入下一次循环等。Perl提供了三个循环控制操作符以供循环过程中使用。

last操作符可以用于立即跳出当前的循环语句块。例:

while($_+=1){last if$_==10}    #当$_的值为10时立即跳出while循环

next操作符可以使循环立即跳到当前循环块的底端,即立即进行下一次完整的循环,从判断条件表达式的布尔值开始。例:

while(){

      chomp;

      nextif$_==0;    #如果$_为0,则直接跳入下一次循环,即使用文件句柄读取下一行数据

      print;

}                 #next操作符会跳至这里,从而直接进入下一循环

redo操作符可以使循环立即跳到当前循环代码块的顶端,即重新执行一次本次循环的代码块的所有内容,不经过条件表达式测试,也不会跳入下一次循环。例:

for(1..10){

                       #如果redo被执行,则会跳至本次循环的这里,不经过本次循环的条件测试,也不进入下一次循环

      print"请输入:$_\n";

      chomp($n=);

      redounless$n==$_;    #除非输入正确,否则重复执行本次循环,即不断要求输入某一数字

}

上述的三个循环控制操作符在默认情况下都只对当前运行的最内层的循环结构语句块起作用。如果外层还有循环结构,则不受影响。例:

for(@n=1..10){

      while($_+=1){lastif$_>=5}    #这里的last只对while循环起作用,而不会影响foreach继续循环

}

标签

标签是Perl中的一种很罕见的用法,这种用法只会在很少的情况中出现。

上文中提到,last、next和redo操作符都只对当前进行的最内层循环起作用,而如果需要从内层循环块对外层循环块进行控制时,就可以使用标签。标签同普通的文件句柄名一样也使用裸字,所以其在命名时也建议使用全大写。

标签相当于对某一外层循环块的命名,书写时,将标签和一个冒号加在该循环块关键字所在的那一行即可,一般建议标签靠左书写,而不管此循环块的缩进深度。标签可以和循环控制操作符连用。使用时,将标签名加在循环控制操作符之后即可,表示此操作符作用于其后的这个标签所在的循环块。例:

LINE:for(@n=1..10){                #将此外层循环块加上标签

      while($_+=1){lastLINE if$_==10}   #如果$_的值为10,则直接跳出foreach循环而不是内层的while循环

}

自增与自减

在编程中,常常会用到将某个变量的值自加1或者自减1这样的情况,所以Perl提供了这种操作的简写,即使用自增“++”与自减“—”操作符。例:

$_++;   #相当于$_+=1

$_--;    #相当于$_-=1

自增与自减操作符的使用分为两种,即前置自增(减)与后置自增(减)。下文以自增操作符为例来说明这两种情况。

如果将自增操作符写在变量之前,就称为前置自增,而如果将自增操作符写在变量之后,就称为后置自增。在自成一行的语句中,这两种写法没有任何区别,效果都是将变量值加1。例:

$_++;   #后置自增

++$_;   #前置自增

这两者的区别在于进行自增运算和取返回值的先后顺序上。如果使用后置自增,则表达式先取得变量的值返回,再将变量自增,而如果使用前置自增,那么表达式就先进行自增运算,再取得自增后的值返回。对于后置自增又可以这样理解:即表达式会直接取变量的值作为返回值,而自增运算只是该变量的副作用,不会对当前的赋值操作或布尔判断等造成任何影响。在非空上下文中,这两种写法就能体现出较大区别,例:

$n=++$_;       #先进行自增运算,再取得返回值1,所以$n的值为1

$m=$_++;       #先取得$_的值1作为返回值,再进行自增运算,所以$m被赋值为返回值1,$_再自增为2

print if$_++;      #先取得$_的值2作为布尔值,返回真,再将$_自增为3。然后执行代码块,输出3

print if++$_;      #先进行自增运算,再取结果4作为布尔值,再执行代码块,输出4

三目条件操作符

三目条件操作符在使用时需要三个操作数,所以又称为三目操作符。其类似于一个if-else结构,也类似于Excel中的if函数。任何一个三目操作符的表达式都可以改写为if-else结构,但在一些选择性赋值式,或一些代码简短的条件语句中,使用三目操作符会大大简化代码书写。

三目操作符由一个问号和一个冒号组成,用以分隔三个操作数。第一个操作数为一个条件表达式,第二和第三个操作数分别为条件表达式返回真和返回假时执行的内容。Perl首先执行条件表达式,并根据条件表达式返回的布尔值决定执行后面的哪一个表达式,另一个则会被略过。例:

$_=$n?1:0;               #根据$n是否为真决定哪一个值会被赋值给$_

$n>$m?print$n:print$m;   #根据比较操作符的返回值决定哪一项操作

三目操作符的第三参数可以多次嵌套,从而实现多路分支。例:

$_=

$n>0?1:

$n<0?-1:

0;    #如果$n为正,则$_为1,如果为负,则$_为-1,如果为0,则$_为0短路控制结构

在Perl初步中已提到,Perl拥有全套的逻辑操作符,可供进行逻辑运算。实际上,逻辑运算操作符是会走捷径的。如逻辑与(and)操作符,当其左边的表达式返回假时,操作符就会走捷径,不会对右边的表达式进行求值,而会直接略过。而对于逻辑或(or)操作符,只有当左边的表达式返回假时,才会对右边的表达式进行求值,否则就会直接略过。因为这样的行为,这些逻辑操作符又被称为短路逻辑操作符,上述操作也成为短路运算。

上文又可以这样理解:短路操作符会对其右边的表达式进行选择性运算,即根据短路操作符的种类与操作符左边表达式的返回值来决定是否对其右边的表达式进行运算。

在Perl中,短路操作符自身不返回布尔值,其返回值由最后进行运算的那部分表达式的返回值充当。这样的返回值与逻辑运算的最终结果是一致的,这里不详细论述。

根据短路操作符的这个性质,就可以构建出一类由短路操作符进行控制的控制结构,即短路控制结构。这种控制结构会根据短路操作符的类别及其左边表达式的返回值来决定是否执行右边的表达式。例:

$n and $_=$n;       #如果$n为布尔真,就赋值

$n<$m or $m=$n;    #如果$n不小于$m,就赋值

and和or是“&&”和“||”的低优先级写法,其优先级比绝大多数表达式都要低。上述的两个表达式均可以使用if等控制结构改写,且很明显,使用if的写法将使代码变得更为清晰直观:

$_=$n if$n;

$m=$n if$n>=$m;    #相比于短路控制结构,更为清晰的if控制结构写法

由此可见,短路控制结构并没有常见的条件控制结构清晰直观,故一般不推荐滥用短路控制结构。但Perl中也有一些常用短路控制结构的地方,最常见的就是open操作符与or die的连用。例:

open I,'1.txt' or die;    #打开文件句柄,如果不成功,再执行die

定义或操作符

在两边均返回布尔值的条件表达式中使用逻辑操作符不会出现任何问题,但如果是在一些其他的情况下,如使用逻辑操作符进行选择性赋值等,就会存在潜在的问题。例:

$_=$m||'None';    #$m为0时也会被略过

在这种选择性赋值式中,决定$m是否要赋值给$_的条件往往不是$m的布尔值是否为真,而是$m是否有定义。所以如果这里使用“||”,就会出现当$m的值为0时$m也会被略过而不会赋值给$_这样的情况。

为了避免这样的问题,在Perl 5.10中引入了类似于逻辑或的定义或操作符,即“//”。定义或操作符对于真假的判定标准不是基于布尔值,而是基于该值是否已定义,即是否为undef。如果左操作数已定义,则判定为真,右操作数会被略过。只有当左操作数为undef时,才会执行右边的内容。下例为逻辑或与定义或操作符的比较:

$_=0||1;       #逻辑或操作符,左边为布尔假值,所以$_为1

$_=0//1;       #定义或操作符,左边已定义,所以$_为0,右边被短路

$_=''//1;        #左边已定义,所以$_为空字符串,右边被短路

$_=undef//1;   #左边未定义,所以执行右边的表达式,$_为1


樱雨楼

完成于2016.1.10

最后修改于2016.1.30

你可能感兴趣的:(Perl学习笔记6——其他控制结构)