1、第6章 子程序,子程序是构造大型程序的有效工具,一个实用程序(不管是系统程序还是应用程序),一般都含有多个子程序。 FORTRAN 90 中的子程序按子程序完成的功能划分有子例行程序、函数子程序、数据块子程序等,按是否定义在某个特定的程序单元内部来划分有程序单元子程序、模块子程序以及内部(INTERNAL)子程序等,这些通称为子程序。 子程序不能独立运行,它们和一种称为主程序(就是本章前读者已经熟知的程序结构)的程序单元一起组成一个实用程序。一个程序可以不含子程序,但不能缺少主程序。 本章讨论各种子程序的结构、功能以及子程序与主程序或子程序之间的数据交互作用。语句函数不具备子程序的一般书写特征
2、,但其作用与子程序相同,也一并放在本章讨论。通过本章的学习,读者应能熟练地选择并设计恰当的子程序形式来构造自己的程序,从而提高程序设计能力。,语句函数,SIN(X)、SQRT(X)等由FORTRAN编译器提供的函数为内在函数。这些内在函数是在程序设计过程中使用频率很高,并且一般实现比较复杂的函数。用户在使用内在函数时,并不需要对函数的实现过程进行描述,只需按照FORTRAN 90手册要求使用即可,FORTRAN 90编译器“认识”这些内在函数并能正确完成函数所规定的功能。 在实际设计过程中,用户还会遇到大量的并未包含在内在函数中的其他函数,计算这些函数时,就不能象内在函数那样仅仅使用函数名来使
3、用这些函数,而必须在适当的地方以FORTRAN 90能“理解”的形式向FORTRAN 90 编译器说明这些非内在的函数的计算过程与参数类型,当函数的计算过程简单到可以用一个语句定义清楚时,这样的定义语句就称为语句函数。,例 6.1 设多项式函数设计一个程序,计算f(1)、f(10)、f(12)、f(-5)的值。 程序如PROGRAM EXAM1A :,PROGRAM EXAM1AX=1FX=5*X*3-2*X*2+7*X+6WRITE(*,*)f(,X,)=,FXX=10FX=5*X*3-2*X*2+7*X+6WRITE(*,*)f(,X,)=,FX,X=12FX=5*X*3-2*X*2+7*
4、X+6WRITE(*,*)f(,X,)=,FXX=-1FX=5*X*3-2*X*2+7*X+6WRITE(*,*)f(,X,)=,FX END,显然,程序EXAM1A不简练,把一个相同的函数表达式重复了多遍,如果能定义一个函数f(x),然后分别使用1、10等参数来调用f(x),将会有效的简化程序量,这就是本节要讨论的内容。,611 语句函数的定义,如前所述,由于FORTRAN 90 的编译器不“认识”一般的函数f(x),不知道怎样计算f(x)的函数值,因而,在使用(称为函数调用)函数时,必须向FORTRAN 90编译器说明该函数的计算方法,这种说明过程称为函数定义。 语句函数定义的一般格式是:
5、,1.语句函数名 在语句函数定义格式中,f称为函数名,函数名的组成规则与变量名相同。如果没有在语句函数定义语句前用类型语句说明该函数名的数据类型,则该语句函数的函数值的类型按其函数名遵守IN隐含规则。如f函数隐含为实型函数,而nf则隐含为整形函数。 F(X,Y)=X*2+Y*2 ! 定义了一个实型函数F INTEGER F(X,Y)=X*2+Y*2 ! 定义了一个整型函数F NF(X,Y)=X*2+Y*2 ! 也同样定义了一个整型函数F 语句函数不能与同一个程序单元中的变量同名。,2.语句函数的虚参 在语句函数定义语句中的函数参数称为虚参,他们本身是没有值的,只有在函数调用时才用实际参数(称为
6、实参)代替。实参或是常数、或是一个有确定值的变量、或是一个可以计算值的表达式。虚参在形式上与普通变量相同,一个语句函数中的虚参不能同名。不同语句函数中的虚参可以同名,虚参也可以和程序中的变量同名。 当没有语句对虚参的数据类型进行说明时,虚参的类型遵守IN隐含规则;当使用了类型说明语句对虚参类型进行说明后,这种说明对于虚参以及与虚参同名的变量同时有效。 INTEGER Z F(X)=3*X*2+5 G(Y)=3*Y*2+5 H(Z)=3*Z*2+5 在上述程序中,函数F和G本质上是一个函数,因为对于任意的实参T,F(T)和G(T)总是相同的,但函数F、G和H有点不同,其虚参Z被说明为整型。 当虚
7、参个数多于一个时,虚参间用逗号分隔,当没有虚参时(这样的语句函数没有使用价值),函数名后的括弧也不能省略。,3.语句函数表达式语句函数表达式说明函数与参数的对应关系,在函数表达式中,可以包含常量、变量、函数虚参(虚参必须包含在表达式中)、FORTRAN 90内在函数、数组以及前面已经说明了的语句函数。4.关于语句函数的进一步说明(1)只有当函数关系简单,可以用一个语句描述函数与参数的对应关系时,才能使用语句函数。(2)语句函数是非执行语句,语句函数的定义语句要放在一个程序单位的所有其他说明语句之后,并放在所有可执行语句之前。(3)语句函数只有在本程序单元中才有意义。也就是说,不能使用其他程序单
8、元中的语句函数。(4)语句函数中的虚参必须是变量,不能是常量、数组元素和内在函数等。(5)语句函数是有类型的,因而语句函数表达式的类型一定要和其函数名的类型相容,612 语句函数的调用,语句函数一经定义,就可以在同一个程序单元中调用它,调用的形式和内在函数完全相同。 函数调用要注意以下两个问题: 1调用时可以使用常量、变量、内在函数以及它们的表达式作为实参代替对应的虚参位置,但要保证实参与虚参具有完全相同的类型,并且实参是可以计算值的 (即调用前实参中包含的变量全部已经赋值。) 2 实参和虚参个数相同。 例 6.2 用函数语句的方法设计例6.1 PROGRAM EXAM1BINTEGER XF
9、(X)=5*X*3-2*X*2+7*X+6WRITE(*,*)F(2),F(10),F(12),F(-2) END,6.2 函数子程序,语句函数由于要求在一个语句中完成函数的定义,因而它只能解决一些较简单的问题,当函数关系比较复杂,用一个语句无法定义时,语句函数就无能为力了,这时需要用到函数子程序。,621 函数子程序的定义,函数子程序是以保留字 FUNCTION 开头,并以保留字 END结束的一个程序段,该程序段可以独立存储为一个文件,也可以和调用它的程序单元合并存储为一个程序文件。函数子程序的定义格式是: 类型说明 FUNCTION 函数名(虚参表)函数体 END 函数名的命名方法与变量名
10、相同,虚参可以是简单变量和数组变量,但不能是常数、数组元素、表达式),例 6.3 编程,求:,分析:上述三个数列的通项不同,求和范围也不同,用一个程序段难以同时计算三个数列的和,并且,因为无法用一个语句函数完成数列的求和计算,所以也无法使用语句函数来简化程序设计。,FUNCTION SM(M,N,L)SM=0DO I=M,NIF (L0) THENSM=SM+I*LELSESM=SM+(1.0*I)*L !进行实型运算 END IFENDDOEND FUNCTION SM,PROGRAM EXAM2 !开始主程序单元定义 WRITE(*,*)S1=,SM(1,100,2) !调用函数子程序SM
11、完成S1的计算 WRITE(*,*)S2=,SM(100,140,3) WRITE(*,*)S3=,SM(20,50,-1) END 程序运行结果如下:S1= 338350.000000S2= 7.291440E+07S3= 9.514656E-01 对程序EXAM2作如下说明: 程序的第一部分是函数子程序的定义部分,如前所述,它可以单独存储为一个程序文件。从保留字PROGRAM开始至END是主程序部分,一个程序总是从主程序开始运行的,主程序的第24个语句都要打印函数SM的值,这时调用函数定义部分,根据函数的定义描述计算函数的值并予以打印。三个打印语句分别调用了三次函数子程序(即执行了三次函数
12、子程序)。,采用函数子程序设计程序时并没有提高程序的执行效率,但却可以有效的提高程序的设计效率。当函数子程序的定义过程和主程序放到一个程序文件中时,存储顺序是任意的,函数子程序可以放到主程序之前(如EXAM2),也可以放到主程序之后,但是程序总是从主程序开始执行。,REAL FUNCTION SM(M,N,L) IMPLICIT NONE INTEGER:M,N,L, J REAL:I(M:N) FORALL(J=M:N) I(J)=J SM=SUM(I*L) END FUNCTION SM,例:程序的优化,FUNCTION SM(M,N,L)SM=0DO I=M,NIF (L0) THENS
13、M=SM+I*LELSESM=SM+(1.0*I)*LEND IFENDDOEND FUNCTION SM,函数定义部分应注意如下问题: 1函数值的类型可以在函数定义时予以说明,下面的两种说明方法是等效的: (1)INTEGER FUNCTION F1(X1,X2.XN) 函数体 END (2)FUNCTION F1(X1,X2.XN) INTEGER F1 函数体 END 都说明F1是一个整型函数,当未使用这种显示的类型说明时(如例6.3),函数值的类型遵守IN隐含规则。,2 函数不能有同名虚参。虚参的类型可以在函数体中进行说明方法,当未对虚参类型进行说明时,虚参类型遵守IN隐含规则。 3
14、函数定义部分中一定要有一个语句将函数值赋值给函数名(如例6.3中的第5句和第7句)。这种赋值语句的格式是:函数名=表达式 注意不要在函数名后带上括弧,写成:函数名(虚参表)=表达式,622 函数子程序的调用,定义函数子程序的目的是为了调用。不仅主程序可以调用一个函数子程序,函数子程序也可以调用其它的函数子程序,甚至于还可以调用本身(递归调用)。调用程序称为主调程序单元,而被调用的函数子程序称为被调程序单元。调用一个函数子程序的方法和调用内在函数和语句函数的方法基本相同: 1 调用时应该用实参代替函数子程序定义部分的虚参,实参和虚参的类型要相同。 和语句函数一样,实参可以是常量、变量、表达式等。
15、 2 调用程序单位中的变量不能与函数子程序同名。函数值的类型由函数定义程序单元决定,与调用程序单元无关。 3 当函数名的类型不满足IN隐含规则时,在调用程序单元中要对函数名的类型给出说明(如例 8.4 中的主程序EXAM3的第2句。) 4 不能调用一个没有定义的函数子程序(这一点和内在函数是不同的。),例 6.4 用函数子程序的方法设计一个程序,求50100内的所有素数及其和。,分析:设计一个函数子程序 PRIME(N),函数PRIME的值定义如下:,主程序的任务是应用PRIME函数子程序在50100之间使用枚举法求出那些使PRIME函数值为1的自然数并求这些数的和。,函数子程序如下: FUN
16、CTION PRIME(N) INTEGER PRIME * 定义PRIME是整型函数 PRIME=0 DO I=2,N-1IF (MOD(N,I)=0) GOTO 10 * 参数N有因子I,非素数,退出循环 ENDDO PRIME=1 * 参数N无任何因子,函数值为1 10 END,主程序如下: PROGRAM EXAM3 INTEGER PRIME * 说明要调用的函数PRIME 为整型 DO I=50,100IF (PRIME(I)=1) THENS=S+IWRITE(*,*)IEND IF END DO WRITE(*,*)S=,S END 运行结果如下:838997S= 732.00
17、0000,PROGRAM EXAM3 INTEGER: PRIME, index(50:100) DO I=50,100index(i)=PRIME(I)*i END DO WRITE(*,*)S=,Sum(index) END,例:程序的优化,PROGRAM EXAM3 INTEGER PRIME DO I=50,100IF (PRIME(I)=1) THENS=S+IWRITE(*,*)IEND IF END DO WRITE(*,*)S=,S END,例 6.5 当一个数各个数位的立方和等于这个数本身时,称这样的数为水仙花数(如153=1*3+5*3+3*3,所以,153是一个水仙花数,
18、)编程:求100999之间的水仙花数。 分析:设计一个函数子程序NUM(N,I),当I=100时,函数NUM返回N的百位上的数;当I=10时,NUM返回N的十位上的数,I=1时,NUM返回N的个位上的数。主程序中调用NUM函数在100999之间找出所有水仙花数。,PROGRAM FLOWERDO I=100,999IF (NUM(I,100)*3+NUM(I,10)*3+NUM(I,1)*3=I) THENWRITE(*,*)IENDIFENDDO END,FUNCTION NUM(N,I)SELECT CASE(I)CASE(100)NUM=N/100 !用N百位上的数赋值给函数名CASE(
19、10)NUM=MOD(N/10,10) !用N十位上的数赋值给函数名CASE(1)NUM=MOD(N,10) !用N个位上的数赋值给函数名END SELECT END 程序运行结果如下:153370371407,题目6.1 设计一个求向量叉乘的子程序,用主程序调用求任意两个向量的数学叉乘值。,题目6.2设:设计一个计算S的函数子程序,并调用该函数子程序计算:题目63 设计一个计算n! 的函数子程序,并调用该子程序计算数e的近似值。当 n!1E8时停止计算。 计算公式是:,题目64 对已知自然数n,设计一个判断n是否为素数的子程序,并调用该子程序: (1) 将100120共11个偶数分解为两个素
20、数和的形式 (2) 用READ语句读入一个自然数n,对n进行素因子分解。 要求使用函数子程序和子例行程序两种方法。 题目65 对任意自然数n,设计一个求n的各数位立方和的函数子程序F(n)(如F(121)=1*3+2*3+1*2=1+8+1=10),并调用F(n)求1000以内的所有水仙花数(水仙花数的概念请参看例 8.5)。,除了函数子程序外,还有一种子例行子程序,函数子程序和子例行子程序都是一种独立的程序单元,二者的差别是:函数子程序的名字代表一个值,因而是有类型的,而子例行程序的名字不代表一个值,因而其名字没有类型问题。从使用上来说,二者是可以相互替代的。 631 子例行子程序的定义 子
21、例行子程序是由保留字SUBROUTINE开头,到保留字END结束的一个程序段。其定义格式是: SUBROUTINE 子程序名(虚参表)子例行程序体 END 子程序的命名方法与变量相同。虚参由变量、数组名(不能是数组元素,常数、表达式)充当,当虚参多于一个时,各虚参间用逗号分隔,当没有虚参时,子例行程序名后的一对括弧可以省略。 子例行程序的设计方法和函数子程序相同,但因为其名字没有值,所以不能有对子例行程序的名字赋值的语句。,63 子例行程序,632 子例行子程序的调用 子例行程序的调用格式是: CALL 子例行程序名(实参表) 其实参的类型与函数子程序相同。和函数子程序的调用不同的是,子例行子
22、程序的调用是一个独立的语句。 子例行程序调用的其它事项与函数子程序的调用相同。 下面通过两个实例来讨论函数子程序与子例行程序的相同点与不同点。,例 6.6 用子例行程序的方法完成例6.3 分析:例6.3中定义了一个函数SM(M,N,L),我们将该函数子程序修改为一个子例行程序 SM(S,M,N,L),虚参M,N,L的含义与例8.3相同,虚参S用来存储结果和(用来替代函数名SM)。 程序如下:,SUBROUTINE SM(S,M,N,L)DO I=M,NIF (L0) THENS=S+I*LELSES=S+(1.0*I)*LEND IFENDDO END,PROGRAM EXAM5CALL SM
23、(S1,1,100,2)CALL SM(S2,100,140,3)CALL SM(S3,20,50,-1)WRITE(*,*)S1=,S1WRITE(*,*)S2=,S2WRITE(*,*) S3=,S3 END 运行结果如下:S1= 338350.000000 S2= 7.291440E+07 S3= 9.514656E-01,例 6.8 设计一个自例行程序程序,求任意矩阵的转置矩阵。 设计一个子例行程序TRAN(A,B,M,N)完成将矩阵A转置后放矩阵B,M、N分别是矩阵A、B的行数和列数。 主程序如下:PROGRAM EXAM6PARAMETER(M=3)PARAMETER (N=4)D
24、IMENSION A(M,N),B(N,M)INTEGER A,BWRITE(*,*)please input a 3x4 MatraREAD(*,200)(A(I,J),J=1,N),I=1,M)CALL TRAN(A,B,M,N)WRITE(*,300)(B(I,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,NDO I=1,MDO J=1,NB(J,I)=A(I,J)ENDDOENDDO END,运行情况: please in
25、put a 3x4 Matra1 2 3 45 6 7 89 10 11 12,显示结果如下:1 5 92 6 103 7 114 8 12,优化?,641 简单变量作为虚参 这是一种最常见的情况,例6.4,6.5都是这种情况,在这种情况下,根据子程序调用时实参的不同类型,又可以进一步分为两种情况: 1简单变量或数组元素作为实参 FUNCTION SUB(A,B) PROGRAM TTEMX=3Y=-5S=S+SUB(X,Y)END END,不同程序单元之间的数据传送方法有参数的虚实结合、建立公用区及通过数据文件等三种方式实现。本节先讨论参数的虚实结合方法,6.7节讨论公用区的概念与用法,其他
26、章节讨论文件方法。,图6.1 A、X,B、Y共用存储单元,6.4 程序单元之间的数据传递,此种情况下FORTRAN 90系统将实参与虚参安排同一个存储单元,对虚参的任何改变都作用在对应的实参上,因而调用一个子程序(包括函数子程序和子例行程序)时,实参的值有可能改变。,2常量或表达式作为实参 当用常量或表达式作为实参时,FORTRAN 90编译器首先计算表达式的值(如果实参为表达式时。)然后将该值赋值给对应的虚参。此种情况下,子程序中不能改变与常量(或表达式)对应的虚参的值,否则结果难以预料。 642 数组名作为虚参 当虚参是数组名时,对应的实参可以是与虚参类型相同的数组名或数组元素,并且实参与
27、虚参共用一片连续的存储单元。 1虚参为数值型或逻辑型数据(此时的实参当然也是对应类型) (1)实参为数组名时,FORTRAN 90 编译器将实参数组的第一个元素的存储地址传送给子程序,并将其作为对应虚参数组的第一个元素的存储地址,从而使两者共用一片存储单元。 (2)当实参是一个数组元素时,FORTRAN 90 编译器将该元素的存储地址传送给子程序,并将其作为对应虚参数组的第一个元素的存储地址,从而使虚参数组与实参自该元素以后的元素共用一片存储单元。,在实参与虚参数组之间传送数据时,不要求两者的行列数相同,甚至于实参元素的个数可以多于虚参(这种情况下,多余的实参不参与子程序中的运算。)牢记:FO
28、RTRAN 90是按列为主存放多维数组的,实参和虚参间的元素按存储顺序对应。如: PROGRAM TTST FUNCTION SUB2(X) DIMENSION A(3,4) DIMENSION X(2,6)DO I=1,3 S=0 DO J=1,4 DO I=1,2A(I,J)=I+J DO J=1,6ENDDO IF (MOD(I+J,3)=0) THENENDDO S=S+X(I,J)WRITE(*,*)SUB2(A) ENDIFEND ENDDO,在程序TTST的倒数第二句调用了函数子程序SUB2,虚参X是一个2x6的矩阵,而实参A是一个3x4的矩阵,两者的元素对应关系是: A(1,1
29、)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(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=18 2虚参为字符型数据 当虚参为字符型数据时(当然对应的实参也为字符型
30、数据),实参和虚参不是按照数组元素的顺序对应,而是按照字符位置一一对应,读者只要了解FORTRAN 90中字符型数据的存储规则,并将实参与虚参的存储顺序派出来,就不难确定实参元素与虚参元素的对应关系,这里不再详细说明。,传递字符串变量时,可以不特别赋值它的长度。因为它的长度早就设置好了,数组会占据内存中一块连续空间,在传递参数时,实际上是传递数组元素当中某一个内存的地址。,多维数组在传递是,只有最后一维可以不赋值大小,其他所有维度必须赋值;一维数组则可以完全不用赋值维度。,3可调数组 请读者重新考察例 8.8中的子例行程序TRAN(A,B,M,N),在子程序的说明中将A、B说明为A、B分别是一
31、个M*N的矩阵,注意在子程序中并没有具体规定M、N的值,这样的数组称为可调数组,可调数组的引入提高了子程序的适应性,大大提高了编程效率。应注意可调数组只能作为虚参使用,不能在主程序中使用可调数组,也不能在子程序的其它地方使用可调数组。 643 子程序名作为虚参 子程序的虚参不仅可以是前面所述的各种数据类型,还可以是一个子程序名。 例 6.9 设有三个连续函数:,分析:用Simpson方法求函数的积分时要计算被积函数的函数值,因为三个被积函数不同,求其函数值的方法也就不同,要设计一个统一的函数子程序,必须设计一个虚参函数,并用被积函数作为实参来调用该函数子程序。 用Simpson方法求函数定积分
32、的函数子程序如下: 首先定义三个被积函数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-10 ENDFUNCTION H(X) !被积函数H(X) H=1/(1+X*2) END,再建立 SIMPSON 求积分函数子程序 FUNCTION SIMPSON(F,A,B) H=(B-A)/2 C=(A+B)/2 SIMPSON=H*(F(A)+4*F(C)+F(B)/3 END PROGRAM SIMPSON_PRO EXTERNAL F,
33、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=,I1 WRITE(*,*)I2=,I2 WRITE(*,*)I3=,I3 END,程序的执行结果是:I1= -2.094445I2= 12500.000000I3= 7.833334E-01 关于实参函数名的说明: 1EXTERNAL 和 INTRINSIC 调用
34、虚参中有程序名的子程序(包括函数子程序和子例行程序)时,我们要在虚参的位置代之以一个实际存在的子程序名作为实参,实参子程序如果是FORTRAN 90 的内在函数,则在调用程序段中要用保留字 INTRINSIC 对该程序名作出说明,如果实参子程序是自己设计(或调用他人的程序库)的,则必须用保留字EXTERNAL对实参程序名作出说明(如例6.9),这种对函数名属性的说明就和对变量的类型进行说明一样,说明语句必须放在该程序段的所有可执行语句前。 必须强调:只需对实参的属性进行说明,至于虚参,因为其只是一个并不存在的形式子程序,并不具有INTRINSIC属性或EXTERNAL属性,是无法也无需说明其属
35、性的。 2内在函数的专用名和通用名 FORTRAN 90 的内在函数有通用名和专用名之分(部分函数的通用名和专用名一致),用FORTRAN 90 的内在函数作为实参时,只能使用这些函数的专用名而不能使用其通用名。,644 星号(*)作为虚参 星号(*)也可以作为虚参,与星号(*)虚参对应的实参是一个冠有星号(*)的语句标号。如: PROGRAM TEST SUBROUTINE F(S,*,*). IF (条件1)THEN. . RETURN 1 ! return 与1之间有空格 CALL F(A,*100,*200) ELSE *100 GOTO 300 RETURN 2 *200 . END
36、IF 300 END END,例 6.10 设计一个子例行程序,当参数C为加号(+)时,计算并打印A+B的值,C为减号(-)时,计算并打印A-B的值。 SUBROUTINE F(A,B,C,*,*,S) CHARACTER*1 C !定义参数C为单个字符变量 SELECT CASE(C)CASE(+)S=A+BRETURN 1 !返回到第一个冠有 *号的语句标号CASE(-)S=A-BRETURN 2 !返回到第二个冠有 *号的语句标号 END SELECT END,PROGRAM TEST CHARACTER*1 C WRITE(*,*)PLEASE INPUT 2 NUMBER AND A
37、 OPERATION SIGN READ(*,*)A,B,C CALL F(A,B,C,*10,*20,S) 10 WRITE(*,*)A,+,B,=,S !C参数为加号返回到此 GOTO 30 20 WRITE(*,*)A,-,B,=,S !C参数为减号返回到此 30 END 应该指出,象上面这样的分支问题,可在子程序单元F或调用程序单元TEST中用分支程序很容易地实现,这里采用的子程序中根据分支条件而返回到不同的出口的方法,违背了一个程序单元应具有单一出口的结果化原则,是不应提倡的,读者在程序设计的实践中应尽量避免这种用法。,645 变量的作用域 变量是为了完成一个计算任务而设置的一些数据
38、存储单元。FORTRAN 90为每一个变量在内存区域中建立一个物理的连续存储区,一旦某个变量完成其使命,FORTRAN 90将释放该变量所占据的物理存储区,该变量变得无定义。程序设计人员不能使用已经被FORTRAN 90释放的变量。到底FORTRAN 90何时为变量建立物理的存储区,又何时释放这些存储区呢?下面就来讨论这些问题。 1变量存储区的分配与释放 一个程序(是一个完整的程序而非组成程序的各程序单元)在投入运行时,系统为这个程序的全部变量一次性分配存储单元,当这个程序退出运行时,系统收回这个程序所占据的全部存储单元。一个程序中的全部变量被同时建立存储单元。一般来说,一个程序的变量单元也将
39、被同时释放,除非我们声明保留某个变量的存储单元。,2变量作用域 一个变量通常只在本程序单元(这里是程序单元而非整个程序)中起作用,离开建立该变量的程序单元,变量就失去了定义。 这种作用域的局限性,使用户在设计程序时,只需考虑在本程序单元中的变量只否有相互干扰,而无需考虑与其它程序单元之间的变量干扰问题,简化了程序的调试工作。在程序调用时,调用程序单元的实参是通过与被调用程序单元的虚参的结合来实现的,并不是调用程序单元中的变量直接在被调用程序单元中有定义。,例 6.11 讨论变量的作用域 SUBROUTINE F(A,B,C)INTEGER X,YX=5 ! 子程序S中有X、YY=8C=A+BW
40、RITE(*,*)X、Y IN SUB,X,YEND,PROGRAM EXAM8U=5V=8X=3 ! 主程序中也有X、YY=2CALL F(U,V,S)WRITE(*,*)X、Y IN MAIN,X,YWRITE(*,*)U,V,SEND,程序的执行结果是:X、Y IN SUB 5 8X、Y IN MAIN 3.000000 2.0000005.000000 8.000000 13.000000,从程序执行结果可以看到,尽管在C程序单元 EXAM8 和程序单元 VERY中都有变量X、Y,但其值是不相同的,说明它们是不相干的变量,只是同名而已。,3子程序中变量的存储属性 变量有数据类型,同时还
41、有存储属性,数据类型决定变量的运算特征,而存储属性决定一个变量所占用的存储单元在什么时候被释放,FPS中,变量的几种常见的存储属性有: (1)SAVE 属性:当声明变量的子程序执行完毕后,具有SAVE属性的变量的存储状态依然保留,在下一次调用该子程序时,这些变量保持上一次调用返回时的值。 (2)STATIC属性:具有该类属性的变量在整个程序(不是声明该变量的子程序)执行期中一直保留在存储单元中,这是变量的默认存储属性 (3)AUTOMATIC属性:当声明变量的子程序执行完毕后,具有AUTOMATIC属性的变量的存储单元被系统回收。 变量的存储属性的说明方法是: 存储属性 变量名表 这里的存储属
42、性是上述三种属性之一。如: AUTOMATIC A,B,C !定义实型变量A、B、C具有AUTOMATIC 存储属性 INTEGER SAVE X,Y !定义变量X、Y为整型,SAVE 存储属性,例 6.12 说明变量存储属性的作用。 PROGRAM DATAS_TEST DO I=1,10WRITE(*,*)I,F() ENDDO END,FUNCTION F() STATIC N,S ! 定义N、S为STATIC属性 N=N+1 S=S+N F=S END,程序的执行结果是:1 1.0000002 3.0000003 6.0000004 10.0000005 15.0000006 21.0
43、000007 28.0000008 36.0000009 45.00000010 55.000000,如果将函数子程序中的N、S变量定义为AUTOMATIC属性,结果与现在完全不同,请读者将修改后的程序上机调试运行,并分析程序结果。,参数属性,Intent (in) 只读,不能改变数值Intent(out)设定为输出结果的参数 Intent(inout) 可读可写,不定个数参数传递,在Fortran77及很多程序语言中,函数参数的个数都是有固定数目的。在fortran 90中,可以用optional命令来表示某些参数是可以省略的。,改变参数传递位置的方法,Fortran 90中允许不按照参数顺
44、序来传递参数。 Subroutine sub (a,b,c) End subroutine Call sub (b=2,a=1,c=3)Subroutine sub (a,b,c,d,e,f) Integer,optional:b,d,e,f End subroutine Call sub (a=1,c=3),递归是一种很有用的数学思想,该种思想使得人们可以使用很简单的方法处理一些无穷概念。在程序设计语言中,所谓递归,就是允许在一个子程序的定义部分直接或间接地调用被定义子程序。FORTRAN 语言在 FORTRAN 90以前的版本都不支持对子程序的递归定义,FORTRAN 90增加了递归了这一
45、功能,方便了用户。 651 递归的概念 数学上很多常见的概念能很好地说明递归的概念,为了帮助读者理解程序设计中的这一重要思想,下面讨论几个实例: 第一个例子:自然数的定义。数学中自然数的定义是用递归的方法给出的,其定义是: (1) 1是自然数。 (2) 如果N是一个自然数,则N+1是一个自然数。 在上述定义中,使用了“自然数”这一被定义的概念。 第二个例子:阶乘函数的定义。,65 递归调用,关于递归定义,读者应注意两个方面: 第一、 递归定义都应该有一个非递归的分支。如第一个例子中明确说明1是一个自然数(非递归定义),第二个例子中明确了1!=1,也不是采用递归的定义。非递归定义分支是正确推导的
46、出发点。 第二、 在递归定义的递归分支中,被定义的概念和用来作为递归定义的概念在规模上有所不同。如第一个例子中在假设n是一个自然数后,肯定n+1是一个自然数(规模越来月大,)第二个例子中用(n-1)!来定义n!(规模越来越小。)规模地改变是按照递归定义能在有穷步骤内解决问题的保证。 如我们根据第一个例子中关于自然数的定义来判断某个数N是否自然数,根据定义,1是一个自然数,于是1+1=2是一个自然数,并且3、4是自然数,如果N能由1加上有限个1得到,则N是自然数,否则不是。在第二个例子中,我们如果需要计算10!,根据定义的第二部分,得到 10!=10*9!,对9!再继续使用定义,连续9次,得到:
47、 10!=10*9*8*7.*1,652 递归函数 递归函数的定义格式是: RECURSIVE FUNCTION 函数名(虚参表) RESULT(变量名).调用该函数本身 END 与一般函数子程序的定义格式比较,这里在保留字FUNCTION 前加上了另一个保留字RECURSIVE,并且后面多了一个RESULT(变量)成分。该变量是FORTRAN 90用来存放函数的中间结果变量,其类型应与函数名的类型相同。在递归函数的函数体中,给函数赋值不是赋值给函数名,而是给RESULT后面括号中的变量赋值。在退出函数子程序并返回到其调用程序单元之前,FORTRAN 90会将该变量的值自动赋值给函数名。,函数子程序如下: RECURSIVE FUNCTION FAC(N) RESULT(FAC1)IF (N=1) THENFAC1=1 !只能给FAC1赋值ELSEFAC1=N*FAC(N-1) !只能调用FAC函数ENDIF END 主程序如下: PROGRAM FAC_PRO WRITE(*,*)PLEASE INPUT A NUMBER READ(*,*)N WRITE(*,*)N,!=,FAC(N) END 运行情况是:PLEASE INPUT A NUMBER5 (并键入回车键)5!= 120.000000,