第十章其它控制结构:
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)";
|