第三章: 循环结构

单纯循环

3.1.1 GOTO语句实现循环

循环结构用于实现重复的算法。它是三种基本结构(顺序、选择、循环)之一,具有重复执行某一段语句的功能,因为它以DO作为关键字,所以又称DO构造。在程序中存在两类循环:无条件的循环和有条件的循环。无条件循环是无休止地执行一个程序段,而有条件的循环是在满足一定条件时才执行循环。

F77允许使用GOTO语句来实现转移。GOTO语句的一般形式为:GOTO (语句标号)。由于GOTO语句破坏了语句顺序执行的正常状况,不符合结构化原则,因此一般不提倡使用GOTO语句。只有在一个基本结构内部可以使用GOTO语句。利用GOTO语句可以实现循环处理。如果在逻辑IF语句中使用GOTO语句就可以实现有条件的循环,循环操作能够在一定条件下结束。

3.1.2有循环变量的DO构造

a)    DO语句和循环次数

DO构造可分为不带循环变量与带循环变量两种形式。

当需要执行的循环次数为已知时,用DO语句实现循环比较方便。它的一般形式为:

DO [[标号][]] 循环变量=初值式,终值式[,增量式]

例:循环读入学生的学号和成绩30次并打印。

DO 10, N1301

READ *NUMGRADE

10  PRINT *NUMGRADE

上面是一个循环,第一行DO语句称为循环语句,DO后面的数10是一个标号,表示循环的范围到标号为10的语句为止,也就是反复执行READ语句和PRINT语句。DO语句中的N是“循环变量”,用它来控制循环次数,“N1301”的意思是:N的初值为1,终值为30,每执行一次循环N的值增加1。当N再变化到31时,由于它已超过了指定的终值30,不再执行循环。

下面的DO语句是合法的:

DO 10I=1102

DO 20 N=15

DO 100X=1.22.40.2

DO T2.5*25O./3.0.3

DO M1.512.515

DO语句的一些特点是:

*        在上述DO语句的一般形式中,当循环变量的增量(步长值)1时增量式可不写。

*        循环变量初值、终值和步长可以分别是常数、变量或表达式。如果是变量则它应预先被赋值。如果是表达式,则先计算出表达式的值。循环次数可以从循环初值、终值和步长计算出来:次数=INT((终值-初值+增量)/增量)。如果计算出的循环次数<0时,则按0处理,即一次也不执行循环。

例:对于D0 I1,10,2 其循环次数=INT((1012)2)5次。I按序分别取值为:1,3,5,7,9。对于D0 I10,1,2 则循环次数=0次。I不可取值,程序运行到这里时将跳过此循环。

*        循环变量的初值、终值和步长可以为正或负。初值、终值可以为零。但步长不应为0,否则循环变量的值永远不会超过终值,从而陷入死循环。

例:对于D0 I-1,-3,-1 其循环次数=INT((31-1)(1))3次。I按序分别取值为:-1,-2,-3

*        如果循环变量的类型和初值、终值和步长的类型不一致,则按赋值的规则处理,即需先将初值、终值和步长的类型转化成循环变量的类型,然后进行处理。为避免错误,应尽量使循环变量类型与初值、终值和步长的类型一致。

例:对于D0 I1.5,3.6,1.2 不要根据INT((3.6-1.51.2)/1.2)2而认为循环次数为2,而应当先将实型量转化为整型量,即变成相当的循环语句 DO I1,3,1 其循环次数为3次而不是2次。

例:对于D0 X1.5,3.6,1.2 由于循环变量不是整型的而是实型的,它的循环次数为2次。X取值分别是1.5,2.7

*        由于实型数在运算和存储时有一些误差,因而循环次数的理论值与实际值之间会有一些差别。这种情况在程序设计中常有发生,而且比较隐蔽不易发现。所以应该避免使用实型的循环变量,用整型循环变量计算出的循环次数是绝对准确的。

例:对于D0 X0.0,50.0,0.1理论循环次数=INT(50.1/0.1)501,但实际上在许多计算机上它只执行500次循环。原因是实数在内存中的误差使得增量值不是准确的0.1,由于循环的误差积累,到执行完500次循环后X的值可能已超过50.O,因而停止执行循环。改用整型循环变量时,则循环改写为:D0 I0,500 X=I/10

b)    DO循环执行步骤

循环执行过程按序为以下几个步骤:

(1).计算初值式、终值式、增量式的值,并将它们转换成循环变量的类型。

(2).将初值赋予循环变量。

(3).计算应循环的次数。

(4).检查循环次数,若≤0则跳过循环体,执行循环终端语句下面的一个执行语句。如果>0,则执行循环体。

(5).执行终端语句时,循环变量增值。

(6).循环次数减1

(7).返回(4),重复执行(4)(5)(6)(7)

c)    循环终端语句

上面介绍的循环中,循环终端语句为一般的执行语句。F90规定:循环终端语句可以是除了GOTO、块IFCASECYCLEDOELSEELSE IFEND IFENDEND SELECTEXITSELECT CASESTOPRETURN语句以外的任一可执行语句,如打印语句、赋值语句、输入语句、逻辑IF语句等都可以作为终端语句。特殊的循环终端语句是:END DO(常用于无语句标号时)CONTINUE(常用于有语句标号时)END DO语句使老的CONTINUE语句显得没有什么用处了,虽然F90的向下兼容性使CONTINUE语句仍然可用,但新编写的程序应该尽量使用以END DO结束的块DO构造。

例:用展开式求指数函数的数值。[e_312_01.f90]

a)    停止语句

CONTINUE语句本身不进行任何机器操作,只是将流程转到逻辑上的下一个语句,因此,CONTINUE语句又称为“空语句”,即进行“空操作”。与CONTINUE语句(继续功能)相对应的停止功能语句是STOPPAUSE语句。

PAUSE语句(F90中不推荐使用,在F95中被废除)暂时中止程序的运行,将系统挂起,使程序操作员可以执行其它操作系统命令。它的一般形式是:PAUSE [暂停值],暂停值为字符串常量或5位数以下的整型数,当程序运行至断点处将输出暂停值。如无暂停值的话,系统将输出默认的信息,WinNT/9x系统上输出“回车才能继续”的信息。

例:PAUSE 701

PAUSE 'ERROR DETECTED'

在这些例子中,它的用处是在程序中加入断点把程序分段,以便于一段一段地调试程序。Visual FortranDebug功能可代替这种程序调试方法,但它也可以用于一些输出情形。[e_312_02.f90]

STOP语句是停止运行,一个程序单位中可以有多个STOP语句,执行到任一个STOP语句处时,程序即完全中止运行。在子程序中如果有STOP语句,也是使整个程序停止运行而不是使控制返回主程序。STOP语句的一般形式为: STOP [停止值],与PAUSE语句类似,程序停止运行时将输出停止值。在F66中,END不作为执行语句而只作为程序单位的结束标志,需要在END之前用STOP语句使程序结束运行。有些人在写F77程序时仍保留此习惯,在END语句之前又写了一个STOP语句。

b)    DO循环嵌套

在一个DO循环中又完整地包含另一个DO循环,称为DO循环的嵌套。嵌套层数可以不限,各层的循环变量不允许同名。注意内循环应当完整地嵌套在外循环之内,即内循环是外循环体中的一部分,内外循环不能交叉。即:

do i=1,1O

do j=1,20

………

end do

end do

程序的执行过程是外循环执行一次,内循环执行一遍。[e_312_03.f90]

例:有10个实数,将它们按大小排列。[e_312_04.f90]

c)    DO循环规则

*        循环变量可以在循环体中被引用,但不应当再被赋值。

例:下面写法是不正确的,循环变量N不能在循环体内被重新赋值。

do n=1,1O

………

n=2*n

end do

*        循环的次数是根据循环变量的初值、终值和步长值计算出来的,在执行循环体期间是确定不变的。

*        可以用转移语句从循环体内转到循环体外,也可以在循环体内转移,但不允许从循环外转到循环内。(块规则)

例:下面写法是合法的(尽管不符合结构化原则)

do n=1,1O

………

if(n*x.gt.1.) go to 10

end do

10  ………

例:下面写法是非法的。

if(x.gt.1.) go to 10

do n=1,1O

………

10  ………

end do

*        多个DO循环可以共享一条循环终端语句,但循环体必须完全包含在外围DO循环体内。

*        如果DO循环出现在ifelse ifelse块内,则DO循环范围必须完全包含在该块中。

*        如果IF语句和SELECT CASE语句出现在DO循环范围内,则相应的END IF语句和END SELECT语句也必须出现在这个DO循环体内。

d)    DO循环

DO循环实际上是带控制循环变量的DO循环,但简化成只有DO循环的第一句,且把关键字DO隐去,成为I=m1,m2,m3形式。它不是独立语句,只是用作为读写语句的输入输出表中一个组成部分,用来控制重复读写的次数。例如:

READ *,(VALUE(I),I=1,20)

表示读入VALUE(1),VALUE(2),,VALUE(20)的值。

WRITE(*,*)(A,B,N=1,5)

表示在当前设备用默认格式重复输出AB的值5次。

DO循环只能作为输入输出表的一部分出现,不能用于其它场合。此时输入输出表的一般形式为:(I/O列表,循环变量名=初值,终值,增值)。也即把输入输出表与循环控制部分一起用括号括起,中间用逗号分开,称为隐DO表,写在读写语句后面作为读写对象。

DO表可以嵌套,如:

PRINT *, ((A(I,J),I=1,3),J=1,3)

先把内层(A(I,J),I=1,3)DO表作为输出表,再与隐DO控制J=1,3合成外层隐DO表。其控制机制与嵌套DO循环一致,先内层循环完,外层循环变量加一步长,再循环完内层。其打印值的次序如下:

A(1,1)A(2,1)A(3,1)A(1,2)A(2,2),…,A(3,3)

即先输出第1列,再输出第2列、第3列。如果把J作为内层,I作为外层循环变量,则输出是按行的:

PRINT *,((A(I,J),J=1,3),I=1,3)

则打印输出值的次序为:

A(1,1)A(1,2)A(1,3)A(2,1)A(2,2),…,A(3,3)