收藏 分享(赏)

模块化程序设计——例程和模块.ppt

上传人:dreamzhangning 文档编号:5753833 上传时间:2019-03-16 格式:PPT 页数:41 大小:114KB
下载 相关 举报
模块化程序设计——例程和模块.ppt_第1页
第1页 / 共41页
模块化程序设计——例程和模块.ppt_第2页
第2页 / 共41页
模块化程序设计——例程和模块.ppt_第3页
第3页 / 共41页
模块化程序设计——例程和模块.ppt_第4页
第4页 / 共41页
模块化程序设计——例程和模块.ppt_第5页
第5页 / 共41页
点击查看更多>>
资源描述

1、第3章 模块化程序设计例程和模块,“模块”直接映射为例程:子程序和函数内部例程和外部例程程序构造单元:主程序(包含有内部例程)、模块(包含有内部例程)、外部例程(单独的,包含有内部例程),第一节 内部例程,一 内部函数 1. 构造形式实例牛顿法求解方程:方程的一般形式为 设根的一个近似值为 , 新的近似值 为:其中: 是 的一阶导数,通过叠代的方法获得近似解 ,满足 。假设 f(x)=x3+x-3, 那么f(x)=3x2+1;根的初值设为 2;叠代控制条件为f(x)10-6 ,或者叠代步数不超过20;用F(x)代表f(x),用DF(x)代表f(x)。,例 3-1 牛顿法解方程PROGRAM N

2、ewtonIMPLICIT NONEINTEGER : Its =0 !叠代数 INTEGER : MaxIts =20 !最大叠代数 LOGICAL : Converged =.false. !是否收敛REAL : Eps = 1e -6 !叠代精度REAL : X =2 !根的初值DO WHILE (.NOT. Converged .AND. ItsMaxIts)X = XF(X)/DF(X)PRINT*,X,F(X)Its = Its +1Converged = ABS(F(X)=EpsEND DOIF (Converged) THENPRINT*, Newton convergedEL

3、SEPRINT*, Newton divergedEND IF,CONTAINSFUNCTION F(X)REAL F, XF = X * 3 + X 3END FUNCTION FFUNCTION DF(X)REAL DF, XDF = 3 * X * 2 + 1END FUNCTION DFEND PROGRAM Newton,内部函数位于主程序的 CONTAINS 关键字和END 语句之间, 构造形式如下:FUNCTION 函数名( 参数列表 ) 声明语句 执行语句 END FUNCTION 函数名 其中, 为可选部分。IMPLICIT NONE,强制类型声明,其作用域为整个程序单元,所

4、以无需在内部函数中再重写此语句。 尽管可以在程序单元头部声明内部函数的函数名,参数及其有关变量,但从数据安全性考虑,还是应该在内部函数中予以声明。 若在内部函数中声明了和全局变量同名的局部变量,全局变量被屏蔽,内部函数引用的是局部变量,这种现象称为同名覆盖。,2. 全局变量和局部变量 例3-2 内部函数采用全局变量PROGRAM FactorialIMPLICIT NONEINTEGER IDO I =1, 10PRINT*, I, Fact(I)END DOCONTAINSFUNCTION Fact(N)INTEGER Fact, N, TempTemp = 1DO I = 2, NTemp

5、 = I * TempEND DOFact = TempEND FUNCTION FactEND PROGRAM Factorial,这是一个求N的阶乘的实例,程序输出结果:1 13 65 1207 50409 36288011 3991680013 193205350415 200431001617 -28852224019 109641728,上述程序的执行过程是: I 是一个全局变量,当内部函数 Fact 第一次被引用时,I 1,该值当传递给虚参 N,相同的 I 在函数循环体中被赋初值2,此时 2N,不执行DO循环(Fact=1),当函数Fact 返回到主程序打印时,I 的值为2;下一次

6、引用时,I 在主程序 DO 循环中增至3,依次类推 正确方式是在内部函数中重新声明 I为局部变量。例如:INTEGER I 在内部例程中声明所有变量应成为一条编程规则,以消除全局变量带来的负面影响。 共享数据,声明变量模块。,3. 函数返回值 Fortran中,表达式(函数)值一是通过函数名赋值来实现, 例如:F= X*3+X-3 此时赋值号左边的函数名不能带参数表。 另一是通过RESULT 子句来实现,例如:FUNCTION F(X) RESULT(R)REAL R,XR = X*3+X-3END FUNCTION F 其中,R代表函数结果,其数据类型代表函数类型;赋值号左边不再是函数名F

7、,而是函数结果 R , 相应的函数类型声明,也由声明函数名 F 改为声明函数结果 R 。,4. 语句函数 Fortran 90 也支持Fortran 77 提供的语句函数,不推荐。例3-3 语句函数的使用PROGRAM Statement_FunctionIMPLICIT NONEREAL f,x,y !函数名f、形参x、实参y均需声明类型f(x) = x*2/SQRT(1.0 + 2.0*x + x*2) !定义语句函数DOWRITE(*,(A),ADVANCE = NO) 输入 x 的值:READ*,yIF(INT(y) = 0) EXIT !0终止循环PRINT*,f(,y,)=,f(y

8、)END DOEND PROGRAM Statement_Function,语句函数的形式为:函数名(参数1,参数2,)=函数表达式语句函数在形式上和数学上的函数表达式完全一样,语句函数使用时应注意以下几点:(1) 语句函数先定义后使用,且只能用一条语句来定义; (2) 定义语句应放在声明部分,且放在语句函数相关的类型声明之后; (3) 参数列表可以为空(此时,函数实际上是一常量表达式),但函数名后边的一对括号,无论在定义还是引用时都不能省略。,二 内部子程序 内部子程序和内部函数主要差别在于: (1)没有返回值和子程序名关联,因此无需声明子程序类型; (2)通过CALL语句调用子程序; (3

9、)在例程原型(头)和 END 语句(尾)中,使用关键字SUBROUTINE; (4)若子程序参数表为空,子程序名后的一对括号可以省略; (5)函数通过函数名返回一个值,子程序通过函数参数可以返回多个值。,例3-4 内部子程序用来实现2个数据的返回(交换)PROGRAM ExchangeIMPLICIT NONEREAL : A =1, B=5CALL Swop(A, B)PRINT*, A, BCONTAINSSUBROUTINE Swop(X,Y)REAL Temp, X, YTemp = XX = YY = TempEND SUBROUTINEEND PROGRAM,程序说明: Fortr

10、an 参数传递,缺省为引用传递(地址传递); 子程序调用时,实参A、B的值被传递给参数 X、Y,改变后的形参值被返回调用程序,实现两个数据的交换; 子程序中的Temp 为局部变量,在主程序中不可访问; 如果希望参数以数值方式传递,这样形参的改变不影响实参,为此Fortran 90 提供了INTENT 属性。内部子程序的构造形式:SUBROUTINE 子程序名(参数表) 声明语句 执行语句 END SUBROUTINE 子程序名 ,主程序构造形式: PROGRAM 程序名 声明语句 执行语句CONTAINS 内部例程END PROGRAM 程序名 注意事项: (1)一个完整的程序有且只有一个主程

11、序; (2)主程序只有 END 语句是必须的,其他都是可选的; (3)若含有内部例程,必须出现关键字CONTAINS; (4)可以有多个内部例程,但内部例程不能再含有自己的内部例程,不允许内部例程的嵌套; (5)END 语句若出现程序名,其前面的 PROGRAM 关键字不能少。,第二节 主程序,构造形式:SUBROUTINE 子程序名(参数表)声明语句执行语句CONTAINS内部例程END SUBROUTINE 子程序名 或者FUNCTION 函数名( 参数表 )声明语句执行语句CONTAINS内部例程END FUNCTION 函数名 外部例程和内部例程的比较: (1)外部例程是单独的外部文件

12、,除头、尾外,形式上与主程序是相同的; (2)外部例程可以含有内部例程,而内部例程不能再含有内部例程; (3)END语句中的关键字 FUNCTION/ SUBROUTINE,在外部例程中是可选的,但在内部例程中是必须的。,第三节 外部例程,例 3-5 采用外部例程实现 Swop(交换)PROGRAM ExchangeIMPLICIT NONEEXTERNAL Swop !声明例程Swop 为外部的REAL : A =1, B=5CALL Swop(A, B)PRINT*, A, BEND PROGRAM ExchangeSUBROUTINE Swop(X, Y)REAL Temp, X, YT

13、emp = XX = YY = TempEND SUBROUTINE Swop若外部程序名与系统的标准例程名相同,编辑器会优先引用标准例程,而不是用户定义的外部例程;为避免出现这种情况,可在调用例程的声明部分,添加外部例程声明语句(EXTERNAL),这应该成为一个编程惯例。,例程接口:包括例程名、参数个数及各自的数据类型等信息。分为:显式接口(对标准例程、内部例程和模块例程)和隐式接口(对外部例程)。Fortran 90提供接口块,以向调用程序明确外部例程的接口信息。接口块构造形式:INTERFACE接口体END INTERFACE接口体由外部例程头、参数声明和外部例程尾构成。参数名可以和外

14、部例程定义用的参数名不同,也可以相同(将外部例程定义的参数声明部分直接拷贝)。,第四节 接口块,例3-6 在主程序中添加接口块,实现Swop外部例程 PROGRAM ExchangeIMPLICIT NONEINTERFACESUBROUTINE Swop(X,Y)REAL Temp,X,YEND SUBROUTINE SwopEND INTERFACEREAL : A =1, B=5CALL Swop(A,B)PRINT*, A , BEND PROGRAM Exchange 在程序中不需要用EXTERNAL 语句声明外部例程。,必须使用接口块的情况: (1)外部例程具有可选参数; (2)例

15、程用来定义操作符重载; (3)外部函数返回数组或变长字符串; (4)外部例程具有假定形状数组,指针或目标参数; (5)例程作参数; (6)例程重载。除了操作符和例程重载外,其他的只要将外部例程转化为模块例程,就可以免去提供接口块的麻烦,模块在Fortran 90中具有十分重要的作用。,1 构造形式 Fortran 90 有三种程序单元:主程序、外部例程和模块,模块主要是用来在程序单元之间共享数据和操作例程。其构造形式如下:MODULE 模块名 声明语句 CONTAINS模块例程 END MODULE 模块名 ,第五节 模块,例3-7 模块使用MODULE MyUtilsREAL, PARAME

16、TER : PI =3.1415927CONTAINSSUBROUTINE Swop(X, Y)REAL Temp , X , YTemp = XX = YY = TempEND SUBROUTINE SwopEND MODULE MyUtils!模块和主程序在同一文件中,模块在主程序之前PROGRAM MainUSE MyUtils !位于IMPLICIT NONE、声明语句之前IMPLICIT NONEREAL : A = PI, B = PI*2CALL Swop(A, B)PRINT*, A , BEND PROGRAM Main,其中:例程 Swop被转换为模块例程,主程序通过引用(

17、USE)模块,使用 其中的模块例程及数据。,模块例程作为模块的内部例程,其形式和主程序,外部例程包含的内部例程是一样的,只不过模块例程还可以包含自己的内部例程。模块、主程序、模块例程、内部例程和外部例程的关系如下: 其中: Mod-sub 指模块例程,Int-subs指内部例程,Ext-sub 指外部例程。,Module,Mod-sub,Int-subs,Ext-sub,Int-subs,Main program,Int-subs,Ext-sub,Int-subs,Module,Fortran 90 中,数据块程序单元被模块程序单元替代; 模块不仅可供主程序和外部例程引用,还可供其他模块引用;

18、 主程序、模块、外部例程之间的关系如下图所示:,Program A1,Use module A,Module A,Contains,Module subroutineof funotion,Subroutine B,External subroutine,Call B,Contains,End Program A1,Internal subroutine of funotion,主程序、模块、外部例程之间的关系,2 USE语句 模块的引用,一般形式为:USE 模块名模块的引用,其他形式为: (1)不方便直接使用模块实体名,可在程序中重新命名,引用形式为:USE 模块名,重命名列表(新实体名 =

19、原实体名) 例如:模块YourMod有一个例程或者变量YourPlonk,被重新命名为MyPlonk: USE YourMod, MyPlonk =YourPlonk(2)程序只使用模块中的部分实体,采用如下的引用形式:USE YourMod,ONLY :X,Y 这表示只使用模块 YourMod 中的 X 和 Y,冒号后边的实体可被重命名。(3)假如要同时引用多个模块,每个模块都要使用单独的 USE语句,并使 USE语句出现在 IMPLICIT NONE 之前,模块的先后次序无关紧要。,3 PUBLIC 和 PRIVATE 属性 信息隐蔽: PRIVATE,将模块内部使用的实体隐藏,实体访问只

20、限于模块内; 缺省访问属性: PUBLIC,外部程序只使用公共部分的实体。例如:REAL,PRIVATE : X 将实型变量 X 规定为私有的,只能在模块内访问。PRIVATE X,Swop 同时规定变量 X,例程Swop 为私有的。PRIVATEPUBLIC Swop 不带实体列表,除Swop外,其余的模块实体都是私有的。,第六节 例程参数,形参(虚参):相对于实参而言 例程在定义时,列表中的参数只是特定数据类型的占位符, 系统不会为他们分配存储单元,称为形参或者虚参;例程被调用或引用时,列表中的参数被分配一定的存储单 元,并接收外部实参传递进来的值;例程执行完毕,例程中的参数所占有的存储空

21、间被系统自动 释放,空间中保存的参数值也随之消失。例程中的局部变量具有同样的性质: 例程执行时“生存”,例程不执行时“消亡”; 如果声明变量具有SAVE 属性,局部变量就是静态变量, 从例程中被调用到程序结束,一直保存有特定的值。,1 参数传递实参和虚参之间的数据传递(1)引用传递;(2)值传递。Fortran 中参数(包括数组参数)通常是以引用方式传递,即地址传递;在实参为常量和表达式的情况下,参数是以值方式传递;若将变量实参括起来,该实参被转化为表达式,表达式以值方式传递。 例如: CALL Sub(A),B) 其中,(A)是表达式,以值方式传递。 * 引用传递就是将实参的内存地址传递给虚

22、参,例程中虚参的变化会反映到实参中;值传递就是将实参值的拷贝传递给虚参,虚参的变化不会影响实参。* 为确保参数按用户的意愿进行传递,Fortran 90提供了INTENT 属性,例如:SUBROUTINE SUB (X,Y,Z)REAL,INTENT(IN) : X ! 向例程传入数据,拥有该属性的虚参不允许改变REAL,INTENT(OUT) : Y ! 向例程传出数据,对应的实参必须是一变量REAL,INTENT(INOUT) : Z ! 传出/传进数据,对应的实参必须是一个变量END SUBROUTINE SUB,2 参数类型匹配PROGRAM MainIMPLICIT NONEEXTE

23、RNAL SubINTEGER : X=1 !对应实参X声明为整型CALL Sub(X)PRINT* ,X=,XEND PROGRAMSUBROUTINE Sub(A)IMPLICIT NONEREAL,INTENT(INOUT): A !虚参A声明实型,对应实参为XA=A+1 END SUBROUTINE例3-8中,参数类型不匹配,出错!,3 可选参数参数列表可以很长,但并不是所有的参数都需要传递,这种 情况下,可以规定部分或全部参数具有可选(OPTIONAL)属 性。假如参数列表既有必选参数又有可选参数,那么所有的必选 参数必须放在可选参数之前,先必选,后可选。例如一个外部例程Sub 有6

24、个参数,2个必选,4个是可选,调 用程序中的接口块形式如下:INTERFACESUBROUTINE Sub( DumU,DumV,DumW,DumX,DumY,DumZ )REAL DumU,DumV,DumW,DumX,DumY,DumZ OPTIONAL DumW,DumX,DumY,DumZ END SUBROUTINEEND INTERFACE,调用语句 CALL Sub(A,B) !1CALL Sub(A,B,C,D) !2CALL Sub(A,B,DumX=D,DumY=E,DumZ=F) !3对!1,只有必选参数DumU,DumV被传递; 对!2,2个必选参数DumU,DumV

25、和前面的2个可选参数DumW,DumX被传递; 对!3,2个必选参数DumU,DumV和后边的3个可选参数DumX,DumY,DumZ被传递。对!1,!2,没有特别说明,参数传递按照列表顺序; 对!3,可选参数可以不按声明的列表次序进行调用;若不按列表次序调用,可选的虚参名必须被引用(需要提供关键字参数列表)。一旦某个可选参数使用了关键字,其后面的可选参数都使用关键字。例如:CALL Sub(A,B,DumX=D,E,F)是错误的外部例程使用可选参数,需要在调用程序中建立其接口块。,例3-9 可选参数的使用MODULE ModIMPLICIT NONECONTAINSREAL FUNCTION

26、 Func(X,A,B,C)! 计算FUNC(X)=A*X2+B*X+C! A,B,C 不传入,值为 0REAL, INTENT(IN) : X ! X值一定要传入REAL,OPTIONAL,INTENT(IN) : A,B,C ! A,B,C可以不传入REAL RA, RB, RCRA=0.0;RB=0.0;RC=0.0 ! 几个简单的赋值语句放在一行IF ( PRESENT(A) ) RA = A ! 检查可选参数是否存在IF ( PRESENT(B) ) RB = BIF ( PRESENT(C) ) RC = CFunc = RA*X*2 + RB*X + RCEND FUNCTION

27、END MODULEPROGRAM MainUSE ModIMPLICIT NONEPRINT*, Func(2.0, C=1.0) ! F(2)=0*22+0*2+1 = 1PRINT*, Func(2.0, B=1.0, A=2.0) ! F(2)=2*22+1*2+0 = 10END PROGRAM,例程重载:指不同参数列表的例程被赋予相同的名字,例程调用时,编译器会依据所传递的实参类型,按实参和虚参类型匹配的原则,调用或引用相关的例程。例如子程序 Swop(X,Y),其功能是交换2个数的值。其中的2个参数,可以是实数,也可以是整数。 例3-10MODULE ModIMPLICIT NO

28、NE INTERFACE Swop !Swop 是调用时使用的例程名MODULE PROCEDURE SwopReal, SwopIntegersEND INTERFACE,第七节 例程重载,!实型数据交换CONTAINSSUBROUTINE SwopReals(X,Y)REAL, INTENT(INOUT) : X,YREAL TempTemp = XX = YY = TempEND SUBROUTINE !整型数据交换 SUBROUTINE SwopIntegers(X,Y)INTEGER, INTENT(INOUT) : X, YINTEGER, TempTemp = XX = YY =

29、 TempEND SUBROUTINEEND MODULE !主程序PROGRAM MainUSE ModIMPLICIT NONEREAL : A=1.0, B=2.0INTEGER : I=1,J=2CALL Swop(A, B)CALL Swop(I, J)PRINT*,A=,A,B=,BPRINT*,I=,I,J=,JEND PROGRAM,建立重载例程接口块,以调用的例程名命名接口块: INTERFACE SwopMODULE PROCEDURE SwopReals,SwopIntegersEND INTERFACE体的例程作为外部例程,调用程序过程中建立的接口块为:INTERFAC

30、E Swop !相比一般的例程接口块,多了接口名SwopSUBROUTINE SwopReals(X,Y)REAL,INTENT(INOUT) : X,YEND SUBROUTINESUBROUTINE SwopIntegers(X,Y)INTEGER,INTENT(INOUT) : X,YEND SUBROUTINEEND INTERFACE,递归例程:在一个例程体内出现直接或间接调用例程自身的语句,称该例程为递归例程。递归例程的典型例子是计算n!Fortran 90 支持递归,但必须添加RECURSIVE 关键字,在例程是函数的情况下,还须添加RESULT 子句; 在递归函数体内引用函数自

31、身时,用的是函数名(Factorial),赋值给函数时,用的是结果名(Fact) 。,第八节 递归例程,例3-11 递归函数求n!PROGRAM MainIMPLICIT NONEINTEGER IDO I=1, 10PRINT*,I,!=, Factorial(I)END DOCONTAINSRECURSIVE FUNCTION Factorial(N) RESULT(Fact)INTEGER Fact, NIF(N = 1)THENFact = 1ELSEFact = N * Factorial( N-1 )!引用的是函数名Factorial!赋值采用的是FactEND IFEND FUC

32、TIONEND PROGRAM,递归算法简单直观,容易编写程序,但递归算法执行效率低,原因是递归包含递推和回归2个过程,系统分别要进行进栈和出栈操作。例如求5!, 递推过程:5! = 54!4! = 43!3! = 32!2! = 21!1! = 1 回归过程:2! = 21! = 23! = 32! = 64! = 43! = 245! = 54! = 120所以递归程序设计除了要找出递归表达式外,还要确定递归的终止条件(如:1!=1),无限递归没有任何意义;在递归子程序下,同样须添加RECURSIVE 关键字。,PROGRAM MainIMPLICIT NONEINTEGER F, IDO

33、 I = 1, 10CALL Factorial( F, I )PRINT*,I,!,FEND DOCONTAINSRECURSIVE SUBROUTINE Factorial( F, N )INTEGER F, NIF (N = 1) THENF = 1ELSECALL Factorial( F,N-1)F = N * F !该递归子程序的关键,将赋值语句 F = N * F 置于递归调用语句之后END IFEND SUBROUTINEEND PROGRAM,小 结 (1)模块化程序的设计思想:将大的程序分解为若干个功能单一的例程。例程可以是包含在程序单元中的内部例程,也可是作为独立程序单元

34、使用的外部例程。Fortran90程序单元包括主程序、外部例程和模块。其中,主程序和外部例程包含的内部例程不能再包含内部例程,而模块中的模块例程允许包含其内部例程,即例程嵌套。 (2)若外部接口信息较简单,可以通过EXTERNAL关键字将例程声明为外部的,以防止调用程序使用和外部例程同名的标准例程;若外部接口信息复杂或某些特殊情况下,必须建立接口块,使编译器可以产生正确的调用。编译器自动为标准例程、内部例程和模块例程提供显式接口。 (3)模块通常含有全局变量和通用例程,专供其他程序单元使用。在通过USE语句引用模块时,可只使用其中的部分实体,还可为模块实体重命名。模块中实体的缺省访问属性为PU

35、BLIC,PRIVATE属性则将实体访问属性限定在模块内。,(4)实参和虚参间的数据传递必须类型匹配。实参变量以引用方式传递,常量和表达式则以值方式传递,编程中应通过INTENT属性明确规定参数的传递方式。Fortran 90 允许声明可选参数,允许以关键字形式使用可选参数。 具有可选参数的外部例程,使用时需在调用程序中建立其接口块。 (5)重载例程通过有名接口块来规定,重载的具体例程可以是模块例程,也可以是 外部例程,但要求规定不同的接口体。 (6) Fortran 90支持递归,允许例程直接或间接调用自身。但前提是声明例程时要添加RECURSIVE 关键字,在函数例程情况下,还要添加RESULT子句。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报