6.1.4 else if结构
你已经看到,测试两个可能的条件(任何数不是偶数,就是奇数;任何年份或者是闰年,或者不是)时,else语句是如何工作的。然而,必须制定的程序设计选择并不总是这么明确。考虑以下任务:编写一个程序,在用户键入的数小于0时显示-1;在此数等于0时显示0;在大于0时显示1(这实际上是所谓的sign函数的一种实现)。显然,在这种情况下,必须做3次测试以确定键入的数是否是负数、0或正数。此时,简单的if-else结构就不再适用。当然,在这种情况下,总是可以使用3条独立的if语句,但这种解决方案并不总是可行的,特别是在做出测试并非相互排斥的时候更是如此。
通过向else子句添加一条if语句,就能处理这种情况。我们提到过,在else子句之后的语句可以是任何合法的Objective-C程序语句,为什么不能是另一条if语句呢?因此,在一般情况下,可编写以下形式:
if(expression 1)
program statement 1
else
if(expression 2)
program statement 2
else
program statement 3
这种形式有效地扩展了if语句,使其从双值逻辑判定变成了3个值的逻辑判定。可以用刚才显示的方式继续向else子句添加if语句,以便有效地将这种选择扩展成n个值的逻辑判定。
上述结构的使用相当频繁,它通常称作else if结构,其格式经常与上面显示的不同,例如:
if(expression 1)
program statement 1
else if(expression 2)
program statement 2
else
program statement 3
后面这种格式提高了语句的可读性,并使做出的具有3个路线的选择更加清晰。之后的程序通过实现前面讨论的sign函数说明了else if结构的使用。
代码清单6-6
//Program to implement the sign function
import<Foundation/Foundation.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
int number, sign;
NSLog(@“Please type in a number:);
scanf(“i”,&number);
if(number<0)
sign=-1;
else if(number==0)
sign=0;
else//Must be positive
sign=1;
NSLog(@“Sign=%i”,sign);
[pool drain];
return 0;
}
代码清单6-6输出
Please type in a number:
1121
Sign=1
代码清单6-6输出(重新运行)
Please type in a number:
-158
Sign=-1
代码清单6-6输出(重新运行)
Please type in a number:
0
Sign=0
如果输入的数小于0,将给sign指派值-1;如果此数等于0,将给sign指派0;否则,这个数一定大于0,因此给它指派值1。
下面的程序分析从终端键入的字符,并根据字母符号(a~z或A~Z)、数字(0~9)或特殊字符(其他任何字符)将其分类。要从终端读取单个字符,需要在scanf调用中使用格式字符%c。
代码清单6-7
//This program categorizes a single character
//that is entered from the keyboard
import<Foundation/Foundation.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
char c;
NSLog(@“Enter a single character:);
scanf(“c”,&c);
if((c>=‘a’&&c<=‘z’)||(c>=‘A’&&c<=‘Z’))
NSLog(@“Its an alphabetic character.”);
else if(c>=‘0’&&c<=‘9’)
NSLog(@“Its a digit.”);
else
NSLog(@“Its a special character.”);
[pool drain];
return 0;
}
代码清单6-7输出
Enter a single character:
&
Its a special character.
代码清单6-7输出(重新运行)
Enter a single character:
8
Its a digit.
代码清单6-7输出(重新运行)
Enter a single character:
B
Its an alphabetic character.
读入字符之后构建的第一个测试确定了char变量c是否是字母符号。这项工作通过测试该字符是否是小写字母或大写字母来完成。测试的前半部分由以下表达式组成:
(c>=‘a’&&c<=‘z’)
如果c位于字符‘a’和‘z’之间,表达式为TRUE;就是说,如果c是小写字母,表达式为TRUE。测试的后半部分由下面这个表达式组成:
(c>=‘A’&&c<=‘Z’)
如果c位于字符‘A’和‘Z’之间,表达式为TRUE;就是说,如果c是大写字母,表达式为TRUE。这些测试适用于以ASCII的格式存储字符的计算机系统上。
如果变量c是字母符号,且第一个if测试获得成功,将显示消息“Its an alphabetic character.”。如果测试失败,将执行else if子句。这个子句确定此字符是否为数字。注意,这个测试将字符c和字符‘0’和‘9’进行比较,而不是整数0和9。这是因为字符是从终端读入的,并且字符‘0’到‘9’不同于数字0到9。事实上,在ASCII中,字符‘0’在内部实际表示为数字48,字符‘1’表示为49,依此类推。
如果c是数字字符,将显示短语“Its a digit.”。否则,如果c不是字母符号并且不是数字字符,将执行最后的else子句,并在终端显示短语“Its a special character.”。然后,程序就会结束。
应该注意到,尽管此处使用scanf读取单个字符,但键入字符之后仍需按下Enter键以便向程序发送输入。一般来说,无论何时从终端读入数据,在按下Enter键之前,程序都不会看到在数据行键入的任何数据。
假设在下一个例子中希望编写程序,允许用户使用以下形式键入简单的表达式:number operator number程序将计算表达式并在终端显示结果。可以识别的运算符是普通的加法、减法、乘法和除法运算符。在这里使用第4章“数据类型和表达式”的代码清单4-6中的Calculator类。因此,每个表达式都向计算器进行计算。
以下程序使用具有多个else if子句的大型if语句来确定要执行的运算。
注意最好使用标准库中的例程islower和isupper,并彻底避免内部表示问题。为此,需要在程序中包含程序行#import<ctype.h>。然而,放到这里只是为了说明的目的。
代码清单6-8
//Program to evaluate simple expressions of the form
//number operator number
//Implement a Calculator class
import<Foundation/Foundation.h>
@interface Calculator:NSObject
{
double accumulator;
}
//accumulator methods
-(void)setAccumulator:(double)value;
-(void)clear;
-(double)accumulator;
//arithmetic methods
-(void)add:(double)value;
-(void)subtract:(double)value;
-(void)multiply:(double)value;
-(void)divide:(double)value;
@end
@implementation Calculator
-(void)setAccumulator:(double)value
{
accumulator=value;
}
-(void)clear
{
accumulator=0;
}
-(double)accumulator
{
return accumulator;
}
-(void)add:(double)value
{
accumulator+=value;
}
-(void)subtract:(double)value
{
accumulator-=value;
}
-(void)multiply:(double)value
{
accumulator*=value;
}
-(void)divide:(double)value
{
accumulator/=value;
}
@end
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
double value1,value2;
char operator;
Calculator*deskCalc=[[Calculator alloc]init];
NSLog(@“Type in your expression.”);
scanf(“lf%c%lf”,&value1,&operator,&value2);
[deskCalc setAccumulator:value1];
if(operator==‘+’)
[deskCalc add:value2];
else if(operator==‘-’)
[deskCalc subtract:value2];
else if(operator==‘*’)
[deskCalc multiply:value2];
else if(operator==‘/’)
[deskCalc divide:value2];
NSLog(@“.2f”,[deskCalc accumulator]);
[deskCalc release];
[pool drain];
return 0;
}
代码清单6-8输出
Type in your expression.
123.5+59.3
182.80
代码清单6-8输出(重新运行)
Type in your expression.
198.7/26
7.64
代码清单6-8输出(重新运行)
Type in your expression.
89.3*2.5
223.25
scanf调用指定了要读入到变量value1、operator和value2中的3个值。double值可用%If格式字符读入。这就是用于读入变量value1值的格式,而变量value1是表达式的第一个运算数。
接下来,读入运算符。因为运算符是字符(+、-、*或/)而非数字,因此需要将它读入到字符变量运算符中。%c格式字符可告诉系统从终端读入下一个字符。格式字符串中的空格表示输入中允许存在任意个数的空格。这样在键入这些值时可以使用空格将运算数和运算符分隔开。
读入两个值和运算符之后,程序将第一个值存储到计算器的累加器中。然后,将operator的值和4个允许的运算数做比较。如果存在正确的匹配,相应的消息就会发送给计算器来执行运算。在最后一个NSlog中,检索累加器的值用于显示。然后程序就会结束。
在这里,我们将简单地讨论一下有关程序的彻底性(thoroughness)的话题。尽管以上程序确实可以完成设置的任务,但是因为它并没有考虑用户所犯的错误,所以实际并不完善。例如,如果用户为运算符错误地键入了一个,将发生什么情况?执行if语句时,程序只会失败,而且终端上不会显示消息警告用户他错误地键入了表达式。
另一种被忽略的情况是,用户键入一个使用0作为除数的除法运算。至此,你知道在Objective-C中永远不要试图用某个数去除以0。该程序应该能检查这种情况。
尽量预测程序可能失败或产生非预期结果的情形,然后采取预防性措施应付这些情况,是产生优秀而可靠的程序的必要部分。对程序运行大量的测试用例,通常可以找出没有考虑的特定情形。但它不仅仅如此。编写程序时,总是询问“如果……将发生什么情况?”并插入必要的程序语句来适当地处理该情况时,这就成为自律的问题。
代码清单6-8A,即修改之后的代码清单6-8,考虑了除以0和键入未知运算符的情形。
代码清单6-8A
//Program to evaluate simple expressions of the form
//value operator value
import<Foundation/Foundation.h>
//Insert interface and implementation sections for
//Calculator class here
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
double value1,value2;
char operator;
Calculator*deskCalc=[[Calculator alloc]init];
NSLog(@“Type in your expression.”);
scanf(“lf%c%lf”,&value1,&operator,&value2);
[deskCalc setAccumulator:value1];
if(operator==‘+’)
[deskCalc add:value2];
else if(operator==‘-’)
[deskCalc subtract:value2];
else if(operator==‘*’)
[deskCalc multiply:value2];
else if(operator==‘/’)
if(value2==0)
NSLog(@“Division by zero.”);
else
[deskCalc divide:value2];
else
NSLog(@“Unknown operator.”);
NSLog(@“.2f”,[deskCalc accumulator]);
[deskCalc release];
[pool drain];
return 0;
}
代码清单6-8A输出
Type in your expression.
123.5+59.3
182.80
代码清单6-8A输出(重新运行)
Type in your expression.
198.7/0
Division by zero.
198.7
代码清单6-8A输出(重新运行)
Type in your expression.
125$28
Unknown operator.
125
键入的运算符是斜杠时,对除法而言,要执行另一个测试以确定value2是否为0。如果是0,将在终端显示一条适当的消息;否则,将执行除法运算并显示结果。在这种情况下,要特别注意嵌套的if语句和对应的else子句。
程序结尾的else子句会捕获所有失败。因此,任何与测试的4个字符不匹配的operator值都会导致执行else子句,并导致在终端上显示“Unknown operator.”。
处理除0这个问题的较好方法是,在计算除法的方法内部执行测试。因此,可将divide:方法修改成以下形式:
-(void)divide:(double)value
{
if(value!=0.0)
accumulator/=value;
else{
NSLog(@“Division by zero.”);
accumulator=99999999.;
}
}
如果value非0,将执行除法;否则,将显示消息并将累加器设置为99999999。这是随意的,可以将它设置成0,或可能设置一个特殊值来表示出现一个错误条件。一般来说,最好让方法处理特殊的情况,而不是依赖程序员使用方法。