Perl中流程控制语句

前言

因为习惯了使用Perl,所以一般写流程的时候就会习惯性的使用这个语言,这里说一下在流程编写过程中,Perl中的流程控制语句。
看到网上有一篇很详细的介绍,我就学习了一遍,然后搬运一下加点自己的理解保存。

骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9511878.html

布尔值控制

在perl中,布尔值判断比较简单。
对于数字,0表示假,其它所有数字都是真。
对于字符串,空字符串('')为假,其它所有字符串为真(有例外,见下一条)。
对于字符串'0',perl是当作数值0来处理的,所以这是唯一的非空但为假的字符串,一定要注意。
对于既不是数字,也不是字符串,那么先转换为数字或字符串再做判断(也就是"undef"表示假,其它所有引用表示真)。
"!"表示取反。
另外,perl还有个技巧,将两个"!"一起用,相当于"负负得正",所以原来是真的仍然是真的,原来是假的仍然是假的。但实际上,perl会将它们转换值"1"和"undef"。

$true = !!"perl";   # $true的值是1
$false1 = !!"0";     # $false1的值为空(undef)
$false2 = !!"";      # $false2的值为空(undef)

print "$true"."\n";
print "$false1"."\n";
print "$false2"."\n";

条件判断:if和unless

if和unless都是条件控制语句,并且都支持else,elsif的句子。

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可以是任意一个标量值,而且是基于布尔值的判断。
unlessif的判断方式相反,ifcondition为真执行后面的代码,否则执行else或者退出if的结构。而unless则是condition为假的时候才执行后面的代码,否则执行else或者退出unless结构,在这里unless相当于if中的else部分,或者相当于if(!condition)·

三目(三元)运算符?:

perl支持常见的三目运算符:如果expression返回真,则整个表达式返回if true,否则返回if false,比较简单的使用:

expression ? if true : if false

$max = $a > $b ? $a : $b;
## 等于
if($a > $b){
    $max = $a;
}
else{
    $max = $b;
}

三目运算符也有复杂的使用:

vi test.pl
#!/usr/bin/perl
use warnings;
use strict;
use feature qw(say);


my $num = $ARGV[0];
$test = ($num < 10) ? "a" : 
        ($num < 100) ? "bb" : 
        ($num < 1000) ? "ccc" : 
        "dddd";          
say $test;

测试结果为:

perl test.pl
a

perl test.pl 3
a

perl test.pl 99
bb

perl test.pl 119
ccc

perl test.pl 1199
dddd

逻辑运算符:and(&&)、or(||)、//、not(!)

expression1 < and or && || // > exxpression2
# && 运算符只有两边为真时才返回真,且短路计算:expression1为假时直接返回false,不会评估expression2
# || 运算符只要一边为真时就返回真,且短路计算:expression1为真时直接返回true,不会评估expression2
# and 和 or 基本等价于对应的 && 和 || ,但文字格式的逻辑运算符优先级非常低
$m < $n and $m = $n;   # 以$m的姿态取出$m和$n之间较大值
not 和 ! 求反,同样文字格式的 not 的优先级很低
# 因为符号格式的逻辑运算符优先级很高,所以往往左边和右边都会加上括号,而文字格式的优先级很低,左右两边不需加括号
# // 运算符

perl 短路计算 和 // 运算符

perl的短路计算返回的是最后运算的表达式的值:
如果这个返回值对应的布尔值为真,则整个短路计算自然为真
如果这个返回值对应的布尔值为假,则整个短路计算自然为假
这个返回值有时候很有用,往往通过逻辑或的操作来设置默认值,所以,这个返回值既保证短路计算的结果不改变,又能得到返回值。比如:

my $test = $str || "test"

$str变量存在时,它返回真(0是例外,见下面),并将$str赋值给$test;当$str变量不存在时,它返回假,于是评估"test",因为是个字符串,所以返回真,于是将test赋值给$test。这样一来,$str变量就有了默认值。

但是,如果$str变量存在,但值为0(字符串的或数值的),由于它也返回假,导致$test被赋以"test"

这个操作最简单的还是三目运算符,改用下面这种先判断,再赋值的行为:

my $test = defined $str ? $str : "test";

但是这样的写法理解起来比较复杂,perl 5.10版本提供了更方便的"逻辑定义或"(logical defined-or)操作符//:当发现左边的值是已经定义过的,就直接进行短路计算,而不管该左边的值评估后是真是假。

my $test = $str // "test";

while循环和until循环

while(condition){
    commands;
}

until(condition){
    commands;
}

until和其它某些语言的until循环有所不同,perl的until循环,内部的commands主体可能一次也不会执行,因为Perl会先进行条件判断,当条件为假时就执行,如果第一次判断就为真,则直接退出until。

无限循环:

while(1){
    commands;
}

for循环

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

for关键字后面括号中的3个表达式都可以省略,但两个分号不能省略:
如果省略第三个表达式,则表示一直判断,直到退出循环或者无限循环
如果省略第二个表达式,则表示不判断,所以会无限循环
如果省略第一个表达式,则表示不做初始赋值

Perl中的for也支持成员测试性的遍历,就像shell中的for i in ...的操作一样,它期待一个列表上下文,表示遍历整个列表。如果省略控制变量,表示使用$_。例如:

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

foreach循环

foreach更适合用于遍历,所有foreach都能直接修改关键字为for而转换成for循环。当写成for格式的时候,perl通过判断括号中的分号来决定这是foreach循环还是for的普通循环。但for能实现的循环功能,foreach不一定能实现,因为for中有初始变量,有条件判断,而foreach则是简单版的for循环。
先解释下foreach,例如,迭代从1到10的列表:

foreach $i (1..10){
    print $i,"\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或数组,每次迭代的过程中,都获取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 "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)可以指定操作的循环层次

以下是打标签的示例(标签建议采用大写):

#!/usr/bin/perl
use strict;

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

上面的标签循环中,首先读取一行输入,然后进入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 -w test.plx a.txt
hello
world
hello
world

附加循环代码:continue

perl中还有一个continue关键字,它可以是一个函数,也可以跟一个代码块。

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/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

参考

Perl流程控制语句

                                —— dulunar 后记于 2019.11
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343