返回介绍

6.2.4 其它过程

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

a) 内部过程

内部过程是包含在程序单元里的 CONTAINS 结构中的函数过程和子程序过程,只有包含它们的程序单元才能够使用该过程。CONTAINS 结构标志着程序中可执行部分和程序包含的任意内部子程序之间的分界,它将内部过程与主过程分开。如果程序包含内部子程序,则必须有 CONTAINS 结构。在 F77 中,—个源文件可以包含一个主程序和几个分别独立的函数或子程序(相当于 F90 中的外部过程)。在 F90 中,可以将若干个过程用 CONTAINS 结构包含在主程序里,它们与宿主程序单元共享变量名。而且,外部过程等其它程序单元都可以有自己的内部过程。

例:program internal

real a,b,c

call find

print *,c

contains

subroutine find

read *, a,b

c=sqrt(a**2+b**2)

end subroutine find

end

使用内部过程的规则:在宿主中不要定义子程序名和函数名的类型,也不能指定它们是有 EXTERNAL 属性。宿主中的变量名和数组名等在内部过程中有效,有相同的数值。但同一名若在内部过程中又进行了类型声明,则此名被视为其过程中的独立变量,无相同的数值。内部过程中也可引用另一内部过程。

因此,写成内部过程的子程序有一个重要特征,即它们通常没有说明语句。它们使用到的变量等实体的说明已统一出现在主程序说明部分中,它们的变量与主程序同名变量的值是相通的。主程序内可以直接引用内部过程变量的值,或赋之以值,也即主程序内定义的变量是全局的,作用于以 PROGRAM 语句到 END PROGRAM 语句之间的所有场合。内部过程的第二个重要特征是它们一般没有哑元。主程序调用时也不需要哑实结合,因为如果内部过程的变量在主程序中被说明,它们就可以直接引用,无需通过哑实结合来传递值。调用内部过程时只要简单地写一个过程名。这是内部过程与外部过程的很大区别。在特殊情况下,内部过程也可保留由自己单独说明的少量哑元。如果变量是在内部子程序中单独说明的,它只是局部变量,作用域只能是局部的,对其主程序的其它部分不起作用。主程序调用时仍需通过哑实结合来传送数据。

b) 递归过程

F90 允许过程递归调用,即在过程内又调用过程自身,这种过程称为递归过程。递归过程包括递归函数和递归子程序,它只是外部过程或内部过程的一种特殊情况,则过程定义语句的前缀中必须出现关键字 RECURSIVE。递归调用时,过程直接或间接引用自身或由该过程中的 ENTRY 语句定义的函数。

几乎所有递归程序都可用一般程序替代。但递归程序简单明白,清晰易懂,有利于阅读与维护。某些问题当用一般程序编写显得太复杂时,可以用递归程序来实现。递归程序的缺点是效率低,比起一般程序来,在 CPU 时间与占用内存等方面的开销都要大得多。因此,要根据具体问题的需要,决定是写成递归程序还是一般程序。

下面是一个经典的递归例子,求 N 的阶乘 N!。若用递归过程编程,可使用程序简单明了。因为 N!=N(N-1)!=…=N(N-1)(N-2)…2*1,如果设 N!的函数过程名为 FACTORIAL(N),则求(N-1)!调用 FACTORIAL(N-1),如此递推下去,直到求得 FACTORIAL(1)=1!为止。然后,系统又自动地一层层向上回归,最后求得 FACTORIAL(N) 的值。上述递推与回归的过程即为递归过程。 [e_624_01.f90]

调用递归函数的主程序写法与非递归函数的写法没有什么两样,只是因被调用的是递归函数,最好写明递归函数接口块。在具体调用时,过程只需调用一次,填进实元即可,各级递归调用与回归会自动进行。

为了加深对递归过程的理解,再来看一个关于汉诺塔(The Tower of Hanoi) 的例子。据传说,在汉诺有座寺庙,里面有一个举行仪式的塔,它由 3 个柱子和柱子上的 64 个金盘组成。这些金盘大小不一,在修建寺庙时,64 个金盘放在 A 柱子上,并且最大的在最下面,往上依次减小,最小的在最上面。

        |                    |                   |
       (1)                   |                   |
      (_2_)                  |                   |
     (__3__)                 |                   |
    (___4___)                |                   |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        A                    B                   C

庙里的和尚的工作就是移动这些金盘,直到 64 个金盘被移到 B 柱子上,到那时就是世界末日来临之时。移动这些金盘必须遵循两个规则:把金盘从一个柱子移到另一个柱子上,一次只能移动一个金盘;一个大的金盘永远不能压在一个较小的金盘上面。这个问题可以用递归子程序来解决。其思路是:为了将上面 4 个盘子从 A 移到 B,需先将上 3 个盘子从 A 移到 C,然后将第 4 个盘子从 A 移到 B,再将上 3 个盘子从 C 移到 B。为了完成 3 个盘子的移动,又需先完成 2 个盘子的移动....。这个递归过程可用递归子程序完成。 [e_624_02.f90]

例:求二项式的展开系数。已知二项式的展开式为:,共有 N+1 个系数,其系数呈杨辉三角形形式:

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

1 5 10 10 5 1

这些系数间有明显的规律,即除了首尾两项系数为 1 外,当 N>1 时,N 阶中间各项系数是 N-1 阶的相应两项系数之和,也即如果把 N 阶的系数列为数组 C(I,N),则除了 C(1,N) 和 C(N+1,N) 恒为 1 外,C(I,N)=C(I,N-1)+C(I-1,N-1)。当 N=1 时,只有两个取值为 1 的系数 C(1,1) 和 C(2,1)。因此,任意 N 阶的系数可由 N-1 阶的系数求得,直到 N=1 为止,可写成递归子程序。[ e_624_03.f90]

c) 类属过程

类属过程是过程的一种,它允许用不同类型的实元与同一个哑元结合,也即放宽了哑实元结合时类型必须一致的条件。在内在基本函数中,已经有许多类属过程,例如求绝对值的函数 ABS(X),哑元为 X,与它结合的实元可以是整型、实型与复型,也即使用同一个过程名 ABS,调用函数 ABS(-1)、ABS(-1.)、ABS(-1.,1.) 都是合法的,并且一般来说,其函数值类型就取实元的类型,如上述前两个引用的函数值是 1 与 1.0。

编写类属过程的方法是先编写着干个功能相同的过程,它们分别以整型、实型、复型等作哑元类型。而后在主调程序中编写接口,为接口取一个统一的名,接口内分别列出哑元类型不同的过程说明部分语句。这个统一的接口名就是类属过程名,可以在后面执行部分中用不同类型的实元作哑实结合。

例如,要编写求两数之和的类属函数时,分别编写哑元是实型和整型的函数:

FUNCTION SUM_REAL(A,B) RESULT(SUM_REAL_RESULT)

REAL :: A,B,SUM_REAL_RESULT

SUM_REAL_RESULT=A+B

END FUNCTION SUM_REAL

FUNCTION SUM_INTEGER(A,B) RESULT(SUM_INTEGER_RESULT)

INTEGER :: A,B,SUM_INTEGER_RESULT

SUM_INTEGER_RESULT=A+B

END FUNCTION SUM_INTEGER

现在把这两个函数过程综合成一个类属函数,类属函数名取为 MY_SUM,在主调程序应写明如下接口:

PROGRAM SUMMATION

INTERFACE MY_SUM

FUNCTION SUM_REAL(A,B) RESULT(SUM_REAL_RESULT)

REAL :: A,B,SUM_REAL_RESULT

END FUNCTION SUM_REAL

FUNCTION SUM_INTEGER(A,B) RESULT(SUM_INTEGER_RESULT)

INTEGER :: A,B,SUM_INTEGER_RESULT

END FUNCTION SUM_INTEGER

END INTERFACE

IMPLICIT NONE

REAL :: X,Y

INTEGER :: I,J

READ *, X,Y,I,J

PRINT *, MY_SUM(X,Y),MY_SUM(I,J)

END PROGRAM SUMMATION

d) 多层调用

主程序调用过程,称为第一层调用。被调用的过程可能也要调用更小的下属过程,这种调用称为第二层调用,依此类推。这样逐层调用下属过程,称为多层调用。

在多层调用中,函数可以调用下属的函数过程或子程序过程,子程序过程也可调用下属的函数过程或子程序过程,并不要求被调用的过程与自己的性质相同。多层调用中的程序控制流程是:控制从主程序(第 0 层) 的第一个执行语句开始,顺次往下执行,当遇到调用第一个第一层过程时,控制转入该过程的第一条可执行语句,并把第 0 层中的实元值赋给该第一层过程的哑元,顺次往下执行。如果再遇到调用第二层过程的语句,控制又转入该层过程的第一条可执行语句,并把第一层的实元值赋给第二层的哑元。如果最下一层子程序内没有调用语句,则控制遇到过程结束语句时,返回上层过程调用语句后,继续执行尚未执行的语句,直到第一层过程结束语句,控制再返回到主程序的调用语句,继续执行完后面的语句。直至遇到主程序结束语句,结束控制。

因此,这个控制机制是按顺序进行的,这一点也可由执行 Visual Fortran 的 Debug 功能时,跟踪过程的顺序执行和变量的变化可见。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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