Fortran基本计算之循环控制结构.docx
《Fortran基本计算之循环控制结构.docx》由会员分享,可在线阅读,更多相关《Fortran基本计算之循环控制结构.docx(21页珍藏版)》请在冰豆网上搜索。
Fortran基本计算之循环控制结构
第11章 基本计算(三)循环控制结构
上章讨论的控制结构的特点是通过判别条件来对结构内的块进行选择,所对应的算法结构,最简单的例子,就是解一元二次方程,在输入方程所以的参数值之后,需要首先计算一个判别式,然后根据判别式的值,再选择使用哪个公式,也就是计算的途径,才能够给出最终的解。
在本章所讨论的控制结构的特点则是针对结构内的块进行多次的重复运算,每完成一次运算,都判别一下是否需要把此次运算结果作为输入,再进行一次运算。
这种控制结构所对应的算法结构,一个最简单的例子,就是求级数的部分和。
我们知道对于具有通项表达式的级数,求它的部分和的每一项,总是需要进行同样的运算过程,如果使用按照序列形式排列的程序结构,那么需要计算多少项,就需要写下多少条语句,把它们顺序排列下来,才能做到程序走一遍即完成计算。
这样的算法设计显然是没有利用运算过程里所表现的循环结构,如果使用一种控制结构与循环过程对应,让程序的运行能够重复表示通项公式的表达式计算,就能够用一个表达式代替所有项的表达式,显然更加合理。
【例11-1】设有一个级数:
如果要求级数在N=K时的值,如果一定要使用序列结构的程序,那么在程序当中肯定会出现如下K个表达式顺序排列的情形:
…
I=1
SUM=1/I**3
I=2
SUM=SUM+1/I**3
I=3
SUM=SUM+1/I**3
…
I=K
SUM=SUM+1/I**3
由于这K个表达式是一样的,因此如果使用如下的一个控制结构,只需要使用一个表达式赋值语句,就可以表示整个循环运算过程:
…
SUM=0.0
DOI=1,K
SUM=SUM+1/I**3
ENDDO
上面的控制结构,就是本章所要讨论的DO结构。
实际上这是一种基本的计算过程,为很多重要算法的实现提供了基础。
FORTRAN语言提供用来进行循环控制的主要结构就是DO结构,因此本章主要讨论的就是DO结构。
最后还会简略的讨论有关分支转移的实现问题,尽管它们不属于循环控制,但是由于在现代结构性编程风格的要求下,这种分支转移是一种过时的方式,因此简略地附加在本章后面。
11.1 DO结构
DO结构包含0个或多个语句或结构,它们在DO结构的其他部分的控制下进行重复运行。
这些被重复运算的语句或结构构成一个循环,DO结构控制了该循环的运行次数。
DO结构的运行分为如下步骤:
● 如果DO结构由一个DO变量控制,那么决定循环次数的该表达式的值首先需要计算出来;
● 然后决定循环部分是否运行;
● 如果运行循环部分,则完成循环后,更新DO变量,再进入第二个步骤;如果不需要运行循环部分,则退出该DO结构,进入程序的后续部分。
其中对于DO结构的循环部分的控制方式有3种:
● 利用一个循环变量从初始DO语句开始,以确定的方式增长(也可能是负方向增长), 该变量作为一个循环计数器,它的取值变化的次数标记了循环的次数;
● 利用WHERE条件;
● 使用简单DO结构,或者称为“永远DO”。
显然这种方式需要一个可执行语句来终止DO结构,例如EXIT语句,可以用来退出循环。
从DO结构的整体构造来看,DO结构分为两种形式:
● DO结构块;
DO结构本身构成一个块结构,总是使用一个ENDDO语句或CONTINUE语句来终止DO结构的运行。
● 非块DO结构。
非块DO结构本身不构成一个块状结构,它或者使用一个作用语句或结构来终止
该DO结构的运行,或者与其他DO结构共享一个终止语句。
这两种形式具有相同的功能,都可以使用DOWHERE与“永远DO”的循环形式,但是非块DO结构属于早期FORTRAN版本的遗留物,是在还不重视结构化编程的时代的产物,因此是过时的表达方式,现代FORTRAN语言不提倡使用。
下面是一个DO结构块的例子。
【例11-2】
DOI=1,N
SUM=SUM+X(I)
ENDDO
下面是执行与上例同样的计算任务的非块DO结构的例子。
【例11-3】
DO100I=1,N
100SUM=SUM+X(I)
11.1.1 DO结构块的形式
DO结构块就是使用不与其他DO结构共享的ENDDO语句或CONTINUE语句作为终止语句的DO结构。
它的一般句法形式(R817)为:
[do-construct-name:
]DO[label][loop-control]
[execution-part-construct]…
[label]ENDDO
其中循环控制(loop-control)的句法形式(R821)为:
[,]scalar-integer-variable-name=&
scalar-integer-expression,scalar-integer-expression&
[,scalar-integer-expression]
[,]WHILE(scalar-logical-expression)
其中ENDDO语句的句法形式(R824)为:
ENDDO[do-construct-name]
CONTINUE
其中在可选的结构名称加关键词DO后面的语句称为DO语句;关键词ENDDO后面的语句称为ENDDO语句;关键词CONTINUE后面的语句称为CONTINUE语句。
DO结构的一般规则如下:
●DO结构里循环控制里的DO变量,必须是整型的标量命名变量。
●整个DO结构块如果有一个结构名称,那么它必须同时出现在DO语句和ENDDO语句里面,而不能只出现在其中之一里面。
●如果DO语句里面不包含标签,那么ENDDO语句里面也不能包含标签;如果DO语句里面包含标签,那么ENDDO语句必须使用同样的标签。
注意DO结构块里的终止语句绝对不能与其他DO结构共享,因此ENDDO语句的标签绝对不能在别的地方被引用。
如果终止语句其他DO结构共享了,则属于非块DO结构。
●DO结构里面可选的标签既可以使用自由源码形式也可以使用固定源码形式。
下面给出一些不同形式的DO结构的例子。
【例11-4】
SUM=0.0
DOI=1,N
SUM=SUM+(-1)**N*(1/N**2)
ENDDO
这个例子里面的DO变量是I。
【例11-5】
FOUND=.FALSE.
I=0
DOWHILE(.NOT.FOUND.AND.I IF(KEY==X(I))THEN
FOUND=.TRUE.
ELSE
I=I+1
ENDIF
ENDDO
这个例子使用了WHERE语句。
【例11-6】
NO_ITERS=0
DO
下面的FU和FU_PRIME都是函数
X1=X0-FU(X0)/FU_PRIME(X0)
IF(ABS(X1-X0)NO_ITERS>MAX_ITERS)EXIT
X0=X1
NO_ITERS=NO_ITERS+1
ENDDO
这个DO结构里面使用IF语句。
【例11-7】
INNER_PROD=0.0
DO100I=1,10
INNER_PROD=INNER_PROD+X(I)*Y(I)
100CONTINUE
这个例子里面使用了CONTINUE语句。
【例11-8】
LOOP:
DOI=1,N
Y(I)=A*X(I)+Y(I)
ENDDOLOOP
在这个例子里面给出了DO结构名称。
11.1.2 非块DO结构的形式
非块DO结构的终止语句总是使用标签,并且采取如下两种情形:
● 一个非块DO结构的终止语句是与其他的DO结构共享的;
● 非块DO结构的终止语句本身是一个结构或作用语句,而不是ENDDO语句或CONTINUE语句。
具有这样的终止语句的DO结构,就不能形成一个块结构,这导致它是一种过时的表述方式。
非块DO结构的句法形式(R826)为:
action-terminated-do-construct
outer-shared-do-construct
其中作用语句终止DO结构(action-terminated-do-construct)的句法形式(R827)为:
[do-construct-name:
]DOlabel[loop-control]
[execution-part-construct]…
labelaction-statement
其中可外部共享DO结构(outer-shared-do-construct)的句法形式(R830)为:
[do--construct-name:
]DOlabel[loop-control]
[execution-part-construct]…
labelshared-termination-do-construct
其中可共享终止DO结构(shared-termination-do-construct)的句法形式(R831)为:
outer-shared-do-construct
inner-shared-do-construct
其中可内部共享DO结构(inner-shared-do-construct)的句法形式(R832)为:
[do-construct-name:
]DOlabel[loop-control]
[execution-part-construct]…
labelaction-statement
其中用来退出作用语句终止DO结构(action-terminated-do-construct)的作用语句称为终止DO作用语句;用来退出可内部共享DO结构(inner-shared-do-construct)的作用语句称为终止DO共享语句;所有这些用来终止非块DO结构的语句或结构形式,包括终止DO作用语句,终止DO共享语句,终止DO共享结构,都称为DO终止,或DO结构的终止语句。
非块DO结构的一般规则如下:
●一个终止DO作用语句不能是任何形式的如下语句:
CONTINUE语句;GOTO语句;RETURN语句;STOP语句;EXIT语句;CYCLE语句;END语句。
●终止DO作用语句和相应的DO语句必须具有相同的标签。
●一个终止DO共享语句不能是任何形式的如下语句:
GOTO语句;RETURN语句;STOP语句;EXIT语句;CYCLE语句;END语句。
●终止DO共享语句和相应的所有共享它的DO语句必须具有相同的标签。
●DO结构里面可选的标签既可以使用自由源码形式也可以使用固定源码形式。
下面给出一些不同形式的非块DO结构的例子:
【例11-9】
PROD=1.0
DO20I=1,N
20 PROD=PROD*P(I)
【例11-10】
DO20I=1,N
DO20J=1,N
20 HILBERT(I,J)=1.0/REAL(I+J)
这个例子里面包含DO语句的嵌套结构。
【例11-11】
FOUND=.FALSE.
I=0
DO20WHILE(.NOT.FOUND.AND.I I=I+1
20 FOUND=KEY==X(I)
这个例子里面使用了WHERE语句。
【例11-12】:
DO20I=1,N
DO20J=I+1,N
A=B(I,J);B(I,J)=B(J,I);B(J,I)=A
20 CONTINUE
这个例子里面使用了CONTINUE语句。
11.1.3 DO结构的范围
所谓DO结构的范围是指从DO语句到该DO结构的终止语句的所有语句。
在DO结构的范围内部可以包含其他类型的结构,例如IF结构,CASE结构,以及其他DO结构,其中任何一个内部结构都必须是完整地包含在另一个结构之中,而不能有各个结构相互交错的情形。
如果在一个DO结构里面包含了其他DO结构,那么该DO结构称为嵌套DO结构。
嵌套的DO结构共享一个终止语句是允许的,但是属于即将淘汰的语法形式,因此尽量不要使用。
从一个DO结构的外部分支到它的内部某个语句是绝对禁止的。
11.1.4 DO结构的激活与灭活
一个DO结构可以处于激活状态,也可以处于灭活状态。
当一个DO结构被运行时,就是处于激活状态。
当一个DO结构处于如下几种情形时,就是处于灭活状态:
● 在检测条件时循环计数器取值为0;
● 在检测条件时WHILE条件为假;
● 执行了EXIT语句,导致从DO结构退出;
● 执行了CYCLE语句;
● 出现一个指向DO结构外部的控制转移;
● 执行了RETURN语句;
● 任何其他原因导致程序终止。
11.1.5 DO结构的运行
由于DO结构具有非常不一样的三种形式,因此DO结构的运行也具有三种形式:
● 带循环计数器的DO结构的运行;
● DOWHILE结构的运行;
● 简单DO结构的运行。
所有这些运行形式都可以包含能够导致程序不按照语句顺序执行的可执行语句,当然也可以包含导致DO结构运行终止的语句。
下面分别予以说明。
1.带循环计数器的DO结构的运行
在这种DO结构的形式里面,循环计数器控制了循环部分执行的次数。
带循环计数器的DO结构的句法形式(R818)为:
DO[label][,]do-variable=expression1,expression2[,expression3]
其中的DO变量与相应的表达式都必须是整型。
下面是这样的不同形式的DO语句的一个例子。
【例11-13】
DO100I=0,N
DO,I=-N,N
DOJ=N,1,-1
所谓循环计数器是用来计量DO结构里面的循环部分的循环运行次数的。
循环计算器里面包括三个表达式,这些表达式经过计算后得到的结果必须转换为DO变量的类型,假设分别为e1,e2,e3,(其中e3不能是0,但可以省略,而取默认值1。
)它们的含义为:
● e1表示DO变量的初始值;
● e2表示DO变量的终止值;
● e3作为可选项表示DO变量的变化的步长,默认值取1。
那么循环的次数可以由下面的公式得到:
MAX((e2-e1+e3)/e3,0)
注意在下面的情形下导致循环计算器的取值为0,也就导致DO结构灭活:
● e1>e2并且e3>0;
● e1然后通过循环计算器的控制,DO结构的运行步骤如下:
1.DO变量的初始值设置为e1;
2.检测循环计数器,如果取值为0,则退出DO结构;
3.如果循环计数器的取值不是0,则DO结构的范围得到执行。
这时循环计数器的值减1,而DO变量增长e3。
4.重复步骤2和3,一直到循环计数器的值为0。
注意在整个DO结构终止后,DO变量的取值保持为相应的循环计数器值为0时的值。
在DO结构的范围的整个运行过程中,DO变量不能被重定义,也不能被去定义。
在DO结构的运行过程当中,决定循环参数的表达式里面的变量的变化不影响循环计数器,每次进入DO结构,循环计数器的值都是固定的。
下面的示意图11-1表示了带有循环计数器的DO结构的运行流:
图11-1 带有循环计数器的DO结构的运行流
【例11-14】下面的例子运行了10次
N=10
SUM=0.0
DO10I=1,N
SUM=SUM+A(I)
N=N+1
10 CONTINUE
运行之后,I=11,N=20
【例11-15】下面的内部循环运行了10次:
X=20
DO10I=1,2
DO9J=1,5
X=X+1.0
9 CONTINUE
10CONTINUE
运行整个DO结构之后,I=3,J=6,X=30。
如果其中的第二个DO语句改为:
DO9J=5,1
那么其中的内部DO结构根本就不会运行,而X取值仍然是20,J=5,I=3。
2.DOWHILE结构的运行
DOWHILE结构的循环控制不是通过一个整型变量来给出循环的次数,而是通过对一个取逻辑型值的表达式的运算,来获得进行循环的条件。
关键词DOWHILE后接逻辑型表达式的语句称为DOWHILE语句。
DOWHILE语句的句法形式为:
DO[label][,]WHILE[scalar-logical-expression]
DOWHILE语句的例子:
DO100WHILE(X(I)/=0)
DO,WHILE(.NOT.FOUND)
DOWHILE(y>=0.01)
DOWHILE结构的运行非常简单:
首先计算逻辑表达式,如果为真,则运行循环部分;如果为假,则退出该DO结构。
【例11-16】
SUM=0.0
I=0
DOWHILE(I<5)
I=I+1
SUM=SUM+I
ENDDO
上面的循环执行5次,最后得到SUM=15.0,I=5。
3.简单DO结构的运行
也可以在DO结构里面不使用任何循环控制,而是直接使用终止语句,即DO范围里面的语句总是循环执行,除非DO范围里面的某条终止语句在一定条件下被执行。
简单DO结构的一般形式就是:
DO[label]
【例11-17】
DO
READ*,DATA
IF(DATA<0)STOP
CALLPROCESS(DATA)
ENDDO
在这个例子里面,一直到DATA读入一个负值,其中的DO范围才停止循环,并退出DO结构。
把上面的例子改写为带有标签的形式,如下:
DO15
READ*,DATA
IF(DATA<0)STOP
CALLPROCESS(DATA)
15CONTINUE
11.1.6 在DO结构范围内改变运行顺序
在DO结构范围内部,可以使用两个专门的语句来改变正常的运行顺序,它们是:
● EXIT语句;
● CYCLE语句。
当然可以用来在DO范围内改变正常运行顺序的语句,并不只是这两个语句,还包括一些分支语句,例如RETURN语句,STOP语句等,但是这两个语句是专门用于DO结构内部的。
而那些分支语句则没有这个限制。
下面分别说明这两个语句。
1.EXIT语句
EXIT语句直截了当地导致程序退出DO结构,DO结构里面的任何其他语句都不再被执行。
EXIT语句既可以在DO结构块里面使用,也可以在非块DO结构里面使用,但是它本身不能充当非块DO结构里面的终止DO作用语句和终止DO共享语句。
EXIT结构的句法形式(R835)为:
EXIT[do-construct-name]
注意EXIT语句必须是在一个DO结构内部。
如果EXIT语句具有一个结构名称,那么该EXIT语句必须是在同名DO结构的内部;当该EXIT语句被执行的时候,该同名DO结构就被终止,同时,任何包含了该EXIT语句的DO结构以及包含在该同名DO结构里面的DO结构全都被终止。
如果EXIT语句不带结构名称,那么只有所有包含EXIT语句的结构之中最接近EXIT语句的DO结构被终止。
【例11-18】
LOOP10:
DO
…
IF(TEMP==INDEX)EXITLOOP10
…
ENDDOLOOP10
上例当中的EXIT语句带有结构名称,那么当IF语句里面的条件被满足时,就运行EXIT语句,也就退出整个LOOP10结构。
2.CYCLE语句
上面的EXIT语句是完全地退出整个DO结构,而CYCLE语句则只是中断已经完成的循环,使用可能更新过的循环计数器以及DO变量,再次启动循环部分的循环计算过程。
CYCLE语句既可以在DO结构块里面使用,也可以在非块DO结构里面使用,但是它本身不能充当非块DO结构里面的终止DO作用语句和终止DO共享语句。
如果CYCLE语句在非块DO结构里面使用,那么非块DO结构里面的终止DO作用语句和终止DO共享语句将不会得到执行。
CYCLE语句的句法形式(R834)为:
CYCLE[do-construct-name]
注意CYCLE语句必须是在一个DO结构内部。
如果CYCLE语句具有一个结构名称,那么该CYCLE语句必须是在同名DO结构的内部;当该CYCLE语句被执行的时候,该同名DO结构就被中断,同时,任何包含了该CYCLE语句的DO结构以及包含在该同名DO结构里面的DO结构全都被终止。
如果CYCLE语句不带结构名称,那么只有所有包含CYCLE语句的结构之中最接近CYCLE语句的DO结构被中断。
CYCLE语句可以用于任意形式的DO语句;在循环控制条件许可的情形下,就可以启动下一个循环的开始。
当DO结构被CYCLE语句中断之后,如果DO结构存在DO变量的话,该DO变量就被更新,同时循环计数器被减小1,然后就开始了DO范围的下一个循环的执行。
【例11-19】
DO
…
INDEX=…
…
IF(INDEX<0)EXIT
IF(INDEX=0)CYCLE
…
ENDDO
在上面的例子当中,只要INDEX为正数,循环就一直进行;一旦INDEX为负值,则整个DO结构被终止;而如果INDEX为0时,则循环部分在CYCLE后面的所有语句都被跳过去,重新开始循环过程。
11.2 程序的分支
所谓分支就是使得程序的运行从当前的语句转移到该程序单位里的另外一个语句,也就意味着紧接当前语句的下面的语句得不到执行了。
导致转移的当前语句称为分支语句,而