收藏 分享(赏)

Fortran程序设计第13章 过程及其通讯.doc

上传人:gnk289057 文档编号:7219253 上传时间:2019-05-10 格式:DOC 页数:44 大小:274KB
下载 相关 举报
Fortran程序设计第13章  过程及其通讯.doc_第1页
第1页 / 共44页
Fortran程序设计第13章  过程及其通讯.doc_第2页
第2页 / 共44页
Fortran程序设计第13章  过程及其通讯.doc_第3页
第3页 / 共44页
Fortran程序设计第13章  过程及其通讯.doc_第4页
第4页 / 共44页
Fortran程序设计第13章  过程及其通讯.doc_第5页
第5页 / 共44页
点击查看更多>>
资源描述

1、第13章 过程及其通讯如果说一个语句可以看成是一条指令,那么在 FORTRAN 95 语言里,一个具有一定结构的计算任务可以对应的最小程序单位,就是一个过程。从专致于科学计算的初衷出发,FORTRAN 在语言的结构层面上,可以认为是面向过程的一种语言,尽管在编程语言流行面向对象的今天,面向过程显得有点落伍,但却有效地适用于描述计算任务,当然随着现代技术对于计算的要求越来越复杂与庞大,FORTRAN 也不是一味地守旧,可以预计 FORTRAN 的下一个版本,就会具有适应大型软件工程的要求的面向对象的语言特性。FORTRAN 95 语言作为一种语言的主要特点,可以说就体现在它的过程这个主要的程序结

2、构上。而从我们程序编写者的角度来看,能否把一个完整的算法转写成一个完整的程序,关键也就在于能否构造出一些恰当的过程来作为程序的基本单位。特别是对于大型的 FORTRAN 95 程序,需要把它分解为好几百个过程是很常见的,这时如何恰当地使用过程来构建整个程序,可以说是编写程序最主要的工作。因此如何构建过程,如何根据需求运用过程,然后在不同的过程之间建立必要的通讯,是值得我们非常仔细地加以讨论的。本章的主要任务即在于此。13.1 过程的分类与性质由于过程具有多方面的功能与属性,因此对于过程的分类可以有多种方式。下面我们首先讨论过程的各种分类方式及其相应的分类意义,然后我们讨论有关过程引用的要点与相

3、应概念。13.1.1 过程的分类过程可以有几个不同的分类方式,每种分类方式反映了过程的某个方面的特性。下面分别予以讨论。从形式上根据调用的方式的不同,FORTRAN 的过程分为两类: 函数;函数返回一个可以供表达式使用的值。因此函数的调用总是作为一个表达式的算元,函数的值也就是相应表达式算元的取值。函数调用时直接使用函数的名称和它的变量,或者作为一个自定义运算,它返回值之后,它的功能就算完成,不对程序产生后效,当然,FORTRAN 标准也不绝对禁止使用产生一定后效的函数。 子例行程序。子例行程序的调用必须使用 CALL 语句,或者是作为一个赋值。子例行程序的功能主要在于产生一定的后效,例如改写

4、一些变元,以及全局变量,或者执行某些输入输出。函数与子例行程序的一般规则如下: 函数与子例行程序在调用方式上的差别实际上来源于一个函数总是和一个相应的函数结果值相关联,只要运行或者调用一个函数,总会得到相应的函数结果值,而子例行程序却没有相应的概念,这就使得子例行程序只能依靠使用专门的 CALL 语句来调用,而既然函数总是能够给出一个函数值来,就可以直接把它作为表达式的算元来调用。 函数结果可以是任意的数据类型,例如派生类型,或者是取数组值都可以。 如果在 FUNCTION 语句当中使用 RESULT 属性,那么就可以给结果一个与函数定义里面的函数名称不同的名称,这主要应用于直接调用自身的递归

5、函数。 在除了模块以及数据块程序单位之外的程序单位的说明部分还可以使用一种由一个语句组成的函数,称为语句函数,不过它的功能完全可以由下面的内部过程来实现,因此已经过时。再从过程与其他程序单位的关系的角度来分类的话,过程也可以分为如下两大类: 外部过程;顾名思义,外部过程就是处于任何的程序单位的外部,它作为一个孤立的过程,可以单独构造,单独编译,单独使用,而可以完全独立于任何的过程与程序单位。甚至还可以是用其他的语言编写的,例如常见的 C 语言。当然,一个外部过程还是可以通过变元列表,模块,公用块等来共享数据与过程之类的信息。 内部过程。内部过程总是定义在一个程序单位内部,该程序单位就称为它的宿

6、主。当一个过程出现在它的宿主内部的时候,总是出现在宿主的 CONTAINS 语句和 END语句之间。一个内部过程对于其宿主而言,总是局域的,而且通过宿主关联继承了宿主的数据环境。特别的,如果一个内部过程的宿主是一个模块,那么这种内部过程被单独称为模块过程。 模块过程。模块过程同样必须出现在其宿主模块里面的 CONTAINS 语句和 END 语句之间。一个模块同样可以通过宿主关联继承宿主模块的数据环境。 但与一般内部过程不同的是,模块内的模块过程可以具有 PUBLIC 属性,即从模块外部可以直接访问具有 PUBLIC 属性的过程;当然模块过程也可以具有 PRIVATE 属性,使得模块外部不能访问

7、该过程。注意在早期 FORTRAN 版本里面出现过语句函数的概念,它是出现在程序单位的说明部分的单语句定义的函数,不能 够出现在模块或数据块程序 单位当中。由于它的功能可以完全由内部过程替代,因此语句函数的概念已 经过时。一般由多个过程组成过程子程序。 对于内部过程来说, 组成 过程子程序的多个过程可以通过宿主关联而获得对宿主数据环境的共享;而对于外部过程来说,当一些外部过程要组成过程子程序的时候,由于缺乏 类似的关联机制,它 们的数据共享 问题是通过所谓过程登录机制来解决的。所谓过程登录是通过运用 ENTRY 语句来说明一个过程与一个过程子程序的关联关系的。每一个 ENTRY 语句就给出了一

8、个过程,所有这些过程就可以共享该过程子程序的数据环境。例如两个不同的函数 SIN 和 COS 就可以通过这种方式使用同一个外部子程序,因 为SIN 和 COS 具有如下的关系:cos()in)2xx。尽管对于内部过程来说,过程登 录是不必要的,但也可以 应 用于模块过程。根据过程对于变元的作用效果来分类,则可以把过程分为纯过程与非纯过程。所谓纯过程就是在执行对变元的作用之后,不会产生后效,这对于程序按照序列进行一般是没有什么影响的,但是对于程序的并行执行,却往往会给程序带来不确定性,因此就需要对一般的过程加以约束,使得其完全没有后效,从而得到纯过程。例如过程 A 和 B,都具有对 X 进行赋值

9、的后效,设 A 把 X 赋值为 1.0,而 B 把 X 赋值为 2.0,那么如果 A 和 B 这两个过程是并行完成的,那么 X 的具体取值就变得不可预料,因此需要对过程 A 和 B 进行一定的约束,使得它们成为纯过程。如果从过程作用于数据对象的方式来进行分类,那么过程可以分为逐元过程和变换过程。 逐元过程,即过程的本来属于标量形式的变元可以被赋予数组,这时过程的作用方式是对数组的每个元素进行单独的运算,得到的所有结果再构成一个与变元数组相同形状的数组,就称为该过程作用于数组后的结果。 变换过程,即过程的变元本来就是数组,对数组的作用方式是把数组作为一个整体进行变换,而不是逐个地针对元素进行。因

10、此逐元过程既可以标量变元来引用,也可以通过数组变元来引用,只是当通过数组变元来引用的时候,实际上就是通过标量数组元素来引用,只不过需要引用多次引用的次数等于数组元素的个数。逐元过程总是纯过程,因为逐元过程不产生后效。如果从过程的来源来看,过程还可以分为固有过程和自定义过程两类。所谓固有过程,也称为内建过程,即 FORTRAN 标准已经建立好了的过程,一共包括115 个固有过程。这意味着任何的 FORTRAN 实现,都必须能够直接提供这些固有过程。固有过程的名称在程序单位的任何地方都是可以直接引用的,唯一的例外,就是当使用了显式的 EXTERNAL 语句或过程界面块的时候,固有过程的通用属性就可

11、能被扩充,即固有过程的名称也可能用来引用非固有过程,这时该名称所表示的固有过程反而成了一种默认值。大部分的固有过程同时还是逐元的,自定义过程也可以被定义为逐元过程,这样就极大地增强了 FORTRAN 的数组计算能力。13.1.2 过程的引用所谓过程的引用就是使得过程的名称出现在程序的恰当位置,当程序运行到该位置时,过程名称能够导致程序转入执行该过程。过程的引用也可以称为调用或访问,在本书当中,这几个词汇是根据一般用词习惯而交替使用的。过程的引用分为两种形式,即子例行程序的引用和函数的引用。 子例行程序的引用是依靠一个单独的 CALL 语句,某些时候也可以使用赋值语句的形式。 函数的引用是作为表

12、达式的一部分,即采用函数名称及其变元列表作为表达式的项出现,某些时候,函数引用也可以采取一元运算或二元运算的形式,其变元作为表达式的算元。过程的引用本质上就是通过变元来进行数据的传递或通讯,在进行过程的引用过程当中,变元根据它所行使的功能,还可以分为三种:实元,哑元,替代返回变元。 实元在过程的引用当中,所谓实元就是在程序执行的时候需要实际采用的数据对象。例如实元在作为变量的时候,可以在每次引用当中取不同的值,因为作为实元,是直接面向具体取值的。实元的取值可以是输入值,也可以是过程运行之后返回的值,或者两者都是。 哑元所谓哑元就是一个名称,通过该名称过程内部就能够访问到相应实元。因此哑元在过程

13、被定义的时候就已经给出,在过程进行计算时,它就充当变元名称。一旦在程序运行时引用该过程,那么引用的实元就通过变元关联而与相应哑元建立关联关系。如果过程界面是显式的,那么在引用过程时,就可以直接使用哑元名称作为实元关键词。过程定义里面的哑元名称还可以用来指称一个过程,这实际上就是一个过程引用。因为与该哑元具有变元关联关系的实元名称就是一个过程的名称,不过这个被关联的过程不能是内部过程,语句函数,已经具有类属性的过程。 替代返回变元所谓替代返回是只在子例行程序里面出现的一种变元的功能,通过替代返回,就可以中断程序的顺序执行次序,控制程序的执行分支到其他某个位置,替代返回里面的实元就是分支目标语句的

14、标签,这种分支方式经常用于从子例行程序里面出错退出的情形,不过在现代的 FORTRAN 控制结构里面,具有更合理的控制方式,因此这种分支方式已经过时。过程通过变元进行通讯的途径就是关联,过程关联的形式有 4 种:变元关联,宿主关联,使用关联,以及共享存储关联。 变元关联变元关联是过程之间进行数据通讯的主要方式。在 13.7 节会详细地加以讨论。 宿主关联宿主关联是建立在一个过程与其所属的程序单位之间的数据关联关系。一个宿主程序单位可以是一个主程序,模块程序单位,外部过程,模块过程。这些宿主程序单位里面所有被允许访问的数据对象都可以通过宿主关联而被其包含的过程访问到。对于宿主关联的详细讨论见第

15、15 章。 使用关联一个过程可以通过一个 USE 语句和该 USE 语句所在的程序单位建立使用关联。通过使用管关联,过程可以和模块共享数据对象,也可以按照程序编写者的意愿,通过 ONLY子句来隐藏某些数据对象。对于使用关联的详细讨论见第 12 章。 共享存储关联有关存储关联的详细讨论见第 7 章和第 15 章。在组织程序时,除了在不同过程之间进行通讯之外,过程自身还可以构成递归的结构。即一个局部变量得到初始化之后,尽管它出现在递归的每一层里面,但是其初始化值只能够在该递归结构开始运行时有效地使用一次,因此实际上等价于对于变量使用了 SAVE 属性,使得每一层的递归都保留了它的变量值,可以说是一

16、种数据的隐式保留属性。一个过程无论是进行直接的递归还是进行间接的递归,都必须在过程定义里面的FUNCTION 语句或 SUBROUTINE 语句里面加上关键词 RECURSIVE,这个关键词的功能就是能够实现编译器对于过程调用的优化。过程的引用并不涉及过程的具体功能,也就是对于过程的内部计算过程,在引用时是不需要考虑的,这样就可以把所有涉及到过程引用的信息集中在一起,包括过程及其变元的名称与属性,这就构成了所谓过程的界面。顾名思义,也就是一个过程与外部进行通讯的层面。如果一个过程的这些信息对于一个引用它的程序单位来说,不是显式的,那么该过程就被称为具有隐式界面。这时,引用它的程序单位就只能依据

17、过程名称以及过程引用里面的实元的属性来确定过程的有关信息,同时有关的哑元与实元的数据匹配都需要程序编写者预先规划好。如果过程界面是显式,那么相关的数据匹配工作就可以完全由编译系统来完成检查。在很多的引用情形下,都一定要求被引用的过程具有显式界面,例如: 作为数组片断的实元; 指针变元; 可选变元; 关键词调用; 自定义运算; 自定义赋值; 自定义过程的逐元调用; 自定义类过程。 各种类型的过程的界面形式的显式或隐式的情况如下: 固有函数,内部过程,模块过程都是显式的界面,自定义过程也可以具有显式界面。所有固有函数的显式界面信息都包含在任何一个 FORTRAN 编译器内部,在对固有函数进行任何引

18、用时,系统都可以根据实元的类型来检查与匹配相应的固有函数,从而得到一个正确的引用。因此对固有函数的引用可以直接使用函数的类名称以及关键词。内部过程和模块过程在它的定义里面就要求具有显式界面,因此对这两种过程的引用同样是安全可靠的。 外部过程和语句函数都是隐式的界面。不过在引用外部过程时,可以在引用程序单位里面为所引用的外部过程提供一个界面块,这样就等价于该外部过程具有了一个显式的界面。有关界面的详细讨论见 13.8 节。正是由于引用只与过程的界面有关,因此被引用的过程可以是使用其他语句编写的,例如 C, C+等,不过使用非 FORTRAN 的过程可能移植性不太好,因为在不同的系统之间,其他的语

19、言可能具有不同的过程通讯机制。13.2 子例行程序的运用一个子例行程序就是一个完备的能够执行一定的计算任务的程序单位。它由以下五个部分构成: 初始的 SUBROUTINE 语句; 说明部分; 计算执行部分; 某些内部过程; END 语句。当一个子例行程序被调用时,从它的第一个可执行结构开始运行,它通过变元关联,使用关联,宿主关联以及存储关联与外部进行数据通讯。下面分别讨论子例行程序的定义与引用。13.2.1 子例行程序的定义一个子例行程序,无论是内部的,外部的,还是模块的,都具有以下语法形式(R1221):subroutine-prefix SUBROUTINE subroutine-name

20、 &(dummy-argument-list)specification-partexecution-partinternal-subprogram-partEND SUBROUTINE subroutine-name其中所谓子例行程序的前缀(subroutine-prefix)是如下的几个可选项:RECURSIVEPUREELEMENTAL不过其中表示递归属性和逐元属性的 RECURSIVE 与 ELEMENTAL 不能同时作为同一个子例行程序的前缀。其中哑元列表(dummy-argument-list)里面的哑元既可以是哑元名称,也可以是星号(*) 。星号表示替代返回变元。当子例行程序被执

21、行的时候,这些哑元就获得了同引用子例行程序时给出的实元的关联。子例行程序的一般规则如下: 如果在 END 语句里面使用了子例行程序的名称,那么这个名称就必须和SUBROUTINE 语句里面的名称一致。 一个内部子例行程序不能再包含一个内部子程序部分。 内部子例行程序不能包含 ENTRY 语句。 内部子例行程序与模块子例行程序的 END 语句必须是如下的形式:END SUBROUTINE subroutine-name即必须在 END 语句里面使用关键词 SUBROUTINE。 如果子例行程序要实现递归结构,那么在 SUBROUTINE 语句里面必须加上前缀RECURSIVE。 在一个 SUBR

22、OUTINE 语句里面,不能重复使用某个前缀。 一个子例行程序不能同时是递归的和逐元的。参见 13.4 节与 13.5 节。 每个哑元都是其所在是子例行程序的局部变元。哑元可以在子例行程序里面获得显式说明,也可以在某些特定情况下获得隐式声明。 子例行程序里面的哑元都可以具有 INTENT 属性或 OPTIONAL 属性,但是哑指针和哑过程都不能具有 INTENT 属性。 在子例行程序里面不能使用 PUBLIC 属性和 PRIVATE 属性。 用于表示替代返回变元的星号*是一种过时的语言特征。【例 13-1】下面是 SUBROUTINE 语句的几种形式的例子:ELEMENTAL SUBROUTI

23、NE BESSELL1SUBROUTINE IMBEDDING(X)SUBROUTINE TASK()SUBROUTINE PARTICAL(CURRENT,*)SUBROUTINE COMPUT2RECURSIVE SUBROUTINE WALLIS(X,Y)【例 13-2】下面是一个子例行程序的例子:SUBROUTINE WAVELET(A,B)REAL AINTEGER B.END SUBROUTINE WAVELET13.2.2 子例行程序的引用当程序的某个位置需要调用一个子例行程序时,就在该位置使用 CALL 语句或者是一个自定义赋值语句。实际上对子例行程序的调用,就是给出子例行程序

24、的名称和实元。下面首先讨论使用 CALL 语句的引用方式。CALL 语句的语法形式(R1211) 为:CALL subroutine-name (subroutine-actual-argument-list)其中的子例行程序的实元的语法形式(R1212) 为:keyword= subroutine-actual-argument其中的的关键词是子例行程序的界面里面的某个哑元名称,而子例行程序的实元则具有以下几种形式(R1214)之一:包含一个变量的表达式;过程名称;作为替代返回的*标签。以上子例行程序的引用实际上就是建立一个哑元与实元的关联。即或者是通过直接使用哑元名称作为关键词,给出相应的

25、实元,或者是省略了关键词,但是实元在实元列表里面的顺序对应着相应的子例行程序界面里面的哑元列表的顺序,从而为每个子例行程序的哑元指定了相应的实元。作为实元的表达式的一个特殊情况就是只有一个变量自身,这时该变量作为实元可以与具有任意 INTENT(IN,OUT,INOUT)属性的哑元相关联,但一个表达式就只能和具有INTENT(IN)属性的哑元相关联。CALL 语句的引用的一般规则如下: 根据变元列表位置来进行实元与哑元的关联的变元,必须放置在实元列表的前面部分,而一旦在该列表当中出现了关键词,那么该关键词后面的变元就必须全部是关键词。 在实元列表与哑元列表的对应当中,一个实元和与之对应的非可选

26、哑元相关联,而对于可选哑元,相应的实元可省略。 关键词就是子例行程序的显式界面里面的哑元名称,如果在引用里面使用关键词,那么实元就与具有该名称的哑元相关联。 如果在一个实元列表当中,与某个实元相关联的关键词被省略了,那么在这个列里面该实元前面的所有关键词都必须省略;一个没有关键词出现的实元列表里面的所有实元,都是依据列表位置来确定它们相应的哑元的。 如果一个实元列表使用了多个关键词,那么关键词的顺序是无关紧要的,如果一个实元列表里面全是关键词,那么它们的顺序可以是任意,不需要与哑元列表里面的顺序有对应关系。 作为替代返回说明符的星号后面的标签,必须是与 CALL 语句同一个作用域单位的分支目标

27、语句的标签。 实元不能是内部过程或语句函数的名称。 与一个哑过程相关联的实元必须是一个种过程的名称,如果一个过程的类名称与种过程相同,那么实际引用的是种过程。 对于某些固有函数来说,它们的种固有函数名称不能用来作为实元。过程引用的另外一种形式就是使用自定义赋值语句。可以通过自定义赋值形式得到引用的子例行程序必须具有一个 ASSIGNMENT 界面,在这个界面里面必须给出两个变元arg1 和 arg2,其中 arg1 具有 INTENT(OUT)或 INTENT(INOUT)属性,而 arg2 则具有INTENT(IN)属性。自定义赋值引用的形式为:arg1 = arg2对于这种引用形式的详细讨

28、论见 13.8.5 节。【例 13-3】下面是一个通过 CALL 语句引用子例行程序的例子。CALL SUB1(100.01+X,*25) !引用 SUBROUTINE SUB125 !替代返回的目标语句 CALL SUB2(X=5.02,N=75) !引用 SUBROUTINE SUB2(X,N)【例 13-4】下面是一个通过自定义赋值形式引用子例行程序的例子。MODULE POLAR_COORDINATESTYPE POLARREAL :RHO THETAEND TYPEINTERFACE ASSIGNMENT(=)MODULE PROCEDURE ASSIGN_POLAR_TO_COMP

29、LEXEND INTERFACESUBROUTINE ASSIGN_POLAR_TO_COMPLEX (Z, P)COMPLEX INTENT ( OUT ) : ZTYPE (POLAR) INTENT (IN) : PZ= CMPLX (P%RHO * COS (P%THETA), &P%RHO * SIN (P%THETA)END SUBROUTINE ASSIGN_POLAR_TO_COMPLEXUSE POLAR_COORDINATESCOMPLEX :CARTESIANCARTESIAN = POLAR(R,PI/6)在上面最后语句里面,使用了一个赋值语句,实际上就等价于下面的 C

30、ALL 语句:CALL ASSIGN_POLAR_TO_COMPLEX(CARTESIAN,POLAR(R ,PI/6)13.3 函数函数与子例行程序的最大区别就是函数可以用来作为表达式的项,而函数的定义的语法结构则与子例行程序类似。函数的变元用来引入初始值,然后经过函数的计算,得到函数结果,作为表达式相应的项的输入值。函数同样可以通过 4 种关联形式从函数外部获得数据。13.3.1 函数的定义一个函数子程序,不管它是外部子程序,内部子程序还是模块子程序,都具有如下的语法形式:function-prefix simplest-function-statement RESULT (result-

31、name) specification-partexecution-partinternal-subprogram-partEND FUNCTION function-name其中的函数前缀(function-prefix)包括如下几种形式:type-specificationRECURSIVEPUREELEMENTAL其中 RECURSIVE 和 ELEMENTAL 不能同时用于一个函数。其中的最简单 FUNCTION 语句(simplest-function-statement)的语法形式为:FUNCTION function-name (dummy-argument-name-list)

32、其中的哑元在函数运行时获得引用实元的变量关联。因此函数语句可以有几种不同的形式,下面是一些例子。【例 13-5】FUNCTION LAGRANGE1 (X,Y)COMPLEX FUNCTION SHORDINGER2(X,T)FUNCTION LATTICE(X,Y) RESULT(CROSS)RECURSIVE REAL FUNCTION SUM1(N,K)RESULT(SUM2)函数遵循如下规则: 函数的类型既可以在函数语句当中予以说明,也可以使用单独的类型声明语句当中予以说明,但是不能同时采用这两种说明方式。如果这两种说明方式都没有出现,那么系统会认为是默认类型。 如果函数值是数组或指针

33、,那么函数声明必须给出其函数结果名称的相应属性。函数结果名称可以是显形数组或哑形数组。如果为显形数组,那么它的非常数界可以是表达式形式。 哑元的属性既可以在函数体当中显式说明,也可以隐式说明。 哑元总是相应函数的局部变量,因此哑元名称在函数内部必须是唯一的。 如果在 END 语句当中出现了函数名称,那么该名称必须和 FUNCTION 语句当中的函数名称一样。 一个内部函数不能再包含一个内部子程序。 一个内部函数不能包含 ENTRY 语句。 在一个函数语句当中,函数前缀不能重复出现。 一个函数不能同时具有前缀 ELEMENTAL 和 RECURSIVE。参见 13.4 和 13.5 节。 内部函

34、数和模块函数的 END 语句必须是如下形式:END FUNCTION function-name即在 END 语句当中必须包含关键词 FUNCTION。 如果函数语句当中不出现结果子句,那么函数名称就用作结果变量的名称,则对函数名称的引用实质上就是对函数结果变量的引用。 如果函数语句当中出现结果子句,那么函数名称就不能用作结果变量的名称,则对函数名称的引用就是函数引用,属于递归调用。 如果函数语句当中出现结果子句,那么函数名称就不能出现在任何说明语句当中。 如果函数的结果值不是一个指针,那么在函数执行完毕之前,它的结果值就必须完全给定定义:如果结果值是数组,那么数组的所有元素都必须给出定义;如

35、果结果值是结构,那么结构的所有成员都必须给出定义。 如果函数结果值是一个数组或指向数组的指针,那么在函数执行完毕之前,它的结果值的形状必须给定。 如果函数结果是一个指针,那么在函数执行完毕之前,指针的分配状态必须给定,即或者是给出其所关联的目标,或者是显式说明它的去关联状态。 函数的哑元的 INTENT 属性和 OPTIONAL 属性必须予以说明。不过对于哑指针和哑过程,则不具备 INTENT 属性。 在函数里面不能使用 PUBLIC 属性和 PRIVATE 属性。 当函数采取直接递归形式的时候,必须同时给出 RECURSIVE 关键词和 RESULT选项,这是唯一的必须给出 RESULT 选

36、项的情形。13.3.2 RESULT 选项RESULT 子句的功能在于给出一个不同于函数名称的名称,用来赋予函数结果值。如果不通过 RESULT 子句来给定单独的函数结果的名称,那么结果值总是赋予函数名称,这样做在函数是非递归的时候,不会带来什么不良后果,但是当函数为递归形式的时候,就会导致歧义,因为在递归引用时,对函数的引用必须和对函数结果值的引用加以区分。例如一个函数的唯一变量是实型的标量,而它的结果值是一个数组,那么在对它进行递归引用时,如果不对它的结果另外规定名称,就会和对数组结果的引用混淆起来,在这种情况下,就必须使用 RESULT 子句对结果变量进行单独的命名。因此当一个函数是直接

37、或间接递归的时候,都必须使用 RESULT 子句。由于函数的结果值在递归过程当中一直在发生变化,因此函数的结果值为最后一个赋予结果名称的值。下面是一个给出了 RESULT 子句的递归函数的例子。【例 13-6】PROGRAM REVERSE_PHRASEPRINT *,REVERSE(“The following inequalities hold for any lattice”)CONTAINSRECURSIVE FUNCTION REVERSE (PHRASE)RESULT(LATTICE)CHARACTER(*) PHRASECHARACTER(LEN(PHRASE) LATTICEL

38、=LEN_TRIM(PHRASE)K=INDEX(PHRASE(1:L),“” ,BACK=.TRUE.)IF(K=0)THEN;LATTICE=PHRASEELSE;LATTICE=PHRASE(K+1;L)/“”/ REVERSE(PHRASE(1:K-1)END IFEND FUNCTION REVERSEEND PROGRAM REVERSE_PHRASE在上面的例子里面,函数结果的长度是动态的,因此其函数界面是显式的。13.3.3 函数引用函数的引用有两种形式: 直接在表达式里面使用函数的名称; 通过自定义运算的形式。第一种函数的引用是非常直接的,就是把函数名称以及它的实元用作表达式

39、的项。而一旦函数通过这种方式得到引用,那么引用该函数的表达式的计算,就意味着函数里面的实元已经得到计算,相应的变量关联已经建立,而该函数的定义里面的所有语句都已经得到执行,因此函数对于引用它的表达式来说,就是函数结果变量。函数引用的一般语法形式(R1210) 为:function-name(function-actual-argument-list)其中函数实元(function-actual-argument)的一般语法形式(R1212) 为:keyword = function-argument其中的函数变元(function-argument)具有一下几种形式(R1214)之一:包含一个

40、变量的表达式过程名称而其中的关键词(keyword)是函数界面里面的一个哑元名称。函数引用里面的实元与函数界面里面的哑元的对应规则与子例行程序一样,或者是根据哑元列表里面的顺序与实元相应顺序的对应,或者是利用关键词来指定。对于引用函数的表达式来说,一个变量也是一种表达式的形式,如果只是一个变量,那么它所关联的哑元可以具有任意的 INTENT(IN,OUT, INOUT)属性,而对于其他形式的表达式,则其中的实元只能关联于具有 INTENT(IN)属性的哑元。注意表达式(X) 总是一个表达式,尽管括号里面的 X 只是一个 变量,因此 X 作为实元,只能关联于一个具有 INTENT(IN)属性的哑

41、元。这种形式的函数引用与子例行程序引用的唯一差别,就是函数引用里面的变元列表里面不能包含替代返回,其他的规则完全与 13.2.2 节里面有关子例行程序引用的规则一样。下面是在表达式里面直接引用函数的例子。【例 13-7】X=Y+5*LEBESGUE1(3.5) !引用了函数 LEBESGUE1(R)PRINT*,ANALOG1(5.0,6.7) !引用了函数 ANALOG1(I, U)函数引用的第二种形式就是通过自定义运算的形式。除了固有运算之外,我们总会需要定义不能由任何固有运算表示的运算,这时就可以使用函数来定义自定义运算以及给出运算符的界面块,具体是说明参见 13.8.4 节。利用自定义

42、运算来引用函数的规则如下。 一元运算只能使用包含一个变元的函数来定义。而二元运算则使用包含 2 个变元的函数定义。 函数的变元不能是可选的,并且必须具有 INTENT(IN)属性。 自定义算符只能采取在两个句点中间为字符串的形式,其中的字符串不能多于 31个字符,而且不能包含下划线,也不能与逻辑型字面常量雷同。 如果自定义运算的算符与固有运算的算符发生雷同的现象,那么自定义运算肯定是对相应的固有运算进行了某些扩充,例如对于算符所适用的算元的取值范围进行了扩充。下面是一个通过自定义运算来引用函数的例子。【例 13-8】INTERFACE OPERATOR(.FOURIER.)FUNCTION F

43、OURIER_OPERATOR(X,Y) !这些界面部分给出函数 FOURIER_OPERATOR 以及 !它的变元 X,Y 的属性,并且需要说明 X 和 Y 具有 !INTENT(IN) 属性。END FUNCTION FOURIER_OPERATOREND INTERFACEPRINT*,X1 .FOURIER. Y1在上面的例子里面,PRINT 语句里面通过使用自定义运算.FOURIER.而引用了函数FOURIER_OPERATOR,而实元 X1 和 Y1 则分别对应着函数界面里面的哑元 X 和 Y。13.3.4 语句函数所谓语句函数语句,就是只包含一条 FORTRAN 语句的函数定义,

44、它的一般语法形式(R1228)为:function-name(dummy-argument-name-list)= scalar-expression实际上语句函数的功能完全可以用内部函数来实现,而通过内部函数形式来表示同样的功能,可以使得程序更加具有结构性,因此语句函数已经过时。与上面的语句函数等价的内部函数采用如下的形式:FUNCTION function-name(dummy-argument-name-list)function-name = scalar-expressionEND FUNCTION可以看到,上面采用内部函数的形式更加具有结构性。语句函数的一般规则如下。 通过语句函数

45、给出的函数以及哑元都必须是标量。 语句函数里面的表达式只能包含固有运算,并且都只能取标量值。表达式可以引用具有数组变元但是取值为标量的固有函数。例如固有函数 SUM(A+B)就是这样的固有函数,因为它的变元都是数组,而函数结果值则是一个标量。 语句函数作为一种函数的定义形式,只能出现在一个程序单位,或内部过程或模块过程的说明部分。在表达式里面引用的语句函数都必须在相应的程序单位的说明部分已经经过定义,因此语句函数不能采取递归的形式,无论是直接的还是间接的递归形式都不行。 语句函数里面的表达式里面的命名常量和变量都必须在说明部分已经经过定义,或者能够通过宿主关联或使用关联而获得数据。 如果在表达

46、式里面使用了某个数组的元素,那么该数组也必须预先在说明部分经过声明。 语句函数的表达式里面的任何数据对象或者是已经预先声明过,或者就是采用默认的隐式类型声明,而在该语句函数之后,如果存在对于这些数据对象的任何显式类型声,那么都必须与语句函数里面采取的隐式类型声明相兼容。 语句函数里面的哑元的作用域与该语句函数语句的作用域一样。 语句函数的哑元被预定为具有 INTENT(IN)的属性,即表达式里面的任何函数引用都不能改变任何哑元的值。 语句函数本身不能作为实元。 语句函数的引用形式与其他形式的函数的引用一致,变元关联的规则也一致。只是语句函数的界面总是隐式的,因此它的实元就不能采取关键词形式。

47、由于语句函数的变元只能是标量,而显式界面只能适用于那些具有数组值结果,哑形哑元,指针变元以及关键词变元的过程,因此语句函数不能具有显式界面。下面是语句函数的几个不同形式的例子。【例 13-9】CHARACTER (10) WORD_10CHARACTER (20) WORD_20WORD_10 (WORD_20) = WORD_20 (11 : 20)REAL WAVE,XWAVE(X)= COS(X)+SIN(X)REAL LENSCOMPLE CLENS(C)= REAL(C)*2 + AIMAG(C)*213.4 纯过程纯过程这个概念完全是 FORTRAN 95 为了适应并行化计算而进行

48、的改进。由于并行化计算本质上要求进行并行计算的不同运算过程具有相对的独立性,也就是处于并行位置的不同计算单位的计算值,相互不会产生影响。而传统的来源于串行计算的 FORTRAN 的过程,在语言设计上,一般允许过程在执行时具有后效。例如当一个函数的引用的目的不是需要使用函数的结果值,而是利用函数来进行值的传递,那么该函数的执行的后果,就是改变了在公用块里面的变元或变量的值。具有后效的过程也具有类似作用,即改变公用块里面的数据对象的值。实际上除了这种作用于作用域外部的后效之外,输入输出本质上就是一种能够带来后效的操作。显然后效对于并行计算会产生不可预计的后果。例如能够执行并行运算的 FORALL

49、结构或语句,它内部的语句执行顺序是不加以规定的,如果其中包含可能产生后效的过程,就会导致最终计算结果的不确定性。因此要想顺利地进行并行计算,就必须在进行并行计算的程序单位里面完全排除具有后效的过程。而所谓纯过程正是这种完全不会导致后效的纯过程。纯过程包括两类: 所有的固有函数和固有子例行程序 MVBITS 都是纯过程; 在 FUNCTION 语句或 SUBROUTINE 语句里面具有前缀 PURE 的自定义过程都是纯过程。必须使用纯过程的上下文有以下 4 种情形: 在 FORALL 语句或结构里面引用的函数; 在说明语句里面引用的函数; 作为实元被调用到一个纯过程的过程; 被纯过程体引用的过程,包括被自定

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

当前位置:首页 > 企业管理 > 管理学资料

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


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

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

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