第5章 循环结构

在Objective-C中,有若干方法可以用于重复执行一系列代码。本章的主题是这些循环功能,它们由以下几部分组成:

·for语句

·while语句

·do语句

从一个简单的例子—计数开始讨论。

如果要把15个大理石弹子排列成一个三角形,排列后的弹子可能如图5-1所示:

三角形的第一行包含一个弹子,第二行包含两个弹子,依此类推。一般来说,包含n行的三角形可容纳的弹子总数等于1到n之间所有整数之和。这个和称为三角数(triangular number)。

如果从1开始,第4位三角数将等于1到4之间连续整数的和(1+2+3+4),即10。

第5章 循环结构 - 图1

图 5-1 三角形排列示例

假设要编写一个程序来计算第8位三角数的值,并在终端上显示。

显然可以在头脑中计算这个值,但是为了学习参数,假设你用Objective-C编写一个带有参数的程序来执行这个任务。代码清单5-1显示了这个程序。

代码清单5-1


import<Foundation/Foundation.h>

//Program to calculate the eighth triangular number

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

int triangularNumber;

triangularNumber=1+2+3+4+5+6+7+8;

NSLog(@“The eighth triangular number is%i”,triangularNumber);

[pool drain];

return 0;

}


代码清单5-1输出


The eighth triangular number is 36


如果计算相对较小的三角数,代码清单5-1中的方法工作良好,但是要找出第200位三角数的值,该程序将会如何处理呢?必须修改代码清单5-1,以便显式地将1到200之间的所有整数相加,这项工作肯定是冗长乏味的。值得庆幸的是,有一个比较简单的方法。

计算机的基本属性之一就是它能够重复执行一组语句。这种循环能力可使程序员开发出包含重复过程的简洁程序,这些过程能够以不同的方式执行成百上千的程序语句。Objective-C包含3种用于编写结构循环的程序语句。

5.1 for语句

看看使用for语句的程序。代码清单5-2的目的是计算第200位三角数。看看你是否可以找出for语句的工作方式。

代码清单5-2


//Program to calculate the 200th triangular number

//Introduction of the for statement

import<Foundation/Foundation.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

int n, triangularNumber;

triangularNumber=0;

for(n=1;n<=200;n=n+1)

triangularNumber+=n;

NSLog(@“The 200th triangular number is%i”,triangularNumber);

[pool drain];

return 0;

}


代码清单5-2输出


The 200th triangular number is 20100


需要对代码清单5-3进行一些解释。用于计算第200位三角数的方法其实与上一个程序中用于计算第8位三角数的方法是相同的,就是求1到200之间的整数之和。

在执行for语句之前,变量triangularNumber被设置为0。一般来说,在程序使用变量之前,需要将所有的变量初始化为某个值(和处理对象一样)。后面将会学到,某些类型的变量将给定默认的初始值,但是无论如何都应该为变量设置初始值。

for语句提供的机制可使你避免显式地写出1到200之间的每个整数。从某种意义上讲,这条语句将为你生成这些数字。

for语句的一般格式如下:


for(init_expression;loop_condition;loop_expression)

program statement


圆括号中的3个表达式init_expression、loop_condition和loop_expression建立了程序循环的“环境”。其后的program statement(当然是以一个分号结束)可以是任何合法的Objective-C程序语句,它们组成循环体。这条语句执行的次数由for语句中设置的参数决定。

for语句的第一部分标着init_expression,它用于在循环开始之前设置初始值。在代码清单5-2中,for语句这个部分将n的初始值设置为1。可以看到,赋值是一种合法的表达式形式。

for语句的第二部分用于指定继续执行循环所需的条件。换言之,只要满足这个条件,循环就将继续执行。再次参见代码清单5-2,for中的loop_condition是由以下关系表达式指定的:


n<=200


这个表达式可读做“n小于或等于200”。“小于或等于”运算符(由等号[=]和紧跟在其后的小于号[<]组成)仅是Objective-C程序设计语言提供的若干关系运算符中的一个。这些关系运算符用于测试特定的条件。如果满足条件,测试结果为真(或TRUE);如果不满足条件,测试结果为假(或FALSE)。

表5-1列出了Objective-C中可用的所有关系运算符。

第5章 循环结构 - 图2

关系运算符的优先级比所有的算术运算符的优先级都低。这意味着,例如以下表达式


a<b+c


将按


a<(b+c)


来求值。

这与你期望的相同。如果a的值小于b+c的值,表达式将为TRUE;否则表达式为FALSE。要特别注意等于运算符(==),不要将其与赋值运算符(=)混淆。表达式


a==2


用于测试a的值是否等于2,而表达式


a=2


用于将值2赋值给变量a。

选择要使用哪个关系运算符由所做的具体测试决定,有的情况下由你的具体偏好决定。例如,关系表达式


n<=200


可以等价地表示为


n<201


回到前一个例子中,只要关系测试结果为TRUE,在这个例子中n的值小于或等于200时,形成for循环体的程序语句—triangularNumber+=;将被重复执行。这条语句的作用是将n的值和triangularNumber的值加到一起。

不再满足loop_condition时,程序在for循环之后的程序语句继续执行。在该程序中,该循环终止之后将继续执行NSLog语句。

for语句的最后一部分包含一个表达式,它在每次执行循环体之后求值。在代码清单5-2中,loop_expression的作用是将n的值加1。因此,每次把n的值加到triangularNumber之后,它的值都要加1,而且该值将从1一直增加到201。

值得注意的是,n的最终值(即201),将不会加到triangularNumber的值上,因为只要不再满足循环条件,或只要n等于201,循环就会终止。

总之,for语句将按以下步骤执行:

1.先求初始表达式的值。这个表达式通常设置一个将在循环中使用的变量,对于某些初始值(例如0或1)来说,通常称作索引变量。

2.求循环条件的值。如果条件不满足(即表达式为FALSE),循环就立即终止。然后执行在循环之后的程序语句。

3.执行组成循环体的程序语句。

4.求循环表达式的值。这个表达式通常用于改变索引变量的值,最常见的情况是,将索引变量的值加1或减1。

5.返回到步骤2。

记住,循环条件要在进入循环时在第一次执行循环体之前立即求值。还要记住,不要在循环末尾处的结束圆括号后面放置分号,这将导致循环立即终止。

代码清单5-2在计算最终结果的过程中,实际生成了所有前200个三角数,所以,生成这些数字的一个表格是不错的主意。然而,为了节省空间,假定你只想打印一张包含前10个三角数的表。代码清单5-3执行这个任务。

代码清单5-3


//Program to generate a table of triangular numbers

import<Foundation/Foundation.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

int n, triangularNumber;

NSLog(@“TABLE OF TRIANGULAR NUMBERS”);

NSLog(@“n Sum from 1 to n”);

NSLog(@“————-”);

triangularNumber=0;

for(n=1;n<=10;++n){

triangularNumber+=n;

NSLog(@“i%i”,n, triangularNumber);

}

[pool drain];

return 0;

}


代码清单5-3输出


TABLE OF TRIANGULAR NUMBERS

n Sum from 1 to n


1 1

2 3

3 6

4 10

5 15

6 21

7 28

8 36

9 45

10 55


在代码清单5-3中,前3个NSLog语句的目的仅仅是提供一个普通标题并标记输出列。

在显示适当的标题后,程序将计算前10个三角数。变量n用于记录当前的数字,你正在计算1到n的和,而变量triangularNumber用于存储第n个三角数的值。

for语句的执行首先是将变量n的值设置为1。前面提到过,在for语句之后的程序语句构成了程序循环的主体。但是如果不只想执行单个程序语句,而且想执行一组语句该怎么办呢?通过把这样的程序语句放入一对花括号中可达到这个目的。系统会把这组(或块)语句看作单个实体。一般来说,在Objective-C程序中能使用单个语句的任何位置均能使用语句块,不过要记住,语句块必须放在一对花括号中才能使用。

因此在代码清单5-3中,要把n加到triangularNumber值上的表达式和在程序循环构成体之后的NSLog语句放到一对花括号中。特别注意程序语句的缩进方式。快速扫视一下,可以轻易地确定哪些语句构成了for循环。还应该注意程序员采用不同的编码风格;一些人更喜欢用以下方式输入循环:


for(n=1;n<=10;++n)

{

triangularNumber+=n;

NSLog(@“i%i”,n, triangularNumber);

}


其中开始的花括号位于for的下一行。严格来说,这只是一个爱好问题,并不会影响程序。通过简单地将n的值加到前一个三角数,可计算出下一个三角数。第一次遍历for循环时,上一个三角数为0,因此n等于1时triangularNumber的新值就是n的值,即1。然后显示n的值和triangularNumber,并带有适当数目的空格,这些空格将插入到格式字符串中,以确保这两个变量的值可以排列到相应的列标题之下。

因为现在执行的是循环体,所以随后将求循环表达式的值。然而,这条for语句中的表达式看上去有些奇怪。当然,这肯定是一个印刷错误,它意味着插入n=n+1来替代这个看上去相当奇怪的表达式:


++n


事实是:++n其实是相当合法的Objective-C表达式。它引入了Objective-C程序设计语言中的一个新(而且相当独特)运算符—自增运算符。双加号(或叫做自增运算符)的作用是将其运算数加1。加1运算在程序设计中很常见,以至于创造了一个特殊符号专门完成这项任务。因此,表达式++n等价于表达式n=n+1。乍一看时,可能觉得n=n+1更易阅读,但是很快你就会习惯这种运算符,甚至更喜欢它的简洁性。

当然,没有哪种语言只提供自增运算符执行加1的操作,而不提供相应的运算符执行减1操作。正如你所猜测的,这种运算符叫做自减运算符,它由双减号来表示。因此,用Objective-C书写的表达式


bean_counter=bean_counter-1


可用自减运算符等价地表示成以下形式:


—bean_counter


一些程序员喜欢将++或—放到变量名后面,如n++或bean_counter—。这种情况是可以接受的,只不过是个人喜好问题。

你可能已经注意到,代码清单5-3输出的最后一行没有对齐。使用以下NSLog语句来替代代码清单5-3中对应的语句,可改正这个小毛病。


NSLog(“2i%i”,n, triangularNumber);


要验证这个改变解决了上述问题,下面给出修改后的程序输出(名为代码清单5-3A)。

代码清单5-3A输出


TABLE OF TRIANGULAR NUMBERS

n Sum from 1 to n


1 1

2 3

3 6

4 10

5 15

6 21

7 28

8 36

9 45

10 55


NSLog语句所做的主要改动是它包含了字段宽度说明。字符%2i告知NSLog例程:不仅在特定点显示整数值,而且要展示的整数应该占用显示器的两列。通常占用空间少于两列的任何整数(即,0到9之间的整数)在显示时都带有一个前导空格。这种情况称为向右对齐。

因而,通过使用字符宽度说明%2i,可以确保至少有两列将用于显示n的值,还能保证对齐triangularNumber的值。

5.1.1 键盘输入

代码清单5-2可计算出第200个三角数,但不能计算更多的数。如果要计算第50个或第100个三角数,该怎么办呢?好吧,如果是这种情况,就不得不更改程序,以便for循环可以执行合适的次数。还必须更改NSLog语句来显示正确的消息。

更加简单的解决方案可能是,在一定程度上允许程序向你询问要计算哪个三角数。然后,给出回答后,程序就可以计算出期望的三角数。使用一个名为scanf的例程,可以实现这样的解决方案。在概念上,scanf例程与NSLog例程类似。但是NSLog例程用于显示值,而scanf例程的用途是程序员可以把值输入到程序中。当然,如果使用图形用户界面(UI)编写Objective-C程序,如Cocoa或iPhone应用程序,那么在程序中可能根本不用NSLog或scanf。

代码清单5-4先询问用户要计算哪个三角数,然后计算该数并显示结果。

代码清单5-4


import<Foundation/Foundation.h>

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

int n, number, triangularNumber;

NSLog(@“What triangular number do you want?”);

scanf(“i”,&number);

triangularNumber=0;

for(n=1;n<=number;++n)

triangularNumber+=n;

NSLog(@“Triangular number%i is%i\n”,number, triangularNumber);

[pool drain];

return 0;

}


在后面的程序输出中,用户键入的数字(100)用黑体显示,以便和由程序显示的输出相区别。

代码清单5-4输出


What triangular number do you want?

100

Triangular number 100 is 5050


依照输出,可以看出数字100是由用户键入的。然后该程序计算第100个三角数并将结果5050显示在终端上。如果用户想要计算一个特定的三角数,可以键入10或者30这些数字。

在代码清单5-4中,第一个NSLog语句用于提示用户键入数字。当然,向用户提示输入内容总是好的。输出消息后,调用scanf例程。scanf的第一个参数是格式字符串,它不以@字符开头。NSLog的第一个参数始终是NSString,而scanf的第一个参数是C风格的字符串。在前面已经提及过,C风格的字符串前面不用加字符@。

格式字符串告知scanf要从控制台(或者是Terminal窗口,如果正在使用Terminal应用程序编译程序的话)读入的值类型。和NSLog一样,%i字符用于指定整型值。

scanf例程的第二个参数用于指定将用户键入的值存储在哪里。在这种情况下,变量number之前的&字符是必需的。但是不要担心它在此处的功能。在第13章“基本的C语言特性”中谈论指针时,我们将详细讨论这个字符,它实际上是一个运算符。

根据前面的讨论,可以看到代码清单5-4中的scanf调用指定要输入整型值并将其存储到变量number中。这个值代表用户希望计算哪个三角数。

键入这个数字之后(按下键盘上的Enter键,表示该数字的键入工作已完成),程序便计算指定的三角数。实现方式和代码清单5-2中的一样,唯一的不同是,此处没有用200作为界限,而是用number作为界限。

计算出期望的三角数之后,显示结果,然后程序的执行结束。