第八章子程序

上传人:仙*** 文档编号:161674416 上传时间:2022-10-14 格式:PPT 页数:121 大小:238.51KB
收藏 版权申诉 举报 下载
第八章子程序_第1页
第1页 / 共121页
第八章子程序_第2页
第2页 / 共121页
第八章子程序_第3页
第3页 / 共121页
资源描述:

《第八章子程序》由会员分享,可在线阅读,更多相关《第八章子程序(121页珍藏版)》请在装配图网上搜索。

1、第八章第八章 子程序子程序8.1 语句函数语句函数8.2 函数子程序函数子程序8.3 子例行程序子例行程序8.4 程序单元之间的数据传递程序单元之间的数据传递8.5 递归调用递归调用8.6 数据共用存储单元与数据块子程序数据共用存储单元与数据块子程序8.7 内部子程序内部子程序8.8 模块模块8.9 程序举例程序举例 子程序是构造大型程序的有效工具,一个实用程序(不管是系统程序还是应用程序),一般都含有多个子程序。FORTRAN 90 中的子程序按子程序完成的功能划分有子例行程序、函数子程序、数据块子程序等,按是否定义在某个特定的程序单元内部来划分有程序单元子程序、模块子程序以及内部(INTE

2、RNAL)子程序等,这些通称为子程序,子程序不能独立运行,它们和一种称为主程序(就是本章前读者已经熟知的程序结构)的程序单元一起组成一个实用程序。一个程序可以不含子程序,但不能缺少主程序。本章讨论各种子程序的结构、功能以及子程序与主程序或子程序之间的数据交互作用。语句函数不具备子程序的一般书写特征,但其作用与子程序相同,也一并放在本章讨论。通过本章的学习,读者应能熟练地选择并设计恰当的子程序形式来构造自己的程序,从而提高程序设计能力。8.1 语句函数语句函数在第二章中,读者使用过了诸如SIN(X)、SQRT(X)这些内在(INTRINSIC)函数。也有教科书称这一类函数为内部(INTERNAL

3、)函数,但为了区别另外一种内部子程序结构,本章称由FORTRAN编译器提供的函数为内在函数。这些内在函数是在程序设计过程中使用频率很高,并且一般实现比较复杂的函数。用户在使用内在函数时,并不需要对函数的实现过程进行描述,只需按照FORTRAN 90手册要求使用即可,FORTRAN 90编译器“认识”这些内在函数并能正确完成函数所规定的功能。但是,在实际设计过程中,用户还会遇到大量的并未包含在内在函数中的其他函数,计算这些函数时,就不能象内在函数那样仅仅使用函数名来使用这些函数,而必须在适当的地方以FORTRAN 90能“理解”的形式向FORTRAN 90 编译器说明这些非内在的函数的计算过程与

4、参数类型,当函数的计算过程简单到可以用一个语句定义清楚时,这样的定义语句就称为语句函数。例 8.1 设多项式函数设计一个程序,计算f(1)、f(10)、f(12)、f(-5)的值。程序如PROGRAM EXAM1A:PROGRAM EXAM1A X=1 FX=5*X*3-2*X*2+7*X+6 WRITE(*,*)f(,X,)=,FX X=10 FX=5*X*3-2*X*2+7*X+6 WRITE(*,*)f(,X,)=,FX X=12 FX=5*X*3-2*X*2+7*X+6 WRITE(*,*)f(,X,)=,FX X=-1 FX=5*X*3-2*X*2+7*X+6 WRITE(*,*)f

5、(,X,)=,FXEND显然,程序EXAM1A不简练,把一个相同的函数表达式重复了多遍,如果能定义一个函数f(x),然后分别使用1、10等参数来调用f(x),将会有效的简化程序量,这就是本节要讨论的内容。6725)(23xxxxf811 语句函数的定义语句函数的定义如前所述,由于FORTRAN 90 的编译器不“认识”一般的函数f(x),不知道怎样计算f(x)的函数值,因而,在使用(称为函数调用)函数时,必须向FORTRAN 90编译器说明该函数的计算方法,这种说明过程称为函数定义。语句函数定义的一般格式是:exxxfn).,(211.语句函数名在语句函数定义格式中,f称为函数名,函数名的组成

6、规则与变量名相同。如果没有在语句函数定义语句前用类型语句说明该函数名的数据类型,则该语句函数的函数值的类型按其函数名遵守IN隐含规则。如f函数隐含为实型函数,而nf则隐含为整形函数。F(X,Y)=X*2+Y*2 !定义了一个实型函数FINTEGER F(X,Y)=X*2+Y*2 !定义了一个整型函数F NF(X,Y)=X*2+Y*2 !也同样定义了一个整型函数F 语句函数不能与同一个程序单元中的变量同名。2.语句函数的虚参在语句函数定义语句中的函数参数称为虚参,他们本身是没有值的,只有在函数调用时才用实际参数(称为实参)代替。实参或是常数、或是一个有确定值的变量、或是一个可以计算值的表达式。虚

7、参在形式上与普通变量相同,一个语句函数中的虚参不能同名。不同语句函数中的虚参可以同名,虚参也可以和程序中的变量同名。当没有语句对虚参的数据类型进行说明时,虚参的类型遵守IN隐含规则;当使用了类型说明语句对虚参类型进行说明后,这种说明对于虚参以及与虚参同名的变量同时有效。INTEGER ZF(X)=3*X*2+5G(Y)=3*Y*2+5H(Z)=3*Z*2+5在上述程序中,函数F和G本质上是一个函数,因为对于任意的实参T,F(T)和G(T)总是相同的,但函数F、G和H有点不同,其虚参Z被说明为整型。当虚参个数多于一个时,虚参间用逗号分隔,当没有虚参时(这样的语句函数没有使用价值),函数名后的括弧

8、也不能省略。3.语句函数表达式 语句函数表达式说明函数与参数的对应关系,在函数表达式中,可以包含常量、变量、函数虚参(虚参必须包含在表达式中)、FORTRAN 90内在函数、数组以及前面已经说明了的语句函数。4.关于语句函数的进一步说明 (1)只有当函数关系简单,可以用一个语句描述函数与参数的对应关系时,才能使用语句函数。(2)语句函数是非执行语句,语句函数的定义语句要放在一个程序单位的所有其他说明语句之,并放在所有可执行语句之前。(3)语句函数只有在本程序单元中才有意义。也就是说,不能使用其他程序单元中的语句函数。(4)语句函数中的虚参必须是变量,不能是常量、数组元素和内在函数等。(5)语句

9、函数是有类型的,因而语句函数表达式的类型一定要和其函数名的类型相容 812 语句函数的调用语句函数的调用语句函数一经定义,就可以在同一个程序单元中调用它,调用的形式和内在函数完全相同。函数调用要注意以下两个问题:1 调用时可以使用常量、变量、内在函数以及它们的表达式作为实参代替对应的虚参位置,但要保证实参与虚参具有完全相同的类型,并且实参是可以计算值的(即调用前实参中包含的变量全部已经赋值。)2 实参和虚参个数相同。例 8.2 用函数语句的方法设计例8.1PROGRAM EXAM1B INTEGER X F(X)=5*X*3-2*X*2+7*X+6 WRITE(*,*)F(2),F(10),F

10、(12),F(-2)END8.2 函数子程序函数子程序语句函数由于要求在一个语句中完成函数的定义,因而它只能解决一些较简单的问题,当函数关系比较复杂,用一个语句无法定义时,语句函数就无能为力了,这时需要用到函数子程序。821 函数子程序的定义函数子程序的定义函数子程序是以保留字 FUNCTION 开头,并以保留字 END结束的一个程序段,该程序段可以独立存储为一个文件,也可以和调用它的程序单元合并存储为一个程序文件。函数子程序的定义格式是:类型说明 FUNCTION 函数名(虚参表)函数体END函数名的命名方法与变量名相同,虚参可以是简单变量和数组变量,但不能是常数、数组元素、表达式)例 8.

11、3 编程,求:50201314010032100121iiiisisis分析:上述三个数列的通项不同,求和范围也不同,用一个程序段难以同时计算三个数列的和,并且,因为无法用一个语句函数完成数列的求和计算,所以也无法使用语句函数来简化程序设计。下面用函数子程序来完成这个问题。程序如下:FUNCTION SM(M,N,L)SM=0 DO I=M,N IF(L0)THEN SM=SM+I*L ELSE SM=SM+(1.0*I)*L !进行实型运算 END IF ENDDO END FUNCTION SMPROGRAM EXAM2 !开始主程序单元定义WRITE(*,*)S1=,SM(1,100,2

12、)!调用函数子程序SM完成S1的计算WRITE(*,*)S2=,SM(100,140,3)WRITE(*,*)S3=,SM(20,50,-1)END程序运行结果如下:S1=338350.000000 S2=7.291440E+07 S3=9.514656E-01对程序EXAM2作如下说明:程序的第一部分是函数子程序的定义部分,如前所述,它可以单独存储为一个程序文件。从保留字PROGRAM开始至END是主程序部分,一个程序总是从主程序开始运行的,主程序的第24个语句都要打印函数SM的值,这时调用函数定义部分,根据函数的定义描述计算函数的值并予以打印。三个打印语句分别调用了三次函数子程序(即执行了

13、三次函数子程序),从这里我们可以看到:采用函数子程序设计程序时并没有提高程序的执行效率,但却可以有效的提高程序的设计效率。当函数子程序的定义过程和主程序放到一个程序文件中时,存储顺序是任意的,函数子程序可以放到主程序之前(如EXAM2),也可以放到主程序之后,读者应该清楚,程序总是从主程序开始执行。函数定义部分应注意如下问题:1函数值的类型可以在函数定义时予以说明,下面的两种说明方法是等效的:(1)INTEGER FUNCTION F1(X1,X2.XN)函数体END(2)FUNCTION F1(X1,X2.XN)INTEGER F1函数体END都说明F1是一个整型函数,当未使用这种显示的类型

14、说明时(如例8.3),函数值的类型遵守IN隐含规则。2 函数不能有同名虚参。虚参的类型可以在函数体中进行说明方法,当未对虚参类型进行说明时,虚参类型遵守IN隐含规则。3 函数定义部分中一定要有一个语句将函数值赋值给函数名(如例8.3中的第5句和第7句)。这种赋值语句的格式是:函数名=表达式注意不要在函数名后带上括弧,写成:函数名(虚参表)=表达式822 函数子程序的调用函数子程序的调用定义函数子程序的目的是为了调用。不仅主程序可以调用一个函数子程序,函数子程序也可以调用其它的函数子程序,甚至于还可以调用本身(递归调用)。调用程序称为主调程序单元,而被调用的函数子程序称为被调程序单元。调用一个函

15、数子程序的方法和调用内在函数和语句函数的方法基本相同:1 调用时应该用实参代替函数子程序定义部分的虚参,实参和虚参的类型要相同。和语句函数一样,实参可以是常量、变量、表达式等。2 调用程序单位中的变量不能与函数子程序同名。函数值的类型由函数定义程序单元决定,与调用程序单元无关。3 当函数名的类型不满足IN隐含规则时,在调用程序单元中要对函数名的类型给出说明(如例 8.4 中的主程序EXAM3的第2句。)4 不能调用一个没有定义的函数子程序(这一点和内在函数是不同的。)例 8.4 用函数子程序的方法设计一个程序,求50100内的所有素数及其和。分析:设计一个函数子程序 PRIME(N),函数PR

16、IME的值定义如下:非素数时当是素数时当nnnprime01)(主程序的任务是应用PRIME函数子程序在50100之间使用枚举法求出那些使PRIME函数值为1的自然数并求这些数的和。函数子程序如下:FUNCTION PRIME(N)INTEGER PRIME *定义PRIME是整型函数PRIME=0DO I=2,N-1 IF(MOD(N,I)=0)GOTO 10 *参数N有因子I,非素数,退出循环ENDDOPRIME=1 *参数N无任何因子,函数值为110 END 主程序如下:PROGRAM EXAM3INTEGER PRIME *说明要调用的函数PRIME 为整型DO I=50,100 IF

17、(PRIME(I)=1)THEN S=S+IWRITE(*,*)I END IFEND DOWRITE(*,*)S=,SEND运行结果如下:.83 89 97 S=732.000000例 8.5 当一个数各个数位的立方和等于这个数本身时,称这样的数为水仙花数(如153=1*3+5*3+3*3,所以,153是一个水仙花数,)编程:求100999之间的水仙花数。分析:设计一个函数子程序NUM(N,I),当I=100时,函数NUM返回N的百位上的数;当I=10时,NUM返回N的十位上的数,I=1时,NUM返回N的个位上的数。主程序中调用NUM函数在100999之间找出所有水仙花数。程序如下:PROG

18、RAM FLOWER DO I=100,999 IF(NUM(I,100)*3+NUM(I,10)*3+NUM(I,1)*3=I)THEN WRITE(*,*)I ENDIF ENDDOENDFUNCTION NUM(N,I)SELECT CASE(I)CASE(100)NUM=N/100 !用N百位上的数赋值给函数名CASE(10)NUM=MOD(N/10,10)!用N十位上的数赋值给函数名CASE(1)NUM=MOD(N,10)!用N个位上的数赋值给函数名 END SELECTEND程序运行结果如下:153 370 371 407除了函数子程序外,还有一种子例行子程序,函数子程序和子例行子

19、程序都是一种独立的程序单元,二者的差别是:函数子程序的名字代表一个值,因而是有类型的,而子例行程序的名字不代表一个值,因而其名字没有类型问题。从使用上来说,二者是可以相互替代的。831 子例行子程序的定义子例行子程序的定义子例行子程序是由保留字SUBROUTINE开头,到保留字END结束的一个程序段。其定义格式是:SUBROUTINE 子程序名(虚参表)子例行程序体END子程序的命名方法与变量相同。虚参由变量、数组名(不能是数组元素,常数、表达式)充当,当虚参多于一个时,各虚参间用逗号分隔,当没有虚参时,子例行程序名后的一对括弧可以省略。子例行程序的设计方法和函数子程序相同,但因为其名字没有值

20、,所以不能有对子例行程序的名字赋值的语句。83 子例行程序子例行程序832 子例行子程序的调用子例行子程序的调用子例行程序的调用格式是:CALL 子例行程序名(实参表)其实参的类型与函数子程序相同。和函数子程序的调用不同的是,子例行子程序的调用是一个独立的语句。子例行程序调用的其它事项与函数子程序的调用相同。下面通过两个实例来讨论函数子程序与子例行程序的相同点与不同点。例 8.6 用子例行程序的方法完成例8.3分析:例8.3中定义了一个函数SM(M,N,L),我们将该函数子程序修改为一个子例行程序 SM(S,M,N,L),虚参M,N,L的含义与例8.3相同,虚参S用来存储结果和(用来替代函数名

21、SM)。程序如下:SUBROUTINE SM(S,M,N,L)DO I=M,N IF(L0)THEN S=S+I*LELSE S=S+(1.0*I)*LEND IF ENDDOENDPROGRAM EXAM5 CALL SM(S1,1,100,2)CALL SM(S2,100,140,3)CALL SM(S3,20,50,-1)WRITE(*,*)S1=,S1 WRITE(*,*)S2=,S2 WRITE(*,*)S3=,S3END运行结果如下:S1=338350.000000 S2=7.291440E+07 S3=9.514656E-01例 8.7 用随机方法生成一个包含20个元素的数组,对

22、该数组按升序排序打印。数据排序的问题读者在第七章已经很熟悉了,下面直接给出程序:PROGRAM EXAM5PARAMETER(N=20)DIMENSION A(N)INTEGER A A(1)=17 该语句及随后的三个语句生成数组A DO I=2,20 A(I)=MOD(19*A(I-1),1024)ENDDO DO I=1,N-1 二重循环完成对A的排序 DO J=I+1,N CALL SWAP(A(I),A(J)ENDDO ENDDO WRITE(100,200)(A(I),I=1,N)200 FORMAT(2(2X,10(I5,X)/)END子例行程序SWAP(M,N)的功能是,若MN,

23、就交换M、N的值,程序段如下:SUBROUTINE SWAP(X,Y)INTEGER X,Y,T IF(XY)THEN T=X X=Y Y=T ENDIFEND程序执行结果是:3 17 57 59 115 137 139 169 305 321 323 537 545 555 593 675 891 979 987 1017从上面两个例题可以看出:1 子例行程序和调用它的主程序的存放顺序是无关紧要的,既可以将子程序存放在前,也可以将主程序存放在前,还可以作两个程序文件分别存放。2 子例行程序和函数子程序在使用上可以相互替代。但是,当要求一段子程序有返回值时,以选择函数子程序比较方便(如例8.6

24、),当子程序没有返回值时(如例8.7),则选择子例行程序较为方便例 8.8 设计一个自例行程序程序,求任意矩阵的转置矩阵。设计一个子例行程序TRAN(A,B,M,N)完成将矩阵A转置后放矩阵B,M、N分别是矩阵A、B的行数和列数。主程序如下:PROGRAM EXAM6 PARAMETER(M=3)PARAMETER(N=4)DIMENSION A(M,N),B(N,M)INTEGER A,B WRITE(*,*)please input a 3x4 Matra READ(*,200)(A(I,J),J=1,N),I=1,M)CALL TRAN(A,B,M,N)WRITE(*,300)(B(I,

25、J),J=1,M),I=1,N)200 FORMAT(4I4)300 FORMAT(3I5)END子例行程序如下SUBROUTINE TRAN(A,B,M,N)INTEGER A(M,N),B(N,M),M,N DO I=1,M DO J=1,N B(J,I)=A(I,J)ENDDO ENDDOEND运行情况:please input a 3x4 Matra 1 2 3 4 5 6 7 8 9 10 11 12显示结果如下:1 5 9 2 6 10 3 7 11 4 8 12841 简单变量作为虚参简单变量作为虚参这是一种最常见的情况,例8.4,8.5都是这种情况,在这种情况下,根据子程序调用

26、时实参的不同类型,又可以进一步分为两种情况:1简单变量或数组元素作为实参FUNCTION SUB(A,B)PROGRAM TTEM X=3 .Y=-5 S=S+SUB(X,Y).END END 不同程序单元之间的数据传送方法有参数的虚实结合、建立公用区及通过数据文件等三种方式实现。本节先讨论参数的虚实结合方法,8.7节讨论公用区的概念与用法,第十一章讨论文件方法。图8.1 A、X,B、Y共用存储单元8.4 程序单元之间的数据传递程序单元之间的数据传递此种情况下FORTRAN 90系统将实参与虚参安排同一个存储单元,对虚参的任何改变都作用在对应的实参上,因而调用一个子程序(包括函数子程序和子例行

27、程序)时,实参的值有可能改变。如图8.1所示:2常量或表达式作为实参当用常量或表达式作为实参时,FORTRAN 90编译器首先计算表达式的值(如果实参为表达式时。)然后将该值赋值给对应的虚参。此种情况下,子程序中不能改变与常量(或表达式)对应的虚参的值,否则结果难以预料。842 数组名作为虚参数组名作为虚参当虚参是数组名时,对应的实参可以是与虚参类型相同的数组名或数组元素,并且实参与虚参共用一片连续的存储单元。1虚参为数值型或逻辑型数据(此时的实参当然也是对应类型)(1)实参为数组名时,FORTRAN 90 编译器将实参数组的第一个元素的存储地址传送给子程序,并将其作为对应虚参数组的第一个元素

28、的存储地址,从而使两者共用一片存储单元。(2)当实参是一个数组元素时,FORTRAN 90 编译器将该元素的存储地址传送给子程序,并将其作为对应虚参数组的第一个元素的存储地址,从而使虚参数组与实参自该元素以后的元素共用一片存储单元。在实参与虚参数组之间传送数据时,不要求两者的行列数相同,甚至于实参元素的个数可以多于虚参(这种情况下,多余的实参不参与子程序中的运算。)读者应该牢记:FORTRAN 90是按列为主存放多维数组的,实参和虚参间的元素按存储顺序对应。如:PROGRAM TTST FUNCTION SUB2(X)DIMENSION A(3,4)DIMENSION X(2,6)DO I=1

29、,3 S=0 DO J=1,4 DO I=1,2 A(I,J)=I+J DO J=1,6 ENDDO IF(MOD(I+J,3)=0)THEN ENDDO S=S+X(I,J)WRITE(*,*)SUB2(A)ENDIF END ENDDO在程序TTST的倒数第二句调用了函数子程序SUB2,虚参X是一个2x6的矩阵,而实参A是一个3x4的矩阵,两者的元素对应关系是:A(1,1)X(1,1),A(2,1)X(2,1),A(3,1)X(1,2)A(1,2)X(2,2),A(2,2)X(1,3),A(3,2)X(2,3)A(1,3)X(1,4),A(2,3)X(2,4),A(3,3)X(1,5)A(

30、1,4)X(2,5),A(2,4)X(1,6),A(3,4)X(2,6)在函数子程序SUB2中,满足求和条件的元素分别是:X(2,1),X(1,2),X(2,4),X(1,5)这些元素对应的实参元素是:A(2,1),A(3,1),A(2,3),A(3,3)故所求的函数值为:3+4+5+6=182虚参为字符型数据当虚参为字符型数据时(当然对应的实参也为字符型数据),实参和虚参不是按照数组元素的顺序对应,而是按照字符位置一一对应,读者只要了解FORTRAN 90中字符型数据的存储规则,并将实参与虚参的存储顺序派出来,就不难确定实参元素与虚参元素的对应关系,这里不再详细说明。3可调数组请读者重新考察

31、例 8.8中的子例行程序TRAN(A,B,M,N),在子程序的说明中将A、B说明为A、B分别是一个M*N的矩阵,注意在子程序中并没有具体规定M、N的值,这样的数组称为可调数组,可调数组的引入提高了子程序的适应性,大大提高了编程效率。应注意可调数组只能作为虚参使用,不能在主程序中使用可调数组,也不能在子程序的其它地方使用可调数组。843 子程序名作为虚参子程序名作为虚参子程序的虚参不仅可以是前面所述的各种数据类型,还可以是一个子程序名。例 8.9 设有三个连续函数:103100220123)()()(11)(1025)(cos3sin)(dxxhIdxxgIdxxfISimpsonxxhxxxg

32、xxxf方法求三个函数的积分用设计一个函数子程序,分析:用Simpson方法求函数的积分时要计算被积函数的函数值,因为三个被积函数不同,求其函数值的方法也就不同,要设计一个统一的函数子程序,必须设计一个虚参函数,并用被积函数作为实参来调用该函数子程序。用Simpson方法求函数定积分的函数子程序如下:首先定义三个被积函数F(X)、G(X)、H(X)的函数子程序:FUNCTION F(X)!被积函数F(X)F=SIN(3*X)+COS(X)END FUNCTION G(X)!被积函数G(X)G=5*X*3+2*X-10END FUNCTION H(X)!被积函数H(X)H=1/(1+X*2)EN

33、D 再建立 SIMPSON 求积分函数子程序FUNCTION SIMPSON(F,A,B)H=(B-A)/2C=(A+B)/2SIMPSON=H*(F(A)+4*F(C)+F(B)/3END PROGRAM SIMPSON_PRO EXTERNAL F,G,H !三个被积函数都非FORTRAN 90 内在函数REAL I1,I2,I3 !故定义三个函数名F、G、H为!计算F(X)的积分 !外部函数(EXTERN 属性)I1=SIMPSON(F,0,2*3.1416)I2=SIMPSON(G,0,10.0)I3=SIMPSON(H,0,1.0)WRITE(*,*)I1=,I1WRITE(*,*)

34、I2=,I2WRITE(*,*)I3=,I3END程序的执行结果是:I1=-2.094445 I2=12500.000000 I3=7.833334E-01关于实参函数名的说明:1EXTERNAL 和 INTRINSIC调用虚参中有程序名的子程序(包括函数子程序和子例行程序)时,我们要在虚参的位置代之以一个实际存在的子程序名作为实参,实参子程序如果是FORTRAN 90 的内在函数,则在调用程序段中要用保留字 INTRINSIC 对该程序名作出说明,如果实参子程序是自己设计(或调用他人的程序库)的,则必须用保留字EXTERNAL对实参程序名作出说明(如例8.9),这种对函数名属性的说明就和对变

35、量的类型进行说明一样,说明语句必须放在该程序段的所有可执行语句前。必须强调:只需对实参的属性进行说明,至于虚参,因为其只是一个并不存在的形式子程序,并不具有INTRINSIC属性或EXTERNAL属性,是无法也无需说明其属性的。2内在函数的专用名和通用名FORTRAN 90 的内在函数有通用名和专用名之分(部分函数的通用名和专用名一致),用FORTRAN 90 的内在函数作为实参时,只能使用这些函数的专用名而不能使用其通用名。844 星号星号(*)作为虚参作为虚参星号(*)也可以作为虚参,与星号(*)虚参对应的实参是一个冠有星号(*)的语句标号。如:PROGRAM TEST SUBROUTIN

36、E F(S,*,*).IF(条件1)THEN .RETURN 1 !return 与1之间有空格REACLL F(A,*100,*200)ELSE*100.GOTO 300 RETURN 2*200 .ENDIF .300 END END例 8.10 设计一个子例行程序,当参数C为加号(+)时,计算并打印A+B的值,C为减号(-)时,计算并打印A-B的值。SUBROUTINE F(A,B,C,*,*,S)CHARACTER*1 C !定义参数C为单个字符变量SELECT CASE(C)CASE(+)S=A+B RETURN 1 !返回到第一个冠有*号的语句标号 CASE(-)S=A-B RET

37、URN 2 !返回到第二个冠有*号的语句标号END SELECTEND PROGRAM TESTCHARACTER*1 CWRITE(*,*)PLEASE INPUT 2 NUMBER AND A OPERATION SIGNREAD(*,*)A,B,CCALL F(A,B,C,*10,*20,S)10 WRITE(*,*)A,+,B,=,S !C参数为加号返回到此GOTO 3020 WRITE(*,*)A,-,B,=,S !C参数为减号返回到此30 END应该指出,象上面这样的分支问题,可在子程序单元F或调用程序单元TEST中用分支程序很容易地实现,这里采用的子程序中根据分支条件而返回到不同

38、的出口的方法,违背了一个程序单元应具有单一出口的结果化原则,是不应提倡的,读者在程序设计的实践中应尽量避免这种用法。845 变量的作用域变量的作用域变量是为了完成一个计算任务而设置的一些数据存储单元。FORTRAN 90为每一个变量在内存区域中建立一个物理的连续存储区,一旦某个变量完成其使命,FORTRAN 90将释放该变量所占据的物理存储区,该变量变得无定义。程序设计人员不能使用已经被FORTRAN 90释放的变量。到底FORTRAN 90何时为变量建立物理的存储区,又何时释放这些存储区呢?下面就来讨论这些问题。1变量存储区的分配与释放一个程序(是一个完整的程序而非组成程序的各程序单元)在投

39、入运行时,系统为这个程序的全部变量一次性分配存储单元,当这个程序退出运行时,系统收回这个程序所占据的全部存储单元。一个程序中的全部变量被同时建立存储单元。一般来说,一个程序的变量单元也将被同时释放,除非我们声明保留某个变量的存储单元。2变量作用域一个变量通常只在本程序单元(这里是程序单元而非整个程序)中起作用,离开建立该变量的程序单元,变量就失去了定义,这种作用域的局限性,使用户在设计程序时,只需考虑在本程序单元中的变量只否有相互干扰,而无需考虑与其它程序单元之间的变量干扰问题,简化了程序的调试工作。在程序调用时,调用程序单元的实参是通过与被调用程序单元的虚参的结合来实现的,并不是调用程序单元

40、中的变量直接在被调用程序单元中有定义。为了帮助读者更清楚的了解这一点,下面举一个说明变量作用域的例子,请读者仔细分析程序、程序的说明以及程序结果,以弄清变量的作用域这一个重要问题。例 8.11 讨论变量的作用域SUBROUTINE F(A,B,C)INTEGER X,Y X=5 !子程序S中有X、Y Y=8 C=A+B WRITE(*,*)X、Y IN SUB,X,Y END PROGRAM EXAM8 U=5 V=8 X=3 !主程序中也有X、Y Y=2 CALL F(U,V,S)WRITE(*,*)X、Y IN MAIN,X,Y WRITE(*,*)U,V,S END程序的执行结果是:X、

41、Y IN SUB 5 8 X、Y IN MAIN 3.000000 2.000000 5.000000 8.000000 13.000000 从程序执行结果可以看到,尽管在C程序单元 EXAM8 和程序单元 VERY中都有变量X、Y,但其值是不相同的,说明它们是不相干的变量,只是同名而已。3子程序中变量的存储属性变量有数据类型,同时还有存储属性,数据类型决定变量的运算特征,而存储属性决定一个变量所占用的存储单元在什么时候被释放,FPS中,变量的几种常见的存储属性有:(1)SAVE 属性:当声明变量的子程序执行完毕后,具有SAVE属性的变量的存储状态依然保留,在下一次调用该子程序时,这些变量保持

42、上一次调用返回时的值。(2)STATIC属性:具有该类属性的变量在整个程序(不是声明该变量的子程序)执行期中一直保留在存储单元中,这是变量的默认存储属性(3)AUTOMATIC属性:当声明变量的子程序执行完毕后,具有AUTOMATIC属性的变量的存储单元被系统回收。变量的存储属性的说明方法是:存储属性 变量名表这里的存储属性是上述三种属性之一。如:AUTOMATIC A,B,C !定义实型变量A、B、C具有AUTOMATIC 存储属性INTEGER SAVE X,Y !定义变量X、Y为整型,SAVE 存储属性例 8.12 说明变量存储属性的作用。PROGRAM DATAS_TESTDO I=1

43、,10 WRITE(*,*)I,F()ENDDOEND FUNCTION F()STATIC N,S !定义N、S为STATIC属性N=N+1S=S+NF=SEND程序的执行结果是:1 1.000000 2 3.000000 3 6.000000 4 10.000000 5 15.000000 6 21.000000 7 28.000000 8 36.000000 9 45.000000 10 55.000000如果将函数子程序中的N、S变量定义为AUTOMATIC属性,结果与现在完全不同,请读者将修改后的程序上机调试运行,并分析程序结果。递归是一种很有用的数学思想,该种思想使得人们可以使用很

44、简单的方法处理一些无穷概念。在程序设计语言中,所谓递归,就是允许在一个子程序的定义部分直接或间接地调用被定义子程序。FORTRAN 语言在 FORTRAN 90以前的版本都不支持对子程序的递归定义,FORTRAN 90增加了递归了这一功能,方便了用户。851 递归的概念递归的概念数学上很多常见的概念能很好地说明递归的概念,为了帮助读者理解程序设计中的这一重要思想,下面讨论几个实例:第一个例子:自然数的定义。数学中自然数的定义是用递归的方法给出的,其定义是:(1)1是自然数。(2)如果N是一个自然数,则N+1是一个自然数。在上述定义中,使用了“自然数”这一被定义的概念。第二个例子:阶乘函数的定义

45、。11*11nnnnn)!(!上述定义中,使用(n-1)!来定义n!,其实还有很多这样的例子,如数学的一个分支图论中的图就是一个典型的递归定义的概念。85 递归调用递归调用关于递归定义,读者应注意两个方面:第一、递归定义都应该有一个非递归的分支。如第一个例子中明确说明1是一个自然数(非递归定义),第二个例子中明确了1!=1,也不是采用递归的定义。非递归定义分支是正确推导的出发点。第二、在递归定义的递归分支中,被定义的概念和用来作为递归定义的概念在规模上有所不同。如第一个例子中在假设n是一个自然数后,肯定n+1是一个自然数(规模越来月大,)第二个例子中用(n-1)!来定义n!(规模越来越小。)规

46、模地改变是按照递归定义能在有穷步骤内解决问题的保证。如我们根据第一个例子中关于自然数的定义来判断某个数N是否自然数,根据定义,1是一个自然数,于是1+1=2是一个自然数,并且3、4.是自然数,如果N能由1加上有限个1得到,则N是自然数,否则不是。在第二个例子中,我们如果需要计算10!,根据定义的第二部分,得到 10!=10*9!,对9!再继续使用定义,连续9次,得到:10!=10*9*8*7.*1852 递归函数递归函数递归函数的定义格式是:RECURSIVE FUNCTION 函数名(虚参表)RESULT(变量名).调用该函数本身 .END与一般函数子程序的定义格式比较,这里在保留字FUNC

47、TION 前加上了另一个保留字RECURSIVE,并且后面多了一个RESULT(变量)成分。该变量是FORTRAN 90用来存放函数的中间结果变量,其类型应与函数名的类型相同。在递归函数的函数体中,给函数赋值不是赋值给函数名,而是给RESULT后面括号中的变量赋值。在退出函数子程序并返回到其调用程序单元之前,FORTRAN 90会将该变量的值自动赋值给函数名。例 8.13 设计一个计算N!的函数子程序,并用其计算从键盘输入的任意自然数N的阶乘。函数子程序如下:RECURSIVE FUNCTION FAC(N)RESULT(FAC1)IF(N=1)THEN FAC1=1 !只能给FAC1赋值 E

48、LSE FAC1=N*FAC(N-1)!只能调用FAC函数 ENDIFEND主程序如下:PROGRAM FAC_PROWRITE(*,*)PLEASE INPUT A NUMBER.READ(*,*)NWRITE(*,*)N,!=,FAC(N)END运行情况是:PLEASE INPUT A NUMBER.5(并键入回车键)5!=120.000000在递归函数子程序中,不能直接给函数名赋值,而只能给RESULT变量(这里是FAC1)赋值,但在递归调用时,则必须使用函数名才可以,这一点请读者务必注意。下面对该程序的执行过程作一些分析:程序从主程序FAC_PRO的第一个语句WRITE开始执行,屏幕上

49、出现“PLEASE INPUT A NUMBER.”提示。用户从键盘输入5,EXAM6用参数5调用函数子程序FAC,这是第一次调用,调用的程序单元是FAC_PRO。在函数子程序FAC中,因为实参5不等于1,执行函数体的ELSE块,以参数N-1=4调用FAC,这是第二次调用FAC,调用的程序单元是FAC本身,如此反复五次,到第五次调用FAC函数子程序时,调用参数N=1,执行FAC的IF块,计算出此时的函数值为1,并最终计算出函数值为120。如果在主程序FAC_PRO中,用户给N输入的值是一个负整数,如-5,根据以上的分析,其递归调用过程是:FAC(-5)=-5*FAC(-6)=(-5)*(-6)

50、*FAC(-7)=.因为调用实参永远不等于1,因而每一次的调用都引起一个新的递归调用,这样的递归过程显然没有完结的时候,为避免这种情况,应该在函数子程序FAC中有对N0)THEN FAC1=N*FAC(N-1)!只能调用FAC函数 ELSE FAC1=0 !假定负数的阶乘函数值为0 ENDIFEND程序中假定,当实参0时,函数值为0下面再讨论一个读者熟悉的例子。例 8.14 Fibonacci数列可以用下列函数形式来定义:设计一个函数子程序,计算Fib(n)。不用递归方法,读者也可以很容易的设计出求Fib(n)的函数,但使用递归方法会使程序更自然一些,下面是使用递归方法的函数子程序:RECUR

51、SIVE FUNCTION FIB(N)RESULT(FI)SELECT CASE(N)CASE(:-1)FI=-1 !假定负数的函数值为-1CASE(0)FI=0CASE(1)FI=1)1()1()2()1(1)0(0)(nnFibnFibnnnFibCASE(2:)FI=FIB(N-2)+FIB(N-1)END SELECTEND程序FIB_PRO7用FIB函数子程序计算FIB(1)到FIB(10)的值。PROGRAM FIB_PROWRITE(*,*)(FIB(I),I=1,10)END结果是:1.000000 1.000000 2.000000 3.000000 5.000000 8.

52、000000 13.000000 21.000000 34.000000 55.000000关于递归函数子程序,读者应注意:1函数子程序单元中不应该直接给函数名赋值,而应该给RESULT变量赋值,并且RELUST变量应和函数名的数据类型相同;2函数体一般由两个或两个以上的分支构成,其中至少有一个分支是非递归的(例8.10的IF块分支、例8.11的:-1,0,1三个分支都是非递归的),并且递归分支的参数应朝非递归分支的方向发展。853 递归子例行程序递归子例行程序递归子例行程序的定义格式:RECRUSIVE SUBROUTINE 子例行程序名(虚参表).CALL 子例行程序名(实参).END例

53、8.15 设设计一个程序,求大于某个数据E的最小的S及对应的n)1()1()1(1)1(0)(1nnSnnnSn分析:这个问题初看与递归问题无关,我们定义一个数列S(n)显然,这里用递归方式所定义的数列S(n)和前面非递归方式定义的数列S是相同的,解决这个问题的递归子程序如下:RECURSIVE SUBROUTINE SN(S,N,E)N=N+1S=S+1.0/NIF(SE)CALL SN(S,N,E)!未到规定值,递归END调用程序如下:PROGRAM EXAM_SNWRITE(*,*)PLEASE INPUT A NUMBER TO EREAD(*,*)EN=0S=0CALL SN(S,N

54、,E)WRITE(*,*)S=,S,N=,N,E=,EEND第一次调用结果:PLEASE INPUT A NUMBER TO E 3 (键入回车键)S=3.019877 N=11 E=3.000000第二次调用结果:PLEASE INPUT A NUMBER TO E 5 (键入回车键)S=5.002069 N=83 E=5.000000设计递归子例行程序应该注意:在递归子例行程序中,递归调用语句应处于一个分支块中,并且随着逐次的递归,分支条件会有不成立的时刻,否则,递归就没有结束的时刻。在例8.15中,递归调用语句置于分支条件(SA(J)THEN T=A(I)A(I)=A(J)A(J)=TE

55、NDIF ENDDOENDDOEND程序的运行结果是:217.366.591.972.757.666.75.344.977.902.775.164.877.50.643.432.457.158.703.124.50.164.432.666.877.75.217.457.703.902.124.344.591.757.972.158.366.643.775.977.前面的一组数是没有排序的,后面是排序后的结果,本例通过等价关系,把一个二维数组的问题简单的化为一个一维数组的问题,这也说明了等价语句的一种作用。两点说明:1 FORTRAN 90 中容许定义不同类型的变量等价,但由于不同类型的变量的数

56、据存储形式不同,因而定义这种等价关系实际上是没有意义的。如:INTEGER AREAL BEQUIVALENCE(A,B)此时整型数据 A 和实型数据 B共享存储单元,但当A以整型数据格式存储在该存储单元中时,如果被B以实型数据格式读出后会变得面貌全非,反之亦然,读者不要认为二者的整数部分会相同,当读者学习过数据的物理存储方法后,就会了解产生这种不同的原因。2 等价语句(EQUIVALENCE)只是建立同一个程序单元中几个变量之间的等价关系,不能实现不同程序单元之间的数据交换,不要试图通过定义不同程序单元之间的变量等价来实现程序单元间的通信。862 公用数据块公用数据块一个应用程序是由一个主程

57、序单元和若干个辅助程序单元(函数子程序、子例行程序、数据块子程序、模块子程序等)组成,这些不同的程序单元为了完成一个共同的任务而相互支持、相互通信、协同工作,前面用了很大的篇幅讨论程序单元之间采用参数的虚实结合完成数据通信的方法,下面讨论数据传递的另外一种方法公用数据区。所谓公用数据区,就是一个程序中的每个程序单元都可以访问的公共区域。数据共用区分为有名公用区和无名公用区两种,一个程序只有一个无名公用区,但可以定义多个有名公用区。定义共用区的语句格式是:COMMON 变量名表 !在无名公用区中定义了一组变量COMMON/公用区名1/变量名表1,/共用区名2/变量名表2,.!在公用区1中定义了变

58、量表1,在公用区2中定义了变量表2。如:PROGRAM MAIN FUNCTION SUB()COMMON A,B,C,D COMMON X,Y,Z,C.END END上例中,主程序MAIN在无名公用区中定义了实型变量A、B、C,D而函数子程序SUB则在无名公用区中定义了实型变量X、Y、Z,C,因为MAIN程序单元与SUB程序单元同属于一个程序,其无名公用区是一个,因而,变量在内存中的存储形式如表8.1所示主程序(MAIN)变量名存储的数据子程序(SUB)中的变量名A3.5XB7.8YC-21.6ZD6.3C表8.1 公用区变量存储示意图 从表8.1可以看出,对同一个存储单元,MAIN程序单元

59、以名字A调用,而SUB程序单元以名字X调用,用这种方法建立起MAIN中A和SUB中X之间的联系,SUB中要传递数据给A的话只需向X赋予要传递的数据即可,反之亦然。公用区语句要注意的问题:1 公用区中的变量按顺序对应,不是按名字对应,如上例中,MAIN中的变量C并不是与SUB中的C对应,而是与Z对应。2 不同程序单元在COMMON 语句中定义的变量个数不必相同,但只在前面相对应的变量间才建立了对应关系(公用存储单元)。3 有对应关系的变量(公用存储单元)的数据类型应该一致,否则无实际意义。4 不要把这里的公用存储单元关系和变量间的等价关系混淆:公用存储单元关系是不同程序单元之间的变量,而等价关系

60、是一个程序单元内部的变量。5 一个程序单元中可以包含多个COMMON语句,如:COMMON X1,X2,X3,X4,X5,X6和语句序列COMMON X1,X2COMMON X3,X4,X5COMMON X6是完全相同的。6 如果公用存储变量的名字不遵守IN隐含规则,则必须对这些变量进行数据类型的说明。如果某个程序单元在COMMON语句中存放了多个变量(比如说20个),而另一个程序单元只与其中的部分变量通信(比如说与第15个变量和第18个变量),因为公用区语句COMMON按顺序对应,因而在第二个程序单元的COMMON语句中必须也至少列出18个变量,不仅麻烦,还把不应与外部程序单元通信的16个变

61、量与外部变量建立了公用存储单元关系,这些变量的值受外部程序单元的影响,不符合模块化程序设计原则。为了克服这一问题,FORTRAN 中引进了有名公用区的概念。比如对上述问题,可以使用有名公用区这样来解决。PROGRAM MAINCOMMON /ARY1/X1,X2,X3,X4,X5,X6,X7,X8,X9,X10COMMON /ARY2/X11,X12,X13,X14,X16,X17,X19,X20COMMON /ARY3/X15,X18.ENDSUBROUTINE SUBCOMMON /ARY3/A,B.END这样就把MAIN中的X15、X18和SUB中的A、B变量建立了公用存储单元关系,并且

62、没建立多余的公用存储关系。使用有名公用区除要注意和无名公用区相同的问题外,还应注意:1 公用区名字写在存储的变量名前,并用两个斜杠(/)括起来,公用区名字命名规则与FORTRAN的变量名相同,同一个程序(不是同一个程序单元)中的有名公用名不能相同。2 不同程序单元之间按公用区名建立公用存储关系,也就是说:存储在不同名字的公用区中的变量没有关系,存储在相同名字的公用区中的变量按顺序建立公用存储关系。我们可以把无名公用区看作一种系统已定义好名字(因而无须在COMMON语句中说明公用区名)的特殊的有名公用区,这样就可以把两者统一起来。例 8.17 设计一个按分数规则进行加减法程序。分析:一般的分数加

63、减法的形式是:jinmlk分析:一般的分数加减法的形式是:其中:i=k*mn*l J=l*m i,j的最大公约数为1我们设计一个子程序完成i,j的计算,显然,子程序要从主程序中获取k、l、m、n等4个数据和一个运算符,并且要把结果i、j返回个主程序,这样,主子程序间共有7个量要进行通信。程序如下:SUBROUTINE ADD !计算两个分数的和或差 COMMON K,L,N,M,C,I,J !在无名公用区中定义7个通信变量 INTEGER(1)C !定义C为单字节整型数 INTEGER F1 !定义函数子程序名F1为整型 IF(C=1)THEN !C为运算符,1表示加法,-1表示减法 N1=K

64、*M+N*L ELSE N1=K*M-N*L ENDIF M1=L*M I=N1/F1(N1,M1)!F1(M,N),函数子程序,求M、N的最大公约数 J=M1/F1(N1,M1)!分子分母用最大公约数约分ENDSUBROUTINE ADD INTEGER FUNCTION F1(M,N)!求M、N的最大公约数INTEGER T,M1,N1,M,NM1=MN1=NIF(M1N1 T=M1 M1=N1 N1=TENDIFT=MOD(M1,N1)DO WHILE(T0)!用辗转相除法求最大公约数 M1=N1 N1=T T=MOD(M1,N1)ENDDOF1=N1END FUNCTION F1PRO

65、GRAM FRACTIONCOMMON N1,N2,N3,N4,C1,N5,N6 !在无名公用区中存储7个变量与ADD通信INTEGER(1)C1INTEGER N1,N2,N3,N4,N5,N6WRITE(*,*)PLEASE INPUT K,L,M,NREAD(*,*)N1,N2,N3,N4WRITE(*,*)PLEASE INPUT C(1,-1)READ(*,*)C1CALL ADD()!调用ADD子程序求结果的分子分母IF(C1=1)THEN WRITE(*,100)N1,N2,N3,N4,N5,N6ELSE WRITE(*,200)N1,N2,N3,N4,N5,N6ENDIF100

66、 FORMAT(I4,/,I4,+,I4,/,I4,=,I4,/,I4)200 FORMAT(I4,/,I4,-,I4,/,I4,=,I4,/,I4)END PROGRAM FRACTION 程序的运行结果是:第一次运行:PLEASE INPUT K,L,M,N1,2,1,3 (回车)PLEASE INPUT C(1,-1)1 (回车)1/2+1/3=5/6第二次运行:PLEASE INPUT K,L,M,N5,6,3,4 (回车)PLEASE INPUT C(1,-1)-1 (回车)5/6-3/4=1/12863 数据块子程序数据块子程序如果一个程序中要对一部分变量赋予相应初值,并且这些初值为该程序中几个不同的程序单元所引用,则可以建立一个特殊的程序单元,在该程序单元中对这些变量进行集中说明并使用DATA语句赋初值,完成这种任务的程序单元称为数据块程序单元。数据块程序单元的形式是:BLOCK DATA 程序名 程序单元END BLOCK DATA如:BLOCK DATA EXAM_BLOCKDATA INTEGER A,B,C,D DIMENSION A(100)COMMON /AR

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!