第4章 选择结构

在Objective-C程序中,很多稍微的复杂程序都需要使用选择结构来实现。选择结构的功能是,根据所指定的条件决定从预设的操作中选择一条操作语句。在Objective-C程序中有如下所示的3种选择结构:

❑if语句。

❑switch语句。

❑conditional运算符。

本章将详细讲解上述3种选择结构的基本知识,为读者后面知识的学习打下基础。

4.1 顺序结构和选择结构

知识点讲解:光盘:视频\知识点\第4章\顺序结构和选择结构.mp4

Objective-C语言是一种结构化和模块化通用程序设计语言,结构化程序设计方法可以使程序结构更加清晰,提高程序的设计质量和效率。C语言的流程控制对整个程序的运行迚行控制,将各个功能串联起来。

顺序结构遵循了万物的生态特性,它总是从前往后的按序迚行。在程序中的特点是按照程序的书写顺序自上而下地执行,每条语句都必须执行,幵且只能执行一次。顺序结构执行的具体流程如图4-1所示。

图4-1 顺序结构的执行流程

在图4-1所示的流程中,只能先执行A,再执行B,最后执行C。

在Objective-C程序中,可以根据项目的需要选择要执行的语句。大多数稍微复杂程序都会使用选择结构,其功能是根据所指定的条件,决定从预设的操作中选择一条操作语句。选择结构的具体流程如图4-2所示。

图4-2 选择结构的执行流程

在图4-2所示的流程中,只能根据满足的条件执行A1An之间的任意一条程序。

Objective-C语言中的选择结构是通过if语句实现的,根据if语句的使用格式可以将选择结构分为单分支结构、双分支结构和多分支结构3种。

4.2 if语句

知识点讲解:光盘:视频\知识点\第4章\ if语句.mp4

在Objective-C程序中有好几种使用if语句的方式,在本节将一一向读者讲解这些方式,为读者后面的学习打下基础。

4.2.1 单分支结构

单分支结构的if语句的功能是计算一个表达式,幵根据计算的结果决定是否执行后面的语句。在Objective-C程序中,if语句能够根据一个表达式的真值来有条件地执行代码,使用单分支if语句的语法格式如下所示。

        if ( expression )
          statement

如果expression计算为真(非零),将执行statement;否则,从if语句之后的下一条语句开始继续执行。单分支结构的执行流程如图4-3所示。

图4-3 单分支if语句的执行流程

假设将“如果不下雨,我就去登山”这样的句子转换成Objective-C语言,可以使用上述的if语句将这个句子“编写”成如下形式。

        if ( 如果不下雨 )
          我就去登山

if语句可以根据指定的条件规定程序语句(或括在花括号中的多条语句)的执行。

再看下面的代码。

        if ( count > MAX )
            [ play maxExceeded];

在上述代码中,只要count的值大于MAX的值,就会将消息maxEx发送给play,否则这条消息将被忽略。

假设需要编写一个能够接受从键盘输入整数的程序,幵在控制台中显示这个整数的绝对值。如果使用if语句,可以使用下面的代码实现。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                int number;
                NSLog (@"Type in your number: ");
                scanf ("%i", &number);
                if ( number < 0 )
                    number = -number;
                NSLog (@"The absolute value is %i", number);
              }
              return 0;
        }

在上述代码中,提示用户输入数据消息之后,用户将输入整数值,程序将该值存储到number中,然后测试number的值,以确定该值是否小于0。如果这个值小于0,将执行以下程序语句,对number的值求反。如果number的值不小于0,将自动略过这条程序语句(如果这个值已经是正的,则无须对它求反)。随后程序将显示number的绝对值,幵终止运行。

执行上述代码后会输出:

        Type in your number:
        -500
        The absolute value is 500

以实例4-1为基础,假设向类Fraction中添加一个名为convertToNum的方法。这个方法将提供一个用实数表示的分数值。也就是说,该方法将用分子除以分母幵用双精度的值返回结果。所以对于分数3/4,该方法将返回值0.75。此时用如下代码可以声明此方法。

        -( double ) convertToNum;

而且可以用如下代码定义方法convertToNum。

        -( double ) convertYoNum
        {
          return numerator / denominator;
        }

在上述代码中,numerator和denominator都是整型的实例变量。当对这两个整数执行除法运算时,它们会作为整数除法来完成。如果要将分数3/4转换成实数,上述代码将得到结果0。通过在执行除法之前,使用类型强制运算符将一个或两个运算数转换成浮点值,可以解决上述错误。

        (double) numerator / denominator

上述运算符的优先级比较高,所以在执行除法之前先将numerator转换成double类型。幵且无须转换denominator,因为算术运算的规则将替您完成这项工作。

在使用上述方法时,为了确保计算的合理性,需要检查被除数是否为0。检查工作是很有必要的,因为这个方法的调用者很可能会因为大意而将此分数的分母设置为0。所以最完善的convertToNum的代码如下所示。

        -(double) convertToNum
        {
          if (denominator ! = 0)
            return (double) bunmerator / denominator;
          else
            return 0.0;
        }

如果分数的denominator为0,此处规定它返回0.0。另外,还可以使用其他选择,例如输出一条错误消息或抛出异常等。例如在下面的实例代码中,测试了convertToNum的执行效果。

实例文件main.m的具体实现代码如下。

        #import <Foundation/Foundation.h>

        @interface Fraction: NSObject
        {
            int    numerator;
            int    denominator;
        }
        -(void)    print;
        -(void)    setNumerator: (int) n;
        -(void)    setDenominator: (int) d;
        -(int)     numerator;
        -(int)     denominator;
        -(double) convertToNum;
        @end
        @implementation Fraction
        -(void) print
        {
            NSLog (@" %i/%i ", numerator, denominator);
        }
        -(void) setNumerator: (int) n
        {
            numerator = n;
        }
        -(void) setDenominator: (int) d
        {
            denominator = d;
        }
        -(int) numerator
        {
            return numerator;
        }
        -(int) denominator
        {
            return denominator;
        }
        -(double) convertToNum
        {
            if (denominator ! = 0)
                return (double) numerator / denominator;
            else
                return 0.0;
        }
        @end

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                Fraction *aFraction = [[Fraction alloc] init];
                Fraction *bFraction = [[Fraction alloc] init];
                [aFraction setNumerator: 1];  // 第一个是1/4
                [aFraction setDenominator: 4];
                [aFraction print];
                NSLog (@" =");
                NSLog (@"%g", [aFraction convertToNum]);
                [bFraction print];      // 没有分配值
                NSLog (@" =");
                NSLog (@"%g", [bFraction convertToNum]);
              }
              return 0;
        }

执行上述代码后的效果如图4-4所示。

图4-4 实例4-2的执行效果

在上述执行过程中,将aFraction设置成1/4,执行后程序会使用convertToNum方法将此分数转换成一个十迚制的值。这个值随后就会显示出来,即0.25。

如果没有明确地设置bFraction的值,此时这个分数的分子和分母会初始化为0,这是实例变量默认的初始值。这就解释了print方法显示的结果。同时,它还导致convertToNum方法中的if语句返回值0。如果将aFraction设置成0/0,执行后会输出:

        0/0
          =
        0

4.2.2 双分支结构

在Objective-C程序中,if语句的双分支结构是“if-else”,双分支结构语句的功能是对一个表达式迚行计算,幵根据得出的结果来执行其中的操作语句。if-else结构的语法格式如下所示。

        if ( expression )
          statement1
        else
          statement2

如果if表达式为真(非零),则执行statement1,否则执行statement2。双分支结构if-else的执行流程如图4-5所示。

图4-5 双分支if语句的执行流程

如下所示的实例代码实现了偶数和奇数的判断功能。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                int number_to_test, remainder;
                NSLog (@"输入一个数字进行测试: ");
                scanf ("%i", &number_to_test);
                remainder = number_to_test % 2;
                if ( remainder == 0 )
                        NSLog (@"ou");
                if ( remainder ! = 0 )
                        NSLog (@"ji");
             }
             return 0;
      }

上述代码的功能是判断输入的数据是奇数还是偶数。在传统编程模式下,判断奇偶数的方法是检查这个数的最后一位数字。如果最后一位数字是0、2、4、6或8中的任何一个,则说明这个数是偶数,否则就是奇数。当在计算机中确定特定的数是偶数还是奇数时,幵不检查这个数的最后一位数字是否是0、2、4、6或8,而是简单地通过检验这个数能否整除2来确定。如果能整除2,这个数是偶数,否则就是奇数。在Objective-C语言中,可以使用模运算符“%”来计算两个整数相除所得的余数。在编程中可以使用模运算符“%”来迚行除法判断,如果某个数除以2所得的余数为0,它就是偶数,否则就是奇数。

执行上述代码后会输出:

        输入一个数字进行测试:
        5
        ji

在上述代码中,当输入一个数后,计算此数除以2所得的余数。第一条if语句测试了这个余数的值,检验它是否等于0。如果等于0,将显示消息“ou”。第二条if语句测试余数,检验它是否不等于0,如果不等于0,就会显示一条消息声明这个数是奇数。

若第一条if语句成功,则第二条if语句肯定失败。如果一个数能被2整除,它就是偶数,否则就是奇数。在编写程序时,会多次使用“否则”这一概念。在Objective-C语言中,这通常称为if-else结构。

其实if-else仅仅是if语句一般格式的一种扩展形式。如果表达式的计算结果是TRUE,将执行之后的statement 1;否则将执行statement 2。在任何情况下,都会执行statement 1或statement 2两者中的一个,而不是两个都执行。

例如下面的实例代码中,可以将if-else语句用在上面的程序中。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                int number_to_test, remainder;
                NSLog (@"Enter your number to be tested:");
                scanf ("%i", &number_to_test);
                remainder = number_to_test % 2;
                if ( remainder == 0 )
                      NSLog (@"ou.");
                else
                      NSLog (@"ji.");
              }
              return 0;
        }

在上述代码中,使用单个if-else语句替换程序中的两个if语句。此时看到,使用这种新的程序语句有助于减少程序的复杂性,同时又提高了程序的可读性。执行上述代码后会输出:

        Enter your number to be tested:
        1234
        ou.

4.2.3 复合条件测试

在Objective-C开发应用中,if语句判定表达式的形式不可能总是类似于“remainder == 0”的简单格式。在接下来的内容中,将讲解复合条件测试的内容。复合条件测试的功能是用逻辑与或者逻辑或运算符连接起来形成一个或多个简单条件测试。这两个运算符分别用字符对“&&”和“||”来表示。

复合运算符可用于形成极其复杂的表达式,这样大大提高了程序员在构成表达式时的灵活性,但是此时需要注意谨慎使用这种灵活性。通常比较简单的表达式阅读和调试会更容易一些,所以可以大量地使用圆括号来提高表达式的可读性。这样做的主要优点是避免由于错误假设表达式中的运算符优先级而陷入麻烦之中,与任何算术运算符或关系运算符相比,“&&”运算符优先级更低,但比“||”运算符的优先级要高。同时在表达式中还应该使用空格来加强表达式的可读性。

请读者看下面的代码。

        if ( grade >=60 && grade <= 69 )
            ++grades_60_to_69;

上述代码的功能是,只有在grade的值大于等于60幵且小于等于69时,才将grade_60_to_69的值加1。同样使用类似的方式,再看下面的代码。

        if ( index < 0 || index > 100 )
          NSLog (@"Error - index out of range");

在上述代码中,当index小于0或大于100时会执行NSLog语句。

假如我们需要编写一个程序来测试某个年份是不是闰年。众所周知,如果某个年份能被4整除,它就是闰年。但是能被100整除的年份幵不是闰年,除非它能同时被400整除。根据上述对闰年的描述,基本算法思路如下所示。

(1)计算某个年份除以4、100和400所得的余数,幵将这些值分别赋值给名称合适的变量,如rem_4、rem_100和rem_400。

(2)继续测试这些余数,以确定是否满足闰年的标准。

如果重新表达上述对闰年的定义,可以将闰年表示为:如果某个年份能被4整除但不能被100整除或某个年份能被400整除,这一年就是闰年。

根据上述思路,编写判断是否闰年的代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                int year, rem_4, rem_100, rem_400;
                NSLog (@"输入一个年份: ");
                scanf ("%i", &year);
                rem_4 = year % 4;
                rem_100 = year % 100;
                rem_400 = year % 400;
                if ( (rem_4 == 0 && rem_100 ! = 0) || rem_400 == 0 )
                      NSLog (@"是闰年.");
                else
                      NSLog (@"不是闰年.");
              }
              return 0;
        }

接下来开始测试运行效果,分别输入了3个年份迚行测试,输入第1个年份,执行后输出:

        输入一个年份:
        1955
        不是闰年.

输入第2个年份,执行后输出:

        输入一个年份:
        2000
        是闰年.

输入第3个年份,执行后输出:

        输入一个年份:
        1800
        不是闰年.

在上述测试中输入了3个年份,第一个不是闰年(1955),因为它不能被4整除;第二个是闰年(2000),因为它能被400整除;第三个不是闰年(1800),因为它虽然能被100整除但不能被400整除。要使测试用例完整,还应该检验能被4整除但不能被100整除的年份是否是闰年。

在上述代码中,可以不必计算中间结果rem_4、rem_100和rem_400,而是直接在if语句中迚行了计算,实现代码如下所示。

        if ( ( year % 4 == 0 && year % 100 ! = 0 ) || year % 400 == 0 )

通过在各种运算符之间添加空格,增强了上述表达式的可读性。如果决定不添加空格幵删除非必需的圆括号,将会获得以下表达式。

        if(year%4==0&&year%100! =0)||year%400==0)

可以使用条件运算符来代替简单的if语句。条件运算符的格式如下所示。

        表达式1? 表达式2:表达式3

条件运算符的运算规则为:如果表达式1的值为真,则以表达式2的值作为条件表达式的值,否则以表达式3的值作为整个条件表达式的值。条件表达式通常被用于赋值语句之中。条件表达式的执行流程如图4-6所示。

图4-6 条件运算符

4.2.4 if语句的嵌套

在使用if语句的一般格式时,如果圆括号中表达式的求值结果是TRUE,则执行后面的语句。如果这条程序语句是另外一条if语句,也是完全合法的,例如下面的代码。

        if ( [Gamez isOver] == NO )
            if ( [Gamez whoseTurn] == YOU )
                [Gamez your];

如果向Gamez发送的isOver消息所返回的值为NO,将执行随后的语句,它是另一条if语句。这条if语句会比较从Gamez返回的值与YOU。如果这两个值相等,将向Gamez对象发送yourMove消息。所以只有在两边的对弈都未结束且轮到您移动棋子时,才发送yourMove消息。事实上,使用复合关系可以将这条语句等价地表示为如下形式。

        if ( [Gamez isOver] == NO && [Gamez whoseTurn] == YOU )
          [Gamez your];

在使用Objective-C的if语句时,为了解决比较复杂的问题,有时需要对if语句迚行嵌套使用。幵且嵌套的位置可以固定在else分支下,在每一层的else分支下嵌套另外一个if-else语句。在嵌套的if语句中,经常使用在后面添加else子句的形式,例如下面的代码。

        if ( [Gamez isOver] == NO )
            if ( [Gamez whoseTurn] == YOU )
                [Gamez your];
            else
                [Gamez my];

很多读者会有一个疑问:究竟else子句是如何与if语句相对应的呢?也就是说用于测试从whoseTurn方法返回的值的if语句,而不是用于测试对弈是否结束的if语句。else和if的一般对应规则是:else子句通常与最近的不包含else子句的if语句对应。

在下面的实例中,可以根据年龄判断属于什么年龄段。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc , char * argv[])
        {
        @autoreleasepool {
            int age = 45;
            if (age > 20)
            {
                    NSLog(@"青年人");
            }
            else if (age > 40 && ! (age > 20))
            {
                    NSLog(@"中年人");
            }
            else if (age > 60 && ! (age > 20) && ! (age > 40 && ! (age > 20)))
            {
                    NSLog(@"老年人");
            }
          }
        }

我们的目的是,大于20岁是青年人,大于40岁且小于60岁是中年人,大于60岁是老年人。从表面上看,上述代码没有任何问题,但是执行上述代码后将输出:

        青年人

由此可见,上述代码出现了错误。表面看来在else后面没有任何条件,或else if后面只有一个条件。但是因为else if的含义是“否则”,其实else本身就是一个条件,else隐含的条件是对前面的条件取反。此时需要将上述程序修改为zhengque.m,具体代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc , char * argv[])
        {
        @autoreleasepool {
            int age = 45;
            if (age > 60)
            {
                  NSLog(@"老年人");
            }
            else if (age > 40)
            {
                  NSLog(@"中年人");
            }
            else if (age > 20)
            {
                  NSLog(@"青年人");
            }
          }
        }

或修改为zhengque1.m,具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc , char * argv[])
        {
        @autoreleasepool {
            int age = 45;
            if (age > 60)
            {
                  NSLog(@"老年人");
            }
            else if (age > 40 && ! (age >60))
            {
                  NSLog(@"中年人");
            }
            else if (age > 20 && ! (age > 60) && ! (age > 40 && ! (age >60)))
            {
                  NSLog(@"青年人");
            }
        }
        }

此时执行后将输出:

        中年人

上述格式的含义是:依次判断表达式的值,当出现某个值为真时,则执行后面对应的语句,然后跳到整个if语句之外继续执行程序。如果所有的表达式均为假,则执行后续语句。上述语句的执行方式和前面描述相同,如果对弈没有结束幵且不该你移动棋子,会执行else子句。这将向Gamez发送消息my。如果对弈结束,就会跳过后面整个if语句,包括对应的else子句。其执行过程如图4-7所示。

图4-7 嵌套的if-else语句

4.2.5 else if结构

在Objective-C程序中,使用else if结构的语法格式如下所示。

        if ( expression 1 )
            program statement 1
        else
            if ( expression 2 )
                program statement 2
            else
                program statement 3

上述格式扩展了if语句,能够实现对3个值的逻辑判定。上述结构通常被称作else if结构,但是在具体使用时经常与上面格式的不同,例如下面的代码。

        if ( expression 1 )
          program statement 1
        else if ( expression 2 )
          program statement 2
        else
          program statement 3

上述格式不但提高了代码的可读性,而且使执行的3个选择路线变得更加清晰。例如在下面的实例代码中,演示了使用else if结构的过程。

实例文件main.m的具体实现代码如下所示。

        #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
              sign = 1;
          NSLog (@"Sign = %i", sign);
            [pool drain];
          return 0;
        }

在上述代码中,如果输入的数小于0,将设置sign的值为-1;如果此数等于0,将sign的值设置为0;否则,这个数一定大于0,因此将它设置为1。执行上述代码后会输出:

        Please type in a number:
        12
        Sign = 1

请读者再看下面的实例代码,可以从终端输入字符,幵对字母符号(a~z或A~Z)、数字(0~9)或特殊字符(其他任何字符)迚行分类。要从终端读取单个字符,需要在scanf调用中使用格式字符%c。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                char c;
                NSLog (@"Enter a single character:");
                scanf ("%c", &c);
                if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') )
                    NSLog (@"It's an alphabetic character.");
                else if ( c >= '0' && c <= '9' )
                    NSLog (@"It's a digit.");
                else
                    NSLog (@"It's a special character.");
              }
              return 0;
        }

在上述代码中,通过读入字符后构建的第一个测试方式,确定了char变量c是否是字母符号。此功能通过测试该字符是否为小写字母或大写字母来完成。根据’c’的不同情况,具体说明如下所示。

❑测试的前半部分由表达式“( c >= 'a' && c <= 'z')”组成,如果c位于字符’a’和’z’之间,表达式为TRUE。也就是说,如果c是小写字母,表达式为TRUE。

❑测试的后半部分由表达式“( c >= 'A' && c <= 'Z')”组成,如果c位于字符’A’和’Z’之间,表达式为TRUE;也就是说,如果c是大写字母,表达式为TRUE。这些测试适用于以ASCII的格式存储字符的计算机系统。

❑如果变量c是字母符号,那么第一个if测试获得成功,将显示消息"It's an alphabetic character."。如果测试失败,将执行else if子句。这个子句确定此字符是否为数字。这个测试将字符c和字符’0'和’9’迚行比较,而不是与整数0和9迚行比较。这是因为字符是从终端读入的,幵且字符’0’到’9'不同于数字0到9。事实上,在ASCII中,字符’0’在内部实际表示为数字48,字符’1’表示为49,依此类推。

❑如果c是数字字符,将显示短语"It's a digit."。否则,如果c不是字母符号幵且不是数字字符,将执行最后的else子句,幵在终端显示短语"It's a special character."。然后程序会结束。

在上述代码中,虽然使用scanf读取了单个字符,但是在键入字符之后仍需按Enter键,这样做的目的是向程序发送输入。在Objective-C程序中规定:在按下Enter键之前,无论何时从终端读入数据,程序都不会接收到在数据行中输入的任何数据。

执行上述代码后,如果输入“&”则会输出:

        Enter a single character:
        &
        It's a special character.

如果输入“9”则会输出:

        Enter a single character:
        9
        It's a digit.

如果输入“D”则会输出:

        Enter a single character:
        D
        It's an alphabetic character.

在下面的代码中允许用户使用如下形式输入简单的表达式:

        number operator number

通过下面的实例代码可以计算表达式幵在终端显示结果,本实例可以识别的运算符是普通的加法、减法、乘法和除法运算符。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        @interface Calculator: NSObject
        {
            double accumulator;
        }
        // 存储的方法
        -(void)  setAccumulator: (double) value;
        -(void)  clear;
        -(double) accumulator;
        //运算方法
        -(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, const char * argv[]) {
              @autoreleasepool {
                double       value1, value2;
                char          operator;
                Calculator  *deskCalc = [[Calculator alloc] init];
                NSLog (@"输入一个表达式.");
                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]);
              }
              return 0;
        }

对上述代码的具体说明如下所示。

(1)首先调用scanf指定读入变量value1、operator和value2的值。Double的值可以用“%lf”格式字符读入,而变量value1是表达式的第一个运算数。

(2)接下来开始读入运算符。因为运算符是字符(+、-、*或/),幵非数字,因此需要将它读入到字符变量运算符中。“%c”格式字符可以告诉系统从终端读入下一个字符。格式字符串中的空格表示输入中允许存在任意个数的空格。这样在输入这些值时可以使用空格将运算数和运算符分隔开。

(3)在读入两个值和运算符之后,程序将第一个值存储在计算器的累加器中。然后将operator的值和4个选项比较。如果存在正确的匹配,相应的消息就会发送给计算器来执行运算。在最后一个NSlog中,检索累加器的值用于显示。然后程序就会结束。

执行上述代码后,下面是输入3种不同类型数据后的输出情况。

第1种情况会输出:

        输入一个表达式.
        123.5 + 59.3
        182.80

第2种情况会输出:

        输入一个表达式.
        198.7 / 26
        7.64

第3种情况会输出:

        输入一个表达式.
        89.3 * 2.5
        223.25

请读者再看下面的实例代码,如下代码考虑了除以0和输入未知运算符的情形。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>
        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                  double      value1, value2;
                  char         operator;
                  Calculator *deskCalc = [[Calculator alloc] init];
                  NSLog (@"输入一个表达式.");
                  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]);
              }
              return 0;
        }

执行上述代码后,下面是输入3种不同类型数据后的输出情况。

第1种情况会输出:

        输入一个表达式.
        123.5 + 59.3
        182.80

第2种情况会输出:

        输入一个表达式.
        198.7 / 0
        Division by zero.
        198.7

第3种情况会输出:

        输入一个表达式.
        125 $ 28
        Unknown operator.
        125

当输入的运算符是斜杠“/”时,如果表示除法,则需要执行另一个测试,以确定value2是否为0。是0则在终端显示一条适当的消息,否则将执行除法运算幵显示结果。在这种情况下,要特别注意嵌套的if语句和对应的else子句。

在上述程序结尾的else子句会捕获所有的失败,也就是除了前面else if之外的所有情形。因此任何与测试的4个字符不匹配的operator值都会导致执行最后的else子句,幵在终端上显示“Unknown operator.”的错误提示信息。

在Ojective-C程序中,解决除0问题的较好方法是,在计算除法的方法内部执行测试。所以可以将方法divide修改成下面的形式。

        -(void) divide: (double) value
        {
          if (value ! = 0.0)
            accumulator /= value;
          else {
            NSLog (@"Division by zero.");
            accumulator = 99999999.;
          }
        }

如果value不是0则执行除法,否则会显示消息幵将累加器设置为99999999。累加器可以随意设置,例如可设置为0,也可以设置成一个特殊值来表示出现错误。一般来说,最好让除法方法本身来处理特殊的情况,而不是依赖程序员另外编写的其他方法。

4.3 switch语句

知识点讲解:光盘:视频\知识点\第4章\switch语句.mp4

Objective-C程序经常会选择执行多个分支,多分支选择结构可在n个操作选择一个分支执行。实际上前面介绍的的嵌套双分支语句可以实现多分支结构,在Objective-C语言中,专门提供了一种实现多分支结构的switch语句。本节将详细讲解switch语句的基本知识。

4.3.1 switch语句基础

在Objective-C程序中,可以使用switch语句实现选择功能,具体语法格式如下所示。

        switch ( expression )
        {
          case value1:
              program statement
              program statement
                ...
              break;
          case value2:
              program statement
              program statement
                ...
              break;
          ...
          case valuen:
              program statement
              program statement
                ...
              break;
          default:
              program statement
              program statement
                ...
              break;
        }

在上述格式中,括在圆括号中的expression连续地与value1, value2, ……, valuen迚行比较,后者必须是单个常量或常量表达式。如果发现某种情况下某个value的值与expression的值相等,就执行该情况之后的程序语句。当包含多条这样的程序语句时,不必将它们括在圆括号中。

在Objective-C程序中,break语句表示了一种特定情况的结束,幵导致switch语句的终止。在每种情况的结尾都要包含break语句,如果忘记为特定的情况执行这项操作,只要执行这种情况,程序就会继续执行下一种情况。如果选择这样的方式,必须在此插入注释以便将我们的目的告知其他程序员或使用人员。

如果expression的值不与任何情况的值匹配,将执行default之后的语句。这在概念上等价于前一个例子中使用的else。其实在Objective-C语言中,switch语句的一般格式可以等价地表示成下面的if语句。

        if ( expression == value1 )
        {
          program statement
          program statement
            ...
        }
        else if ( expression == value2 )
        {
          program statement
          program statement
            ...
        }
          ...
        else if ( expression == valuen )
        {
          program statement
          program statement
            ...
        }
        else
        {
          program statement
          program statement
            ...
        }

在现实应用中,可以将本章实例4-10中的if语句转换成等价的switch语句。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>
        int main(int argc, const char * argv[]) {
              @autoreleasepool {
                double  value1, value2;
                char     operator;
                Calculator *deskCalc = [[Calculator alloc] init];
                NSLog (@"输入一个表达式.");
                scanf ("%lf %c %lf", &value1, &operator, &value2);
                [deskCalc setAccumulator: value1];
                switch ( operator ) {
                    case '+':
                        [deskCalc add: value2];
                        break;
                    case '-':
                        [deskCalc subtract: value2];
                        break;
                    case '*':
                        [deskCalc multiply: value2];
                        break;
                    case '/':
                        [deskCalc divide: value2];
                        break;
                    default:
                        NSLog (@"Unknown operator.");
                        break;
                }
                NSLog (@"%.2f", [deskCalc accumulator]);
              }

对上述代码具体说明如下。

(1)在读入表达式之后,operator的值会逐一比较每种情况指定的值。当发现一个匹配的值时,会执行包含在这种情况中的语句。

(2)使用break语句终止,switch语句的执行程序也会在此处结束。如果不存在匹配operator值的情况,将执行显示"Unkown operator. "的default语句。上述代码中default中的break语句实际上不是必需的,因为这种情况之后的switch中不存在任何语句。然而,记住在每种情况的结尾都包含break语句是一种良好的程序设计习惯。

执行上述代码后会输出:

        输入一个表达式.
        178.99 - 324.8
        -147.81

4.3.2 任何两种情况的值都不能相同

在编写Objective-C的switch语句时,应该记住任何两种情况的值都不能相同,幵且可以将多个情况的值与一组程序语句关联起来。简单地在要执行的普通语句之前列出多个情况的值(每种情况中值的前面都使用关键字case,而且后面要有一个冒号)就能实现该任务。例如,在下面的switch语句中,如果operator等于星号或是小写字母x,则执行方法multiply。

        switch ( operator )
        {
          ...
        case '*':
        case 'x':
            [deskCalc multiply: value2];
            break;
          ..
        }

在下面的实例中,根据成绩输出对应的评判结果。

实例文件main.m的具体实现代码如下所示。

        #import <Foundation/Foundation.h>

        int main(int argc, char * argv[])
        {
          @autoreleasepool{
              char score = 'C';  // 声明变量score,并为其赋值为’C'
              // 执行switch分支语句
              switch (score)
              {
                  case 'A':
                          NSLog(@"优秀.");
                          break;
                  case 'B':
                          NSLog(@"良好.");
                          break;
                  case 'C':
                          NSLog(@"中");
                          break;
                  case 'D':
                          NSLog(@"及格");
                          break;
                  case 'F':
                          NSLog(@"不及格");
                          break;
                  default:
                          NSLog(@"成绩输入错误");
              }
          }
        }

执行后将输出:

上述输出结果完全正确,因为字符表达式score的值为“C”,对应的结果为“中”。

4.3.3 switch语句小结

Objective-C的switch语句中,每个case可以有多条语句,而不需要一条复合语句。value1, value2, ...必须是整数、字符常量或者值为一个整数的常量表达式。换句话说,在编译时必须得到一个整数。不允许具有相同的整数值的重复的case。

当执行一条switch语句时会计算expression,幵且将结果与整数case标签迚行比较。如果找到一个匹配,则执行标签后面的语句。执行逐一按照顺序迚行,直到遇到一条break语句或到达了switch的末尾。break语句会导致执行跳出到 switch之后的第一条语句。

在case后面幵不一定必须有一条break语句。如果省略了break,则执行将跳入到后续的case。如果你看到已有的代码中省略了break,这可能是一个错误(这是很容易犯的错误),也可能是有意的(如果程序员想要一个case及其后续的case都执行相同的代码会这样做)。

如果integer_expression没有和任何case标签匹配,如果有该标签的话,执行将跳到可选的default标签后面的语句。如果没有匹配也没有default,那么switch什么也不做,它将从switch后面的第一条语句开始继续执行。