返回介绍

6.2.2 外部过程

发布于 2025-03-08 16:28:36 字数 7376 浏览 0 评论 0 收藏 0

外部过程是程序员编写的函数或子程序,它独立于主程序外部。外部过程可以单独以源文件存储和编译,也可以包括在主程序源代码的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文