3.1 IF语句 在程序设计中常遇到这样的问题:某些运算和操作的执行取决于某条件是否成立。例如 y = a + b√x 若x ≧ 0 或 a + b√x 若x ≧ 0 a - b√x 若x < 0 即某种运算是否执行或执行哪一种运算要根据变量X的取值而定。这里x ≧ 0和x ﹤ 0是语句执行时的判别条件,在PACSAL语言中可用布尔表达式来表达这样条件。上述公式用PASCAL语句可表示为 IF x >= 0 THEN y:= a + b * sqrt(x); 或者 IF x >= 0 THEN y:= a +b *sqrt(x) ELSE y:= a – b * sqrt(abs(x)); 这里给出了PASCAL语言中IF语句的两种形式。
举例说明:某学校开学初进行英语分班考试。成绩80分以上者分在A班,现要统计出A班人数。用PASCL语句可写成: IF engmark>80 THEN class:=class+1; 其中,engmark为表示外语成绩的变量标识符,class为表示A班人数的变量标识符。在执行该IF语句前,变量engmar必须由输入语句或者赋值语句给以确定的值。关系表达式 engmark>80 表示判别条件,根据该表达式的值确定应执行什么操作。 在实际问题中,判别条件也可能比较复杂,是由几个条件组合而成。例如,分在A班的条件是外语成绩和数学成绩都在80分以上,如果用matmark作为表示数学的变量标识符,则可写出如下PASCAL语句 IF(engmark>80)AND(matmark>80)THEN class:=class+1; 其中AND为布尔运算符,它和在其两边的关系表达式一起构成一个布尔表达式,只有当这两个关系表达式的值都为true时,该布尔表达式的值才为true。如果分在A班的条件是外语成绩或都数学成绩在80分以上,则可写出如下PASCAL语句: IF(engmark>80)OR(matmark>80)THEN class:=class+1; 其中布尔运算符OR表示‘或’运算。它和在其两边的关系表达式一起构成一个布尔表达式。当两个关系表达式的值中有一个为true时。该布尔表达式的值即为true。 下面举一个完整的程序例子来说明如何使用IF语句。 例3-1 计算机读入两个量,将其中的较大者放在变量max中,并打印出来。 程序中使用max和min作为两个变量的标识符。temp作为临时存放的中间变量。现列出程序如下: PROGRAM getmax(input,output); VAR max , min,temp:integer; BEGIN read(max,min); IF max<min THEN BEGIN temp:=max; max:=min; min:=temp; END; writeln(‘max =’,max); END. 若输入数据为 19 43 若输出为 max = 43 在例3-1中,max,min,temp说明为整型量。执行程序时,首选读入两个值放入标识符为max和min的两个量中,然后由关系表达式进行判别:如果max小于min,则必须将两者的值交换,将较大值放入max中,但要完成这一交换操作必须使用一个中间存放单元temp,同时用三个赋值语句来实现。必须注意,我们不能写如下形式: IF max<min THEN temp:=max; max:=min; min:=temp; 因为按时前述IF语句的语法规则,对上述IF语句编译程序理解为在保留字THEN后面只执行一个语句,即 temp:=max; 其它两个语句被理解为IF语句的后继语句,只在关系表达式不成立时才执行,这显然是一种错误。为了避免出现混淆,在程序中必须用BEGIN和END将这三个语句括起来。编译程序处理时将BEGIN和END及其中的若干语句视为一个复合语句。在保留字THEN后面执行这一个复合语句。 复合语句以BEGIN开始,以END结束。BEGIN和END又称为语句括号。其中若干语句以分号分隔。复合语句这种结构是很重要的,以后还要陆续接触到这种结构。
例如,在上述外语分班的例子中,若外语成绩80分以上者分在A班,80分以下者分在B班。统计A班和B班的人数,可写出如下语句: IF engmark>80 THEN classa:=classa +1 ELSE classb:=classb+1;
例3-2 编写一个程序求一元二次方程 ax2+bx+c=0 的根。其中a不等于0。 从代数中已知,一般情况下该方程的根为 x=(-b/2a)+√b2-4ac /2a 其中b2-4ac称为二次方程式的根判别式。若b2-4ac≧0,则方程有两个实根;若b2-4ac<0,则方程有两个复根。我们可以将方程的解分成-b/2a以及√b2-4ac /2a两部分,分别用标识符term1和term2表示。程序编写如下: PROGAM quad(input,output); VAR a,b,c,disc,twoa,tem1,term2:real; comp:toolean; BEGIN read(a,b,c); writeln(‘quadratic with coefficients’); writeln(a,b,c); disc:=b*b-4*a*c twoa:=2*a; term1:=-b/twoa; term2:=sqrt(abs(disc))/twoa; comp:=disc<0; IF comp THEN BEGIN writeln(‘complex roots’); wirteln(‘teal part=’,term1,’imag part =’,term2); END ELSE BEGIN writeln(‘teal rools’); writeln(‘rootl=’,term1+term2,’root2=’,term1-term2); END END. 程序中语句执行部分首选读入方程的三个系数a,b,c再将它们打印出来。然后分别形成二次方程根的判别式disc以及twoa,term1,term2和comp等变量。在输出结果时用IF语句,根据布尔量comp的值判别出方程具有复根或者实根,分别打印输出。这里用到了IF…THEN…ELSE的语句形式。在THEN和ELSE的后面用BEGIN和END括起来的复合语句。
(三)IF语句的多重嵌套
在实际问题中,常要用到多重条件的嵌套。例如,我们要在三个不相等的数a,b,c中找出最大数,可写出如下语句: IF a>b THEN IF a>c THEN write(a) ELSE write(c) ELSE 这表示在a>b条件成立,即布尔表达式a>b的值为true的前提下,如果a>c的条件也成立,则a就是最大数;如果a>b而a<c时,c就是最大数。这里第一个保留字THEN后面又是一个IF语句。至此,我们还只是处理了a>b的情况,如果a>b条件不成立,则在最后一个ELSE的后面写出如下语句 IF b>c THEN write(b) ELSE write(c) 将以上语句合在一起,即为 IF a>b THEN IF a>c THEN write(a) ELSE write(c) ELSE IF b>c THEN writ.( ) ELSE write(c); 这说明在IF语句中,保留字THEN和ELSE后面的语句又可以是另一个IF语句,在PASCAL语言中,以下几种多重IF语句都是合法的: (1) IF b1THEN s1 ELSE IF b2 THEN s2; (2) IF b1THEN s1 ELSE IF b2 THEN s2 ELSE s3; (3 ) IF b1THEN IF b2 THEN s1 (4) IF b1THEN IF b2 THEN s1 ELSE s2 (5) IF b1THEN IF b2 THEN s1 ELSE s2 ELSE s3; 各语句中b1,b2为布尔表达,s1,s2,s3为PASCAL语句,它既可以是简单语句,也可以是复合语句。 仔细分析上述第4种多重IF语句,发现有可能出现语法上的二义性,即其中ELSE可以看成和语句中的第一个THEN相配对,也可看成为和第二个THEN相配对。为避免这种二义性,PASCAL语言将上述第4种形式按下面的结构来执行。 IF b1THEN BEGIN IF b2THEN s1 ELSE s2 END 也就是说,ELSE总是与最邻近的THEN相配对的。如果在实际问题中需要将ELSE和第一个THEN配对,在程序中就应使用语句括号BEGIN和END,以免造成混淆。即 IF b1 THEN BEGIN IF b2 THEN s1 END ELSE s2;
例3-3 将三个实数值读入计算机,并将大小居中的值打印出来。 程序中使用多重IF语句,a,b,c为读入的三个实数。 PROGRAM middle(input,output); VAR A,b,c:real; BEGIN Read(a,b,c); IF a>b THEN IF b>c THEN writelr(b) ELSE IF a>c THEN writeln(c) ELSE writeln(a) ELSE IF a>c THEN writeln(a) ELSE IF b>c THEN writeln(c); ELSE writeln(b); END.
PROGRAM sumofseries(input,outpu); VAR n:integer; sum:real; BEGIN sum:=0; read(n); write(‘for’,n,’items sum=’) REPEAT sum:=sum+1/n; n:=n-1; UNTIL n=0; write(sum) END. 程序中n为循环控制变量,由read(n)语句n的初值。在循环体中赋值语句n:=n-1改变循环控制变量的值。每执行一次循环体,n的值减去1,直到n=0时为止。这时已求得级数前n项之和,从循环中转出以后,执行一输出语句,打印出sum的值。 REPEAT循环控制语句更常用于那些人们不能预先确定循环次数的情况。例如将例3-4中的问题改为求调和级数需要多少项才能满足不等式 1+1/2+1/3+…+1/n>val,其中val为某一固定值。 可写出如下PASCAL程序。 PROGRAM findn(input,output); VAR count:integer; sum,val:real; BEGIN count:=0; sum:=0; read(val); REPEAT count:=count+1; sum:=sum+1/count; UNTIL sum>val; write(count); END.
例3-5 用牛顿迭代法求解方程 f(x)=3x3-4x2-5x+13=0 迭代公式为: xk+1=xk-f(xk)/f’(xk) 其中f’(x)为函数f(x)的导数。当 dx=-f(xk)/f’(xk) 的绝对值小于某一很小的数e时,xk+1就作为方程的解。 为提高计算机的运行效率,将方程左部必定成如下形式: (((3x-4)*x-5)*x+13) 程序中取e=10-6 PROGRAM soi(input,output); CONST epsilon=1E-6; VAR x,d:ral: BEGIN read(x); REPEAT d:=- (((3x-4)*x-5)*x+13)-((9*x-8)*x-5); x:=x+d; write(‘x=’,x:20:10,’d=’,d:20:10); UNTIL (‘The root is ’,x:15:8); END. 方程的解为x=-1.54891000 。
上一节打印1到1000之间2的乘幂的值,也可用下列WHILE语句来实现: power:=1 WHILE power<1000 Do BEGIN Write(power); Power:=power*2 END; 这段程序中赋值语句 power:=1给变量power赋初值,它就是该循环语句中的控制变量。WHILE后的布尔表达式进行条件判别,确定是否要执行DO后面的语句。这语句即为循环体。每执行一次循环体,power的值就改变一次。一直到布尔表达式power<1000的值为false,循环为止。 可以看出,与REPEAT 语句的不同之处是WHILE语句一开始就判别布尔表达式的值;而REPEAT语句是在执行一次循环体以后才判别布尔表达式的值。所以,在REPEAT语句中至少要执行一次循环体,而WHILE语句中的循环体可能一次也不执行。此外,REPEAT语句中布尔表达式的值为false时执行循环体,而在WHILE语句中布尔表达式的值为true时执行循环体。 从上述土简单的程序段中还可以看出,执行WHILE循环 语句的主要步骤是: (1)初始化:给循环控制变量赋以初值。 (2)测试条件:如果布尔表达式的值为true,就执行DO后面的循环体;如果值是false,就跳过循环体去执行跟在循环体后面的语句。 (3)执行循环体:在循环体中必须包括改变循环控制变量的值的语句。 (4) 返回第(2)步重复执行。 WHILE循环语句是程序设计中经常使用的语句。下面举几个例子来说明。
例3-6 计算从0到某一数之间所有奇数的和。 所求数的上限以limit表示,奇数和用sum表示,odds 表示从0到limit之间的奇数。可写出程序如下: PROGAM sumodd(input,output); VAR Odds,limit,sum:integer; BEGIN Read(limit); Sum:=0; odds:=1; WHILE odds<=limit Do BEGIN Sum:=sum+odds; Odds:=odds+2; END:{WHILE} Writeln(‘The sum of odd numbers through’,limit,’is’,sum.) END. 如果输入数据为248,则会打印结果: The sum of odd numbers through 248 is 15376
例3-7 每隔150打印出00-1800之间的正弦值和余弦值。 本例题中使用PASCAL标准函数sin(x)和cos(x),其中x是以弧度计的。所以必须将读入的角度值化成弧度。 10=π/180弧度 θ0=θ*π/180弧度 程序编写如下: PROGRAM sincos(input,output); CONST Pi=3.14159; VAR Angle:integer; Radiant:real; BEGIN Writeln(‘angle(in degree)’,’sin(angle)’:15,’cos(angle)’:22); Angle:=0; WHILE angle<=180 Do BEGIN Radian:=angle*pi/180; Writeln(angle:8,/’ ’:10,sin*(radian),’ ’,cos(radian)); END END.
例3-8 输入若干实数x,打印出函数值trunc(x),round(x),abs(x),sqr(x),sqrt(abs(x)). 在程序中设置一判别标志,当x=0.0时就停止输出打印。 PROGRAM func(input,output); CONST Sent:=0.0 VAR X:real; BEGIN Read(x); Writeln(‘x’:10,’trunc(x)’:10,’round(x)’:10,’abs(x)’:10,’sqr(x)’:10,’sqrt(abs(x))’:15); WHILE x<>senti DO BEGIN Writeln(x:10:2,trunc(x):10,round(x):10,abs(x):10:2,sqr(x):10:2,sqrt(abs(x)):10:2); Read(x) END END.
还有一种控制循环结束的方法是使用标准函数eof。因为无论从终端键盘或者由卡片输入,数据都是以文件形式进行传送的。如前所述input文件是对应于终端键盘的标准文件。所以,可以用eof函数判断文件是否结束。在程序中可使用语句 WHILE NOT eof( ) DO <语句> 来控制循环的结束与否。详细举例请见第八章。
例3-9 编写一个译码程序将一句子译成数字代码。译码规则是以数字1代替字母A,数字2代替字母B,…26代替字母Z,如遇安全可靠则打印一个星号*。英文句子以‘*’结束。 由编码规则可知,其字母的数字编码都在1到26之间,且可用下列计算: code:=ord(letter)-ord(‘A’)+1; 其中,letter为字符型变量,其取值为26个英文字母中任意一个字母。Code为对应的数字编码。程序如下: PROGRAM encd(input,output); CONST Blank=’ ’ Termi=’ ’ Star=’ * ’ VAR Letter:char; Code:integer; BEGIN Read(letter); WHILE letter<>termi DO BEGIN IF letter=blank THEN write(star:2) ELSE BEGIN Code:=ord(leteer)-ord(‘A’)+1; Write(code:3) END; Read(letter); END; Write(termi) END. 若输入英文句子为,HE IS A STUDENT则输出编码为: 8 5 * 9 19 * 1 * 19 20 21 4 5 14 20