第十章其它控制结构:
unless控制结构:
在if控制结构中,只有当条件表达式为真的时候才执行某块代码,如果你想让程序块在条件为假的时候才执行,请把if改成unless。
#!/usr/bin/perl -w
unless ($fred =~ /^[A-Z_]\w*$/i) { print "The alue of \$fred doesn't look like a Perl identifier name.\n"; } #上面这个程序和下面的一样的:
if ($fred =~/^[A-Z_]\w*$/i) { #什么都不做
} else { print "The value of \$fred doesn't look like a Perl identifier name.\n"; } #这两种写法的效率是一样的,他们应该会被编译相同的内部字节码。还有一个改成的方法,就是以取反操作!来否定条件。
if ( ! ($fred =~ /^[A-Z_]\w*$/i) ) { print "The value of \$fred doesn't look like a Perl identifier name.\n"; }
|
unless附带的else子句:
其实哪怕是在unless结构中也可以使用else语句,虽然支持这样的语法,但是可能会导致困惑:
#!/usr/bin/perl -w
unless ($mon =~ /^Feb/) { print "This month has at least thirty days.\n"; } lese { print "Do you see what's going on here?\n"; } #如果用if语句我们可以写成这样:
if ($mon =~ /^Feb/) { print "Do you see what's going on here?\n"; } else { print "This month has at least thirty days.\n"; }
|
Until控制结构:
有时也许会想要颠倒while循环的条件。那么,使用until:
until ($j > $i) { $j *= 2; }
|
这个循环会一直执行,直到条件为真,它和while的唯一差别在于until会在条件为假(而非真)时重复执行。因为条件判断发生在循环第一次迭代之前,所以它仍旧是一个执行零次以上的循环,和while循环一样。类似if和unless转化的例子。
条件修饰词:
为了进一步简化表达,表达式后面可以接着一个用来控制它的修饰词,例如用if修饰词来模拟一个if块。
=========================== #!/usr/bin/perl -w
$n = -1; print "$n is a negative number.\n" if $n < 0;
和下面是一样的: if ($n < 0) { print "$n is a negative number.\n"; } =================================== @error("Invalid input") unless &valid($input); $i *= until $i > $j; print " ";, ($n += 2) while $n < 10; &greet($_) foreach @person;
print " ", ($n += 2) while $n < 10; 和 while ($n < 10) { print " ", ($n += 2); } 是一样的。
|
修饰词的两边都只能写单个表达式,因此不能写某事if某事while某事until某事unless某事foreach某事,
因为这样太让人困惑了。另外修饰词的左边也不能放多条语句。
祼块控制结构:
所谓祼块就是没有关键字或条件的代码块。有一个while循环:
while (condition) { body; body; body; } #然后拿走关键字while和条件,就会得到一个祼块:
{ body; body; body; }
|
为临时词法变量圈定有效范围:
{ print "Please enter a number: "; chomp (my $n = <STDIN>); my $root = sqrt $n; print "Th square root of $n is $root.\n"; }
|
elsif 子句:
许多情况下,需要逐项检查一系列的条件表达式,看看其中哪个为真。这可以通过if控制结构的elsif子句来写成如下代码:
if ( ! defined $dino ) { print "The value is undef.\n"; } elsif ( $dino =~ /^-?\d+\.?$/) { print "The value is an integer.\n"; } elsif ( $dino =~ /^-?\d*\.\d+$/ ) { print "The value is a _sample_ floating-point number.\n"; } elsif ($dino eq '') { print "The value is the empty string.\n"; } else { print "The value is the string '$dino'.\n"; }
|
如果没有任何一项符合,则执行最末端的else块。这样如果有一百个elsif的子句,必须执行前面的99个失败的测试,
才会达到第一百个。这样效率会很低。可以用case或switch来处理同样的问题。在5.10或者更高版本用户还可以选择
given-when。
自增和自减:
使用自增操作符++能对标量加1,变成43。
#!/usr/bin/perl -w
my $bedrock = 42; $bedrock++; print $bedrock;
|
跟其他将变量加1的方法一样,标量若未定义将会被创建:
#!/usr/bin/perl -w
my @people = qw{ Fred barney Fred wilma dino barney Fred pebbles }; my %count; #新的空哈希
$count{$_}++ foreache @people; #按需要创建新的键/值对
================================= [server1@root perl]# cat sss
#!/usr/bin/perl -w
my @people = qw{ Fred barney Fred wilma dino barney Fred pebbles }; my %count; $count{$_}++ foreach @people; print %count,"\n";
[server1@root perl]# perl sss
pebbles1barney2dino1wilma1Fred3 ================================= $bedrock--;
|
自增的值:
可以获得变量的值并且同时进行修改。把++操作符写在变量之前就能先增加变量的值,然后获取变量的值。这是前置自增:
#!/usr/bin/perl -w
use strict; my $m = 5; my $n = ++$m; print "$n\n"; #结果为6 加1
======================== #!/usr/bin/perl -w
use strict; my $m = 5; my $c = --$m; print "$c\n"; #结果为4 减1
======================== #!/usr/bin/perl -w
use strict; my $m = 5; my $d = $m++; print "$d\n"; #结果为5 不变
======================== #!/usr/bin/perl -w
use strict; my $m = 5; my $e = $m--; print "$e\n";
|
说明,当将变量名放在前面会先取得值,之后再自增或自减。这种做法称为后置自增或后置自减:
如果你写的表达式中只有自增或自减,不使用值而只是利用副作用,前置后置交没有任何区别:
这类操作符的一个常见用法就是(配合哈希计数)判断之前已经见过的元素:
#!/usr/bin/perl -w
my @People = qw{ Fred barney bamm-bamm wilma dino barney betty pebbles }; my %seen; foreach (@people) { print "I've seen you somewhere before, $_!\n" if $seen{$_}++; }
|
for控制结构:
for (initialization; test increment) { body; body; }
|
for这种类型的循环事实上只是一种变相的while循环:如下。
initialization; while (test) { body; body; increment; }
|
for循环目前最常见的用途,就是控制重复的运算过程:
for ( $i = 1; $i <= 10; $i++) { #从1数到10
print "I can count to $i!\n"; } ======================= [server1@root perl]# cat for1
#!/usr/bin/perl -w
use strict; use warnings; my $i; for ( $i=1 ; $i <= 10; $i++) { print "I can count to $i\n"; } [server1@root perl]# perl for1
I can count to 1 I can count to 2 I can count to 3 I can count to 4 I can count to 5 I can count to 6 I can count to 7 I can count to 8 I can count to 9 I can count to 10
|
事实上这三个循环控制部分(初始化、测试和递增)都可以为空,但是即使不需要它们也还得保留分号。在下面这个极不寻常的例子里,测试条件是一个替换,而递增则是空:
#!/usr/bin/perl -w
for ($_ = "bedrock";s/(.)//; ) { #当s///这个替换成功时,循环继续
print "One character is: $1\n"; } =============== [server1@root perl]# perl for2
b e d r o c k
|
在测试表达式为空的时候,两个连续的分号会被强行解释为真,从而导致死循环。但是在你学到本章的后面,能自如地中断这种循环之前,请不要尝试:
#!/usr/bin/perl -w
use strict; use warnings; for (;;) { print "It's an infinite loop!\n"; }
|
foreach 和 for 之间的秘密:
事实上,在Perl的解析器里,foreach和for这两个关键字是等价的。也就是说,当Perl看到其中之一时,也就好像看到了另一个。
Perl可以从圆括号里的内容判断出你的意图。如果里面有两个分号,它就是刚才见到你的for循环。若是没有分号就是一个纯正的
foreach循环。
循环控制:
目前我们已经感觉到,Perl是一种所谓的结构化编程语言。特别是Perl程序的任何都只有一个入口,也就是块的顶端。不过相比前面介绍过的结构,有时候需要提早退出某块代码。Perl有三个循环控制操作符,我们可以在循环里使用它们,让循环非常灵活。
last操作符:
操作符last能立即中止循环。就像在C一类语言中的break操作符一样。它是循环的紧急出口。当你看到last,循环就会结束,例如:
#打印出所胡提到fred的行,直到碰到__END__记号:
======================= #!/usr/bin/perl -w
use strict; use warnings; while (<STDIN>){ if (/__END__/) { last; } elsif (/fred/) { print; } } =======================
|
在Perl中有5种循环块。也就是for foreach while until 以及裸块
而if块或者子程序带的字括号不是循环的块,如同前面的例子,last操作符对整个循环块起作用。
last操作符只会对运行中最内层的循环块发挥作用。要跳出外层块,请继续看下去,我们很快就会提到。
next操作符:
有时个并不需要立刻退出循环,但是需要立刻结束当前这次迭代。这就是next操作符的用处,它会跳到内层循环块的底端。在next之后,程序将会断续执行循环的下次迭代,这和C-类语言中continue操作符的功能相似:
#分析输入文件中的单词:
while (<>) { foreach (split) { #将$_分解成单词,然后每次将一个单词赋值给$_
$total++; next if /\W/; #如果碰到不是单词的字符,跳过之后的表达式
$valid++; $count{$_}++; #分别统计每个单词出现的次数
##上面的next语句如果运行,会跳到这里##
} } print "total things = $total, valid words = $valid\n"; foreach $word (sort keys %count) { print "$word was seen $count{$word} times.\n"; } =============================== #!/usr/bin/perl -w
while (<>) { foreach (split) { $total++; next if /\W/; $valid++; $count{$_}++; } } print "total things = $total, valid words = $valid\n"; foreach $word (sort keys %count) { print "$word was seen $count{$word} times.\n"; } ===============================
|
redo操作符:
循环控制族第三个成员是redo。它能将控制返回到本次循环的顶端,不经过任何条件测试,也不会进入下一次循迭代。而那些C一类语言的人却会对这个操作符感觉陌生。因为那些语言里没有这个概念。
#打字测试
my @words = qw{ Fred barney pebbles dino wilma betty }; my $errors = 0;
foreach (@words) { ##redo 指令会跳到这里##
print "Type the word '$_':"; chomp(my $try = <STDIN>); if ($try ne $_) { print "Sorry - That's not right.\n\n"; $errors++; redo; #跳回循环的顶端
} } print "You've completed the test, with $errors errors.\n";
|
和另外两个操作符一样,redo在这5种循环块里都可以使用,并且在循环块嵌套的情况下只对最内层的循环起作用。
next和redo两者之间最大的区别在于next会正常继续下一次迭代,而redo则会重新执行这次迭代。下面的程序可以让你体验这三种
操作符工作方式的区别:
#!/usr/bin/perl -w
foreach (1..10) { print "Iteration number $_.\n\n"; print "Please choose: last, next, redo, or none of the above?"; chomp(my $choice = <STDIN>); print "\n"; last if $choice =~ /last/i; next if $choice =~ /next/i; redo if $choice = /redo/i; print "That wasn't any of the choices ...onward!\n\n"; } print "That's all, folks!\n";
|
带标签的块:
三目操作符?:
三目操作符就像一个if-then-else控制表达式。由于使用时需要三个操作数,所以称为三目操作符。它看起来像这样:
条件表达式 ? 真表达式 : 假表达式
首先,执行条件表达式,看看究竟是真还是假。如果为真,则执行真表达式;否则,就执行假表达式。每次使用时都会执行问号右边两个表达式中的一个,另一个则会跳过。换句话说,若条件表达式为真,则第二个表达式会被求值返回,忽略第三个表达式;倘若条件表达式为假,则忽略第二个表达式,而对第三个表达求值并返回。
逻辑操作符:
if ( $dessert{ 'cake' } && $dessert{ 'ice cream'}) { #两个条件都为真
print "Hooray! Cake and ice cream!\n"; } elsif ($dessert{'cake'} || $dessert{'ice cream'}} { #至少一个条件为真
print "That's still good...\n"; } else { #两个条件都为假--什么也不干
} ================================ $hour = 10; if ( (9 <= $hour) && ( $hour < 17) ) { print "Aren't you supposed to be at work...?\n"; } ================================
|
短路操作符的值:
与C一类语言不同的地方在于,Perl的短路操作符求得的值不只是简单的布尔值,而是最后运算的那部分表达式的值。
这恰好和布尔值兼容,因为最后部分的值若是真,整个就是真,反之亦然。
my $last_name = $last_name{$someone} ||'(No last name)';
my $last_name = defined $last_name{$someone} ? $last_name{$someone} : '(No last name)';
|| 与 // 的区别。(搞死我了,搞了一下午) no matter if that value on the lefthand side is true or false. Even if someone’s last name is 0, this version still works:
如下比较: 1、 ========================= #!/usr/bin/perl -w
my %last_name = ( "fred" => "flintstone", "dino" => undef, "barney" => "rubble", "linscora" => "" , "qinghua" => "", "betty" => "rubble", ); my $last_name = $last_name{'linscora'} // '(No last name)'; my $last_name1 = $last_name{'qinghua'} || '(No last name)'; print "$last_name\n"; print "$last_name1\n"; ==== [server1@root bin]# ./perl /perl/notun
#这行是//出来的效果。
(No last name) #这行是||出来的效果。
#0或undef的话对于|| 就是第一个不匹配 去匹配后面的
#但是0对于// 来说就是,第一个已经匹配了
#或这样说:第一个是当$last_name{$someone}等于0或空字符串时会把No last name赋值给$last_name;第二个只有$last_name{$someone}在为undef时才会把No last name赋值给$last_name
打完这几行字,头还是晕的。看来我的脑子真是不好用啊!哎
|
2、 ==================== #!/usr/bin/perl -w
use 5.010; foreach $try ( 0, undef, '0', 1, 25) { print "Trying [$try] ---> "; my $value = $try // 'default'; say "\t got [$value]"; } =========== Trying [0] ---> got [0] Use of uninitialized value $try in concatenation (.) or string at /perl/notun line 4. Trying [] ---> got [default] Trying [0] ---> got [0] Trying [1] ---> got [1] Trying [25] ---> got [25] ==================
|
使用部求值操作符的控制结构:
前面学习了4个操作符&& || // 和?: 都有一个共性:根据左边的求值决定是否计算右边的表达式。有些情况会执行的表达式,
在另外的情况下并不执行。因此被统称为部分求值操作符,部分求值操作符是天然的控制结构,因为不会执行所有的表达式。
习题:
#!/usr/bin/perl -w
use strict; my $secret = int ( 1 + rand 100); print "Don't tell anyone, but the secret number is secret.\n"; while (1) { print "Please enter a guess from 1 to 100: "; chomp(my $guess = <STDIN>); if ($guess =~ /quit|exit|^\*$/i) { print "Sorry you gave up. The number was $secret.\n"; last; } elsif ( $guess < $secret ) { print "Too small, Try again!\n"; } elsif ( $guess == $secret ) { print "That was it!\n"; last; } else { print "Too large. Try again!\n"; } } ===================== #!/usr/bin/perl -w
use 5.010; $ENV{ZERO} = 0; $ENV{EMPTY} = ''; $ENV{UNDEFINED} = undef;
my $longest = 0; foreach my $key ( keys %ENV) { my $key_length = length( $key ); $longest = $key_length if $key_length > $longest; }
foreach my $key (sort keys %ENV ) { printf "%-${longest}s %s\n", $key, $ENV{$key} // "(undefined)"; } printf "%-${longest}s %s\n", $key, $ENV{$key} ? $ENV{$key}: "(undefined)";
|
阅读(1220) | 评论(0) | 转发(0) |