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,或可能设置一个特殊值来表示出现一个错误条件。一般来说,最好让方法处理特殊的情况,而不是依赖程序员使用方法。