- 第一章 Fortran 语言程序设计初步
- 第二章 改变程序流程
- 第三章 循环结构
- 第四章 数据结构
- 第五章 数组
- 第六章 过程和模块
- 第七章 输入输出和文件
6.2.2 外部过程
外部过程是程序员编写的函数或子程序,它独立于主程序外部。外部过程可以单独以源文件存储和编译,也可以包括在主程序源代码的 END 语句后。外部过程本身也可以包含有内部函数或内部子程序。
a) 子程序
子程序以 SUBROURTINE 语句开始,END 语句结束的过程。其一般形式为:
[前缀]SUBROUTINE 子程序名[(哑元列表)]
...
END [SUBROUTINE[子程序名]]
其中,哑元可以是变量名、数组名、过程名、指针名等均可作为哑元。哑元表内列出本程序体内要用到的全部哑元,列在括号内,彼此用逗号分隔(哑元都要在说明语句中说明类型)。前缀是 F90 中新增的,它可以是:类型说明 [关键词]或关键词 [类型说明]。关键词是下面之一:RECURSIVE(F90),PURE(F95),ELEMENTAL(F95)。RECURSIVE 表示过程时可以直接或间接地调用自身,即递归调用,其过程是递归过程。
子程序名只是用来作标识,不代表任何值。哑元列表是子程序与调用单元之间进行数据传送的主要渠道,当有一个以上哑元时,它们之间用逗号隔开,如果没有哑元,则一对括号可以省略。从 SUBROUTINE 语句后面一直到 END 语句则是子程序体。它的说明部分应包括对哑元和子程序中所用变量、数组等的说明,它的执行语句部分完成运算和操作功能。其中的 END 语句或 RETURN 语句使执行流程返回到调用单元。
哑元表中的哑元个数,理论上不受限制,但从软件工程观点看,不宜过多,一般不应超过六、七个。如果太多,意味着该子程序的算法较复杂,应该把该过程再分解为几个子功能分别编写成几个子程序。
调用子程序时必须用一条独立的 CALL 语句,其形式为:CALL 子程序名[(实元列表)]
例:读入 3 个整数,按大小順序重排。
INTEGER :: i,j,k
READ *, i,j,k
IF(i<j) CALL swap(i,j)
IF(j<k) CALL swap(j,k)
IF(i<j) CALL swap(i,j)
PRINT *,i,j,k
END
!
SUBROUTINE swap(p,q)
INTEGER :: p,q,r
r=p;p=q;q=r
RETURN
END
调用子程序时的实元是和哑元相同类型的变量、数组元素、数组和常数。当用 CALL 语句予以调用时,哑元和实元才按列表顺序一一对应,取得同一数值。但使用常数时要避免下例中的常数替换式,否则将导致不可预料的错误,应尽可能用变量作实元。
例:SUBROUTINE bai(x)
REAL :: x
x = 2. * x
END
s=1.
CALL bai(1.)
PRINT*,s
END
b) 函数
自定义函数和子程序比较相近,共同的特征是作为过程的集合,子程序的很多规则也可应用。两者的不同之处在于函数返回一个值,且通常不改变哑元的值,因此,它代表数学上的一个函数。而子程序通常完成一项更为复杂的任务,通过变元或其他手段返回几个结果。函数的一般形式为:
[前缀] FUNCTION 函数名([哑元列表])[RESULT(结果名)]
...
END [FUNCTION 函数名]
如果没有哑元,则哑元表是一个空括号。RESULT 是关键字,要照写,后面括号内的变量名就是函数计算的结果值。函数结果变量名有值,必须在说明语句中说明类型,在程序执行部分中至少赋值一次。在引用时,它的值就是函数值。函数结果名不可列入哑元表。如果没有结果名,则函数名就是结果名。F90 中之所以增添了结果名功能,就是为了区别函数字面上的名称(函数名) 和实际运算结果变量的名称。
函数调用与调用内在函数形式一样,在主调程序任何处,作为表达式的一项写下:函数名(实元表) 就完成调用。如果函数无哑元,则调用形式是在表达式中写上函数名,后跟空括号:函数名()。系统在该位置上返回以该实元为自变量的函数结果值,参加表达式的运算。
例如,下面都是合法的 FUNCTION 语句:
FUNCTION FUN1
FUNCTION FUN2()
INTEGER FUNCTION FUN3(A,B)
RECURSIVE FUNCTION FUN4(X,Y,Z)
REAL RECURSIVE FUNCTION FUN5(M,N) RESULT(R_FUN5)
例:将一个 4 字节的整数用 16 进制表示出来。 [e_622_01.f90]
FUNCTION hex(n)
CHARACTER(LEN=8) :: hex
CHARACTER(LEN=1) :: h(0:15)=(/'0','1','2','3','4','5','6',&
'7','8','9','A','B','C','D','E','F'/)
INTEGER :: n,j,nn
hex = ' '
DO j=8,1,-1
nn=n/16
hex(j:j)=h(n-16*nn)
IF(nn==0) EXIT
n=nn ! 将 n 指定为 INTENT(IN) 将导致错误
END DO
END FUNCTION
PROGRAM main
CHARACTER(LEN=8)::hex !函数名的类型在调用单元也须加以声明
INTEGER::i
PRINT *,'Input a positive integer, or negative one to stop:'
DO
READ *,i; IF(i<0) EXIT
PRINT *,hex(i)
END DO
END
在本例中,函数值是赋给函数名的,如果要将值赋给非函数名的另一结果名,则
FUNCTION hex(n) RESULT(hx)
CHARACTER(LEN=8)::hx
…
hx(j:j)=
但在此例中就没有必要用函数了,可以用子程序,如:
SUBROUTINE hex(n,hx)
CHARACTER(LEN=8),INTENT(OUT)::hx
例:分形图形的计算。分形(fractal) 是 Mandelbrot 将自然界的复杂图形(如海岸线,树叶形,雪花结晶型) 进行数学理想化后提出的一种概念,其核心是图形的任意细小部分都与图形的整体具有自相似性,这种图形的维数不是整数,而是有分数维的。一个典型例子是 Koch 曲线,它有雪花形,可通过对一段直线反复进行某一简单操作而得到,把这个过程用数学语言描述,即为在复空间定义的一种简单迭代过程,它是一个图形的缩小映射,从而产生自相似曲线。一种简单的缩小映射是:
迭代过程是用作为初始值代入得
,
然后反复将得到的值作为初始值代入得一序列复数值
,
,
,
,…。然后将此序列复数在复空间中绘出即得到相似曲线。程序中要计算迭代到 n 阶所需的迭代函数,其个数为
:用 0 记
,01 记
1 阶:0,1
2 阶:00,01,10,11
3 阶:000,001,010,011,100,101,110,111 [e_622_02.f]
c) EXTERNAL 属性和哑过程
指定 EXTERNAL 语句或属性说明实元实际上是外部过程。其一般形式为:
类型定义语句:类型,EXTERNAL :: 外部函数名[,外部函数名]…
或 EXTERNAL 语句:EXTERNAL [外部函数名][,子程序名][,块数据名]…
哑元也可以是一个过程,这时作为哑元的过程称为哑过程。只有在多层调用(至少两层调用)时才能用哑过程。如果要用外部过程名作实元,则过程名必须出现在一个 EXTERNAL 语句中,或被该作用范围中的一个类型语句指明具有 EXTERNAL 属性,或在该作用范围中被一个接口块 INTERFACE 声明为一个过程。这就是说,在主调程序中,除了把一个实际存在的过程名作实元与哑过程名结合外,还需对该实过程名作特别说明,以便让编译系统明白,该实元不是一般的简单变量,而是一个函数(或子程序),或者用接口块来通知编译系统。
例:REAL FUNCTION CALCULATE(A,B,FUNC)
EXTERNAL FUNC
REAL,INTENT(IN) :: A,B
REAL F,X
…
F=FUNC(X) !调用自定义的外部函数
…
END FUNCTION CALCULATE
程序中的 EXTERNAL 语句说明函数 FUNC 是外部函数,也可用类型声明语句说明函数 FUNC 具有 EXTERNAL 属性,把程序改写为:
例:REAL FUNCTION CALCULATE(A,B,FUNC)
REAL EXTERNAL :: FUNC
…
也可以用显式的接口块来代替 EXTERNAL 语句,把上面的程序改写为:
例:REAL FUNCTION CALCULATE(A,B,FUNC)
INTERFACE
REAL FUNCTION FUNC(X)
REAL,INTENT(IN) :: A,B
END FUNCTION FUNC
END INTERFACE
REAL F,X
…
F=FUNC(X) !调用自定义的外部函数
…
END FUNCTION CALCULATE
例:采用梯形公式近似求[a,b]区间上函数 f(x) 的定积分
FUNCTION INTEGRAL(F,A,B,N) RESULT(INT_RES)
IMPLICIT NONE
REAL :: INT_RES
INTERFACE
FUNCTION F(X)
REAL :: F,X
END FUNCTION
END INTERFACE
REAL,INTENT(IN) :: A,B
INTEGER,INTENT(IN) :: N
REAL :: H,SUM
INTEGER :: I
H=(B-A)/N
SUM=(F(A)+F(B))/2
DO I=1,N-1
SUM=SUM+F(A+I*H)
END DO
INT_RES=H*SUM
END FUNCTION INTEGRAL
在上面的函数 INTEGRAL 中,哑元 F 被一个接口块说明为一个过程,因此它是一个哑过程。在调用该过程时,必须对该哑元提供一个另外定义的函数过程作为实元与之结合,同时也需要写上接口块。
当用 EXTERNAL 语句说明 SIN,COS 等名字时,它们被解释为自定义函数,而非内在函数(三角函数)。
d) ENTRY 语句
ENTRY 语句允许在特定的可执行语句中插入外部过程或模块过程。内部过程不能有 ENTRY 语句。使用语句的目的是,把多个有条件进入过程的相关函数或子程序组织起来。它的形式和用法与函数和子程序类似:语句包括一个入口点的名称,一个可选哑元列表,一个可选的 RESULT 结果名(在函数的情况下)。一个过程可以有一个或多个 ENTRY 语句。F90 的块结构如 CASE 语句所实现的控制流方式比通过 ENTRY 方式为好,故 F90 中并不提倡用 ENTRY 语句。
函数过程中的 ENTRY 语句:定义了另一个函数,函数名由 ENTRY 语句指定,结果名即函数名或由 RESULT 指定。
例:计算双曲三角函数 sinh,cosh 和 tanh 的函数过程。
REAL FUNCTION TANH(X)
TSINH(X)=EXP(X)-EXP(-X)
TCOSH(X)=EXP(X)+EXP(-X)
TANH=TSINH(X)/TCOSH(X)
RETURN
ENTRY SINH(X)
SINH=TSINH(X)/2.
RETURN
ENTRY COSH(X)
COSH=TCOSH(X)/2.
RETURN
END
函数过程中的 ENTRY 语句,定义了另外两个函数,函数名由 ENTRY 语句指定。
例:PROGRAM TEST
...
CALL WAHAHA(A,B,C,D)
...
END
SUBROUTINE HAHA(X,Y,Z)
...
ENTRY WAHAHA(Q,R,S,T)
...
END SUBROUTINE
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论