




已阅读5页,还剩39页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第13章 过程及其通讯如果说一个语句可以看成是一条指令,那么在FORTRAN 95语言里,一个具有一定结构的计算任务可以对应的最小程序单位,就是一个过程。从专致于科学计算的初衷出发,FORTRAN在语言的结构层面上,可以认为是面向过程的一种语言,尽管在编程语言流行面向对象的今天,面向过程显得有点落伍,但却有效地适用于描述计算任务,当然随着现代技术对于计算的要求越来越复杂与庞大,FORTRAN也不是一味地守旧,可以预计FORTRAN的下一个版本,就会具有适应大型软件工程的要求的面向对象的语言特性。FORTRAN 95语言作为一种语言的主要特点,可以说就体现在它的过程这个主要的程序结构上。而从我们程序编写者的角度来看,能否把一个完整的算法转写成一个完整的程序,关键也就在于能否构造出一些恰当的过程来作为程序的基本单位。特别是对于大型的FORTRAN 95程序,需要把它分解为好几百个过程是很常见的,这时如何恰当地使用过程来构建整个程序,可以说是编写程序最主要的工作。因此如何构建过程,如何根据需求运用过程,然后在不同的过程之间建立必要的通讯,是值得我们非常仔细地加以讨论的。本章的主要任务即在于此。13.1 过程的分类与性质由于过程具有多方面的功能与属性,因此对于过程的分类可以有多种方式。下面我们首先讨论过程的各种分类方式及其相应的分类意义,然后我们讨论有关过程引用的要点与相应概念。13.1.1 过程的分类过程可以有几个不同的分类方式,每种分类方式反映了过程的某个方面的特性。下面分别予以讨论。从形式上根据调用的方式的不同,FORTRAN的过程分为两类: 函数;函数返回一个可以供表达式使用的值。因此函数的调用总是作为一个表达式的算元,函数的值也就是相应表达式算元的取值。函数调用时直接使用函数的名称和它的变量,或者作为一个自定义运算,它返回值之后,它的功能就算完成,不对程序产生后效,当然,FORTRAN标准也不绝对禁止使用产生一定后效的函数。 子例行程序。子例行程序的调用必须使用CALL语句,或者是作为一个赋值。子例行程序的功能主要在于产生一定的后效,例如改写一些变元,以及全局变量,或者执行某些输入输出。函数与子例行程序的一般规则如下: 函数与子例行程序在调用方式上的差别实际上来源于一个函数总是和一个相应的函数结果值相关联,只要运行或者调用一个函数,总会得到相应的函数结果值,而子例行程序却没有相应的概念,这就使得子例行程序只能依靠使用专门的CALL语句来调用,而既然函数总是能够给出一个函数值来,就可以直接把它作为表达式的算元来调用。 函数结果可以是任意的数据类型,例如派生类型,或者是取数组值都可以。 如果在FUNCTION语句当中使用RESULT属性,那么就可以给结果一个与函数定义里面的函数名称不同的名称,这主要应用于直接调用自身的递归函数。 在除了模块以及数据块程序单位之外的程序单位的说明部分还可以使用一种由一个语句组成的函数,称为语句函数,不过它的功能完全可以由下面的内部过程来实现,因此已经过时。再从过程与其他程序单位的关系的角度来分类的话,过程也可以分为如下两大类: 外部过程;顾名思义,外部过程就是处于任何的程序单位的外部,它作为一个孤立的过程,可以单独构造,单独编译,单独使用,而可以完全独立于任何的过程与程序单位。甚至还可以是用其他的语言编写的,例如常见的C语言。当然,一个外部过程还是可以通过变元列表,模块,公用块等来共享数据与过程之类的信息。 内部过程。内部过程总是定义在一个程序单位内部,该程序单位就称为它的宿主。当一个过程出现在它的宿主内部的时候,总是出现在宿主的CONTAINS语句和END语句之间。一个内部过程对于其宿主而言,总是局域的,而且通过宿主关联继承了宿主的数据环境。特别的,如果一个内部过程的宿主是一个模块,那么这种内部过程被单独称为模块过程。 模块过程。模块过程同样必须出现在其宿主模块里面的CONTAINS语句和END语句之间。 一个模块同样可以通过宿主关联继承宿主模块的数据环境。 但与一般内部过程不同的是,模块内的模块过程可以具有PUBLIC属性,即从模块外部可以直接访问具有PUBLIC属性的过程;当然模块过程也可以具有PRIVATE属性,使得模块外部不能访问该过程。注意在早期FORTRAN版本里面出现过语句函数的概念,它是出现在程序单位的说明部分的单语句定义的函数,不能够出现在模块或数据块程序单位当中。由于它的功能可以完全由内部过程替代,因此语句函数的概念已经过时。一般由多个过程组成过程子程序。对于内部过程来说,组成过程子程序的多个过程可以通过宿主关联而获得对宿主数据环境的共享;而对于外部过程来说,当一些外部过程要组成过程子程序的时候,由于缺乏类似的关联机制,它们的数据共享问题是通过所谓过程登录机制来解决的。所谓过程登录是通过运用ENTRY语句来说明一个过程与一个过程子程序的关联关系的。每一个ENTRY语句就给出了一个过程,所有这些过程就可以共享该过程子程序的数据环境。例如两个不同的函数SIN和COS就可以通过这种方式使用同一个外部子程序,因为SIN和COS具有如下的关系:。尽管对于内部过程来说,过程登录是不必要的,但也可以应用于模块过程。根据过程对于变元的作用效果来分类,则可以把过程分为纯过程与非纯过程。所谓纯过程就是在执行对变元的作用之后,不会产生后效,这对于程序按照序列进行一般是没有什么影响的,但是对于程序的并行执行,却往往会给程序带来不确定性,因此就需要对一般的过程加以约束,使得其完全没有后效,从而得到纯过程。例如过程A和B,都具有对X进行赋值的后效,设A把X赋值为1.0,而B把X赋值为2.0,那么如果A和B这两个过程是并行完成的,那么X的具体取值就变得不可预料,因此需要对过程A和B进行一定的约束,使得它们成为纯过程。如果从过程作用于数据对象的方式来进行分类,那么过程可以分为逐元过程和变换过程。 逐元过程,即过程的本来属于标量形式的变元可以被赋予数组,这时过程的作用方式是对数组的每个元素进行单独的运算,得到的所有结果再构成一个与变元数组相同形状的数组,就称为该过程作用于数组后的结果。 变换过程,即过程的变元本来就是数组,对数组的作用方式是把数组作为一个整体进行变换,而不是逐个地针对元素进行。因此逐元过程既可以标量变元来引用,也可以通过数组变元来引用,只是当通过数组变元来引用的时候,实际上就是通过标量数组元素来引用,只不过需要引用多次引用的次数等于数组元素的个数。逐元过程总是纯过程,因为逐元过程不产生后效。如果从过程的来源来看,过程还可以分为固有过程和自定义过程两类。所谓固有过程,也称为内建过程,即FORTRAN标准已经建立好了的过程,一共包括115个固有过程。这意味着任何的FORTRAN实现,都必须能够直接提供这些固有过程。固有过程的名称在程序单位的任何地方都是可以直接引用的,唯一的例外,就是当使用了显式的EXTERNAL语句或过程界面块的时候,固有过程的通用属性就可能被扩充,即固有过程的名称也可能用来引用非固有过程,这时该名称所表示的固有过程反而成了一种默认值。大部分的固有过程同时还是逐元的,自定义过程也可以被定义为逐元过程,这样就极大地增强了FORTRAN的数组计算能力。13.1.2 过程的引用所谓过程的引用就是使得过程的名称出现在程序的恰当位置,当程序运行到该位置时,过程名称能够导致程序转入执行该过程。过程的引用也可以称为调用或访问,在本书当中,这几个词汇是根据一般用词习惯而交替使用的。过程的引用分为两种形式,即子例行程序的引用和函数的引用。 子例行程序的引用是依靠一个单独的CALL语句,某些时候也可以使用赋值语句的形式。 函数的引用是作为表达式的一部分,即采用函数名称及其变元列表作为表达式的项出现,某些时候,函数引用也可以采取一元运算或二元运算的形式,其变元作为表达式的算元。过程的引用本质上就是通过变元来进行数据的传递或通讯,在进行过程的引用过程当中,变元根据它所行使的功能,还可以分为三种:实元,哑元,替代返回变元。 实元在过程的引用当中,所谓实元就是在程序执行的时候需要实际采用的数据对象。例如实元在作为变量的时候,可以在每次引用当中取不同的值,因为作为实元,是直接面向具体取值的。实元的取值可以是输入值,也可以是过程运行之后返回的值,或者两者都是。 哑元所谓哑元就是一个名称,通过该名称过程内部就能够访问到相应实元。因此哑元在过程被定义的时候就已经给出,在过程进行计算时,它就充当变元名称。一旦在程序运行时引用该过程,那么引用的实元就通过变元关联而与相应哑元建立关联关系。如果过程界面是显式的,那么在引用过程时,就可以直接使用哑元名称作为实元关键词。过程定义里面的哑元名称还可以用来指称一个过程,这实际上就是一个过程引用。因为与该哑元具有变元关联关系的实元名称就是一个过程的名称,不过这个被关联的过程不能是内部过程,语句函数,已经具有类属性的过程。 替代返回变元所谓替代返回是只在子例行程序里面出现的一种变元的功能,通过替代返回,就可以中断程序的顺序执行次序,控制程序的执行分支到其他某个位置,替代返回里面的实元就是分支目标语句的标签,这种分支方式经常用于从子例行程序里面出错退出的情形,不过在现代的FORTRAN控制结构里面,具有更合理的控制方式,因此这种分支方式已经过时。过程通过变元进行通讯的途径就是关联,过程关联的形式有4种:变元关联,宿主关联,使用关联,以及共享存储关联。 变元关联变元关联是过程之间进行数据通讯的主要方式。在13.7节会详细地加以讨论。 宿主关联宿主关联是建立在一个过程与其所属的程序单位之间的数据关联关系。一个宿主程序单位可以是一个主程序,模块程序单位,外部过程,模块过程。这些宿主程序单位里面所有被允许访问的数据对象都可以通过宿主关联而被其包含的过程访问到。对于宿主关联的详细讨论见第15章。 使用关联一个过程可以通过一个USE语句和该USE语句所在的程序单位建立使用关联。通过使用管关联,过程可以和模块共享数据对象,也可以按照程序编写者的意愿,通过ONLY子句来隐藏某些数据对象。对于使用关联的详细讨论见第12章。 共享存储关联有关存储关联的详细讨论见第7章和第15章。在组织程序时,除了在不同过程之间进行通讯之外,过程自身还可以构成递归的结构。即一个局部变量得到初始化之后,尽管它出现在递归的每一层里面,但是其初始化值只能够在该递归结构开始运行时有效地使用一次,因此实际上等价于对于变量使用了SAVE属性,使得每一层的递归都保留了它的变量值,可以说是一种数据的隐式保留属性。一个过程无论是进行直接的递归还是进行间接的递归,都必须在过程定义里面的FUNCTION语句或SUBROUTINE语句里面加上关键词RECURSIVE,这个关键词的功能就是能够实现编译器对于过程调用的优化。过程的引用并不涉及过程的具体功能,也就是对于过程的内部计算过程,在引用时是不需要考虑的,这样就可以把所有涉及到过程引用的信息集中在一起,包括过程及其变元的名称与属性,这就构成了所谓过程的界面。顾名思义,也就是一个过程与外部进行通讯的层面。如果一个过程的这些信息对于一个引用它的程序单位来说,不是显式的,那么该过程就被称为具有隐式界面。这时,引用它的程序单位就只能依据过程名称以及过程引用里面的实元的属性来确定过程的有关信息,同时有关的哑元与实元的数据匹配都需要程序编写者预先规划好。如果过程界面是显式,那么相关的数据匹配工作就可以完全由编译系统来完成检查。在很多的引用情形下,都一定要求被引用的过程具有显式界面,例如: 作为数组片断的实元; 指针变元; 可选变元; 关键词调用; 自定义运算; 自定义赋值; 自定义过程的逐元调用; 自定义类过程。 各种类型的过程的界面形式的显式或隐式的情况如下: 固有函数,内部过程,模块过程都是显式的界面,自定义过程也可以具有显式界面。所有固有函数的显式界面信息都包含在任何一个FORTRAN编译器内部,在对固有函数进行任何引用时,系统都可以根据实元的类型来检查与匹配相应的固有函数,从而得到一个正确的引用。因此对固有函数的引用可以直接使用函数的类名称以及关键词。内部过程和模块过程在它的定义里面就要求具有显式界面,因此对这两种过程的引用同样是安全可靠的。 外部过程和语句函数都是隐式的界面。不过在引用外部过程时,可以在引用程序单位里面为所引用的外部过程提供一个界面块,这样就等价于该外部过程具有了一个显式的界面。有关界面的详细讨论见13.8节。正是由于引用只与过程的界面有关,因此被引用的过程可以是使用其他语句编写的,例如C,C+等,不过使用非FORTRAN的过程可能移植性不太好,因为在不同的系统之间,其他的语言可能具有不同的过程通讯机制。13.2 子例行程序的运用一个子例行程序就是一个完备的能够执行一定的计算任务的程序单位。它由以下五个部分构成: 初始的SUBROUTINE语句; 说明部分; 计算执行部分; 某些内部过程; END语句。当一个子例行程序被调用时,从它的第一个可执行结构开始运行,它通过变元关联,使用关联,宿主关联以及存储关联与外部进行数据通讯。下面分别讨论子例行程序的定义与引用。13.2.1 子例行程序的定义一个子例行程序,无论是内部的,外部的,还是模块的,都具有以下语法形式(R1221): subroutine-prefix SUBROUTINE subroutine-name & (dummy-argument-list) specification-part execution-part internal-subprogram-part END SUBROUTINE subroutine-name其中所谓子例行程序的前缀(subroutine-prefix)是如下的几个可选项: RECURSIVE PURE ELEMENTAL不过其中表示递归属性和逐元属性的RECURSIVE与ELEMENTAL不能同时作为同一个子例行程序的前缀。其中哑元列表(dummy-argument-list)里面的哑元既可以是哑元名称,也可以是星号(*)。星号表示替代返回变元。当子例行程序被执行的时候,这些哑元就获得了同引用子例行程序时给出的实元的关联。子例行程序的一般规则如下: 如果在END语句里面使用了子例行程序的名称,那么这个名称就必须和SUBROUTINE语句里面的名称一致。 一个内部子例行程序不能再包含一个内部子程序部分。 内部子例行程序不能包含ENTRY语句。 内部子例行程序与模块子例行程序的END语句必须是如下的形式: END SUBROUTINE subroutine-name即必须在END语句里面使用关键词SUBROUTINE。 如果子例行程序要实现递归结构,那么在SUBROUTINE语句里面必须加上前缀RECURSIVE。 在一个SUBROUTINE语句里面,不能重复使用某个前缀。 一个子例行程序不能同时是递归的和逐元的。参见13.4节与13.5节。 每个哑元都是其所在是子例行程序的局部变元。哑元可以在子例行程序里面获得显式说明,也可以在某些特定情况下获得隐式声明。 子例行程序里面的哑元都可以具有INTENT属性或OPTIONAL属性,但是哑指针和哑过程都不能具有INTENT属性。 在子例行程序里面不能使用PUBLIC属性和PRIVATE属性。 用于表示替代返回变元的星号*是一种过时的语言特征。【例13-1】下面是SUBROUTINE语句的几种形式的例子: ELEMENTAL SUBROUTINE BESSELL1 SUBROUTINE IMBEDDING(X) SUBROUTINE TASK() SUBROUTINE PARTICAL(CURRENT,*) SUBROUTINE COMPUT2 RECURSIVE SUBROUTINE WALLIS(X,Y)【例13-2】下面是一个子例行程序的例子: SUBROUTINE WAVELET(A,B) REAL A INTEGER B . END SUBROUTINE WAVELET13.2.2 子例行程序的引用当程序的某个位置需要调用一个子例行程序时,就在该位置使用CALL语句或者是一个自定义赋值语句。实际上对子例行程序的调用,就是给出子例行程序的名称和实元。下面首先讨论使用CALL语句的引用方式。CALL语句的语法形式(R1211)为: CALL subroutine-name (subroutine-actual-argument-list)其中的子例行程序的实元的语法形式(R1212)为: keyword= subroutine-actual-argument其中的的关键词是子例行程序的界面里面的某个哑元名称,而子例行程序的实元则具有以下几种形式(R1214)之一:包含一个变量的表达式;过程名称;作为替代返回的*标签。以上子例行程序的引用实际上就是建立一个哑元与实元的关联。即或者是通过直接使用哑元名称作为关键词,给出相应的实元,或者是省略了关键词,但是实元在实元列表里面的顺序对应着相应的子例行程序界面里面的哑元列表的顺序,从而为每个子例行程序的哑元指定了相应的实元。作为实元的表达式的一个特殊情况就是只有一个变量自身,这时该变量作为实元可以与具有任意INTENT(IN,OUT,INOUT)属性的哑元相关联,但一个表达式就只能和具有INTENT(IN)属性的哑元相关联。CALL语句的引用的一般规则如下: 根据变元列表位置来进行实元与哑元的关联的变元,必须放置在实元列表的前面部分,而一旦在该列表当中出现了关键词,那么该关键词后面的变元就必须全部是关键词。 在实元列表与哑元列表的对应当中,一个实元和与之对应的非可选哑元相关联,而对于可选哑元,相应的实元可省略。 关键词就是子例行程序的显式界面里面的哑元名称,如果在引用里面使用关键词,那么实元就与具有该名称的哑元相关联。 如果在一个实元列表当中,与某个实元相关联的关键词被省略了,那么在这个列里面该实元前面的所有关键词都必须省略;一个没有关键词出现的实元列表里面的所有实元,都是依据列表位置来确定它们相应的哑元的。 如果一个实元列表使用了多个关键词,那么关键词的顺序是无关紧要的,如果一个实元列表里面全是关键词,那么它们的顺序可以是任意,不需要与哑元列表里面的顺序有对应关系。 作为替代返回说明符的星号后面的标签,必须是与CALL语句同一个作用域单位的分支目标语句的标签。 实元不能是内部过程或语句函数的名称。 与一个哑过程相关联的实元必须是一个种过程的名称,如果一个过程的类名称与种过程相同,那么实际引用的是种过程。 对于某些固有函数来说,它们的种固有函数名称不能用来作为实元。过程引用的另外一种形式就是使用自定义赋值语句。可以通过自定义赋值形式得到引用的子例行程序必须具有一个ASSIGNMENT界面,在这个界面里面必须给出两个变元arg1和arg2,其中arg1具有INTENT(OUT)或INTENT(INOUT)属性,而arg2则具有INTENT(IN)属性。自定义赋值引用的形式为: arg1 = arg2对于这种引用形式的详细讨论见13.8.5节。【例13-3】下面是一个通过CALL语句引用子例行程序的例子。 CALL SUB1(100.01+X,*25) !引用SUBROUTINE SUB1 25 !替代返回的目标语句 CALL SUB2(X=5.02,N=75) !引用SUBROUTINE SUB2(X,N) 【例13-4】下面是一个通过自定义赋值形式引用子例行程序的例子。 MODULE POLAR_COORDINATES TYPE POLARREAL :RHO THETA END TYPE INTERFACE ASSIGNMENT(=) MODULE PROCEDURE ASSIGN_POLAR_TO_COMPLEX END INTERFACE SUBROUTINE ASSIGN_POLAR_TO_COMPLEX (Z, P) COMPLEX INTENT ( OUT ) : Z TYPE (POLAR) INTENT (IN) : P Z= CMPLX (P%RHO * COS (P%THETA), & P%RHO * SIN (P%THETA) END SUBROUTINE ASSIGN_POLAR_TO_COMPLEX USE POLAR_COORDINATES COMPLEX :CARTESIAN CARTESIAN = POLAR(R,PI/6)在上面最后语句里面,使用了一个赋值语句,实际上就等价于下面的CALL语句: CALL ASSIGN_POLAR_TO_COMPLEX(CARTESIAN,POLAR(R,PI/6)13.3 函数 函数与子例行程序的最大区别就是函数可以用来作为表达式的项,而函数的定义的语法结构则与子例行程序类似。函数的变元用来引入初始值,然后经过函数的计算,得到函数结果,作为表达式相应的项的输入值。函数同样可以通过4种关联形式从函数外部获得数据。13.3.1 函数的定义一个函数子程序,不管它是外部子程序,内部子程序还是模块子程序,都具有如下的语法形式: function-prefix simplest-function-statement RESULT (result-name) specification-part execution-part internal-subprogram-part END FUNCTION function-name其中的函数前缀(function-prefix)包括如下几种形式: type-specification RECURSIVE PURE ELEMENTAL其中RECURSIVE和ELEMENTAL不能同时用于一个函数。其中的最简单FUNCTION语句(simplest-function-statement)的语法形式为: FUNCTION function-name (dummy-argument-name-list)其中的哑元在函数运行时获得引用实元的变量关联。因此函数语句可以有几种不同的形式,下面是一些例子。【例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)函数遵循如下规则: 函数的类型既可以在函数语句当中予以说明,也可以使用单独的类型声明语句当中予以说明,但是不能同时采用这两种说明方式。如果这两种说明方式都没有出现,那么系统会认为是默认类型。 如果函数值是数组或指针,那么函数声明必须给出其函数结果名称的相应属性。 函数结果名称可以是显形数组或哑形数组。如果为显形数组,那么它的非常数界可以是表达式形式。 哑元的属性既可以在函数体当中显式说明,也可以隐式说明。 哑元总是相应函数的局部变量,因此哑元名称在函数内部必须是唯一的。 如果在END语句当中出现了函数名称,那么该名称必须和FUNCTION语句当中的函数名称一样。 一个内部函数不能再包含一个内部子程序。 一个内部函数不能包含ENTRY语句。 在一个函数语句当中,函数前缀不能重复出现。 一个函数不能同时具有前缀ELEMENTAL和RECURSIVE。参见13.4和13.5节。 内部函数和模块函数的END语句必须是如下形式: END FUNCTION function-name即在END语句当中必须包含关键词FUNCTION。 如果函数语句当中不出现结果子句,那么函数名称就用作结果变量的名称,则对函数名称的引用实质上就是对函数结果变量的引用。 如果函数语句当中出现结果子句,那么函数名称就不能用作结果变量的名称,则对函数名称的引用就是函数引用,属于递归调用。 如果函数语句当中出现结果子句,那么函数名称就不能出现在任何说明语句当中。 如果函数的结果值不是一个指针,那么在函数执行完毕之前,它的结果值就必须完全给定定义:如果结果值是数组,那么数组的所有元素都必须给出定义;如果结果值是结构,那么结构的所有成员都必须给出定义。 如果函数结果值是一个数组或指向数组的指针,那么在函数执行完毕之前,它的结果值的形状必须给定。 如果函数结果是一个指针,那么在函数执行完毕之前,指针的分配状态必须给定,即或者是给出其所关联的目标,或者是显式说明它的去关联状态。 函数的哑元的INTENT属性和OPTIONAL属性必须予以说明。不过对于哑指针和哑过程,则不具备INTENT属性。 在函数里面不能使用PUBLIC属性和PRIVATE属性。 当函数采取直接递归形式的时候,必须同时给出RECURSIVE关键词和RESULT选项,这是唯一的必须给出RESULT选项的情形。13.3.2 RESULT选项RESULT子句的功能在于给出一个不同于函数名称的名称,用来赋予函数结果值。如果不通过RESULT子句来给定单独的函数结果的名称,那么结果值总是赋予函数名称,这样做在函数是非递归的时候,不会带来什么不良后果,但是当函数为递归形式的时候,就会导致歧义,因为在递归引用时,对函数的引用必须和对函数结果值的引用加以区分。例如一个函数的唯一变量是实型的标量,而它的结果值是一个数组,那么在对它进行递归引用时,如果不对它的结果另外规定名称,就会和对数组结果的引用混淆起来,在这种情况下,就必须使用RESULT子句对结果变量进行单独的命名。因此当一个函数是直接或间接递归的时候,都必须使用RESULT子句。由于函数的结果值在递归过程当中一直在发生变化,因此函数的结果值为最后一个赋予结果名称的值。下面是一个给出了RESULT子句的递归函数的例子。【例13-6】 PROGRAM REVERSE_PHRASE PRINT *,REVERSE(“The following inequalities hold for any lattice”) CONTAINS RECURSIVE FUNCTION REVERSE (PHRASE)RESULT(LATTICE) CHARACTER(*) PHRASE CHARACTER(LEN(PHRASE) LATTICE L=LEN_TRIM(PHRASE) K=INDEX(PHRASE(1:L),“”,BACK=.TRUE.) IF(K=0)THEN;LATTICE=PHRASE ELSE;LATTICE=PHRASE(K+1;L)/“”/ REVERSE(PHRASE(1:K-1) END IF END FUNCTION REVERSE END PROGRAM REVERSE_PHRASE在上面的例子里面,函数结果的长度是动态的,因此其函数界面是显式的。13.3.3 函数引用函数的引用有两种形式: 直接在表达式里面使用函数的名称; 通过自定义运算的形式。第一种函数的引用是非常直接的,就是把函数名称以及它的实元用作表达式的项。而一旦函数通过这种方式得到引用,那么引用该函数的表达式的计算,就意味着函数里面的实元已经得到计算,相应的变量关联已经建立,而该函数的定义里面的所有语句都已经得到执行,因此函数对于引用它的表达式来说,就是函数结果变量。函数引用的一般语法形式(R1210)为: function-name(function-actual-argument-list)其中函数实元(function-actual-argument)的一般语法形式(R1212)为: keyword = function-argument其中的函数变元(function-argument)具有一下几种形式(R1214)之一:包含一个变量的表达式过程名称而其中的关键词(keyword)是函数界面里面的一个哑元名称。函数引用里面的实元与函数界面里面的哑元的对应规则与子例行程序一样,或者是根据哑元列表里面的顺序与实元相应顺序的对应,或者是利用关键词来指定。对于引用函数的表达式来说,一个变量也是一种表达式的形式,如果只是一个变量,那么它所关联的哑元可以具有任意的INTENT(IN,OUT,INOUT)属性,而对于其他形式的表达式,则其中的实元只能关联于具有INTENT(IN)属性的哑元。注意表达式(X)总是一个表达式,尽管括号里面的X只是一个变量,因此X作为实元,只能关联于一个具有INTENT(IN)属性的哑元。这种形式的函数引用与子例行程序引用的唯一差别,就是函数引用里面的变元列表里面不能包含替代返回,其他的规则完全与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节。利用自定义运算来引用函数的规则如下。 一元运算只能使用包含一个变元的函数来定义。而二元运算则使用包含2个变元的函数定义。 函数的变元不能是可选的,并且必须具有INTENT(IN)属性。 自定义算符只能采取在两个句点中间为字符串的形式,其中的字符串不能多于31个字符,而且不能包含下划线,也不能与逻辑型字面常量雷同。 如果自定义运算的算符与固有运算的算符发生雷同的现象,那么自定义运算肯定是对相应的固有运算进行了某些扩充,例如对于算符所适用的算元的取值范围进行了扩充。下面是一个通过自定义运算来引用函数的例子。【例13-8】 INTERFACE OPERATOR(.FOURIER.) FUNCTION FOURIER_OPERATOR(X,Y) !这些界面部分给出函数FOURIER_OPERATOR以及 !它的变元X,Y的属性,并且需要说明X和Y具有 !INTENT(IN)属性。 END FUNCTION FOURIER_OPERATOR END INTERFACE PRINT*,X1 .FOURIER. Y1在上面的例子里面,PRINT语句里面通过使用自定义运算.FOURIER.而引用了函数FOURIER_OPERATOR,而实元X1和Y1则分别对应着函数界面里面的哑元X和Y。13.3.4 语句函数所谓语句函数语句,就是只包含一条FORTRAN语句的函数定义,它的一般语法形式(R1228)为: function-name(dummy-argument-name-list)= scalar-expression实际上语句函数的功能完全可以用内部函数来实现,而通过内部函数形式来表示同样的功能,可以使得程序更加具有结构性,因此语句函数已经过时。与上面的语句函数等价的内部函数采用如下的形式: FUNCTION function-name(dummy-argument-name-list) function-name = scalar-expression END FUNCTION可以看到,上面采用内部函数的形式更加具有结构性。语句函数的一般规则如下。 通过语句函数给出的函数以及哑元都必须是标量。 语句函数里面的表达式只能包含固有运算,并且都只能取标量值。表达式可以引用具有数组变元但是取值为标量的固有函数。例如固有函数SUM(A+B)就是这样的固有函数,因为它的变元都是数组,而函数结果值则是一个标量。 语句函数作为一种函数的定义形式,只能出现在一个程序单位,或内部过程或模块过程的说明部分。在表达式里面引用的语句函数都必须在相应的程序单位的说明部分已经经过定义,因此语句函数不能采取递归的形式,无论是直接的还是间接的递归形式都不行。 语句函数里面的表达式里面的命名常量和变量都必须在说明部分已经经过定义,或者能够通过宿主关联或使用关联而获得数据。 如果在表达式里面使用了某个数组的元素,那么该数组也必须预先在说明部分经过声明。 语句函数的表达式里面的任何数据对象或者是已经预先声明过,或者就是采用默认的隐式类型声明,而在该语句函数之后,如果存在对于这些数据对象的任何显式类型声,那么都必须与语句函数里面采取的隐式类型声明相兼容。 语句函数里面的哑元的作用域与该语句函数语句的作用域一样。 语句函数的哑元被预定为具有INTENT(IN)的属性,即表达式里面的任何函数引用都不能改变任何哑元的值。 语句函数本身不能作为实元。 语句函数的引用形式与其他形式的函数的引用一致,变元关联的规则也一致。只是语句函数的界面总是隐式的,因此它的实元就不能采取关键词形式。 由于语句函数的变元只能是标量,而显式界面只能适用于那些具有数组值结果,哑形哑元,指针变元以及关键词变元的过程,因此语句函数不能具有显式界面。下面是语句函数的几个不同形式的例子。【例13-9】 CHARACTER (10) WORD_10 CHARACTER (20) WORD_20 WORD_10 (WORD_20) = WORD_20 (11 : 20) REAL WAVE,X WAVE(X)= COS(X)+SIN(X) REAL LENS COMPLE C LENS(C)= REAL(C)*2 + AIMAG(C)*213.4 纯过程纯过程这个概念完全是FORTRAN 95为了适应并行化计算而进行的改进。由于并行化计算本质上要求进行并行计算的不同运算过程具有相对的独立性,也就是处于并行位置的不同计算单位的计算值,相互不会产生影响。而传统的来源于串行计算的FORTRAN的过程,在语言设计上,一般允许过程在执行时具有后效。例如当一个函数的引用的目的不是需要使用函数的结果值,而是利用函数来进行值的传递,那么该函数的执行的后果,就是改变了在公用块里面的变元或变量的值。具有后效的过程也具有类似作用,即改变公用块里面的数据对象的值。实际上除了这种作用于作用域外部的后效之外,输入输出本质上就是一种能够带来后效的操作。显然后效对于并行计算会产生不可预计的后果。例如能够执行并行运算的FORALL结构或语句,它内部的语句执行顺序是不加以规定的,如果其中包含可能产生后效的过程,就会导致最终计算结果的不确定性。因此要想顺利地进行并行计算,就必须在进行并行计算的程序单位里面完全排除具有后效的过程。而所谓纯过程正是这种完全不会导致后效的纯过程。纯过程包括两类: 所有的固有函数和固有子例行程序MVBITS都是纯过程; 在FUNCTION语句或SUBROUTINE语句里面具有前缀PURE的自定义过程都是纯过程。必须使用纯过程的上下文有以下4种情形: 在FORALL语句或结构里面引用的函数; 在说明语句里面引用的函数; 作为实元被调用到一个纯过程的过程; 被纯过程体引用的过程,包括被自定义运算或自定义赋值引用的过程。如果一个过程被上面的上下文要求是纯过程,那么它的界面就必须是显式的,并且包含关键词PURE。纯过程的一般规则如下: 纯函数的哑元必须具有INTENT(IN)属性。 纯子例行程序的哑元必须给出其INTENT属性,除非该哑元是过程,或指针,或替代返回变量。 纯过程的局部变量不能具有SAVE属性。也就是说纯过程里面的局部变量不能在过程定义的说明部分获得初始化,因为获得初始化意味着该变量自动具有了SAVE属性。 在纯过程里面的所有内部过程都必须是纯过程。 纯过程不能包含如下几种语句: STOP语句; PRINT语句; OPEN语句; CLOSE语句; BACKSPACE语句; ENDFILE语句; REWIND语句; INQUIRE语句; 说明外部文件的READ语句; 说明外部文件的WRITE语句。 纯过程里面的任意一个变量,只要它是公用的,或者是具有宿主关联,或者是具有使用关联,或者是具有INTENT(IN)属性,或者是与这样的变量具有存储关联,那么它就不能用于以下的上下文情形: 赋值语句的左边; 指针赋值语句; DO变量或隐式DO变量; READ语句来自内部文件的输入项; WRITE语句的内部文件; I/O语句的来自内部文件的IOSTAT变量或SIZE变量; ALLOCATE语句或DEALLOCATE语句的STAT变量或分配对象; NULLIFY语句里面的指针对象; 赋值语句的右边,而该赋值语句的左边在某个层次包含了指针成员。下面是纯过程的一个例子。【例13-10】 PURE FUNCTION ACROSS(X,C) REAL ACROSS REAL,INTENT(IN):X COMPLEX C INTENT(IN):C END FUNCTION ACROSS13.5 逐元过程如果说纯过程的概念是为了给程序运行的并行化排除障碍,那么逐元过程的概念则是FORTRAN 95实现程序运行的并行化的强有力工具。逐元过程的哑元都是标量,逐元函数的结果值也是标量,而它实现并行化计算的功能就体现在任意调用它的实元可以是任意秩的数组。一般说来任意形式的实元数组都是与逐元过程的标量哑元相匹配的,不过还是有如下两种例外情形: 如果一个固有函数带有变元关键词KIND,那么相应的实元就不能是数组,而只能是一个标量整型初始化表达式。 如果一个自定义函数的哑元用于一个说明表达式,那么与该哑元相关联的实元就只能是标量。那么一个以标量为哑元的逐元过程是如何作用于数组实元的呢?实际上该过程的作用对象仍然是标量,即逐元过程作用于数组实元的每一个标量元素,然后对于每一个元素得到相应的值,所有这些值构成一个集合,就是该逐元过程作用于数组实元所得到的数组值。至于该过程对数组实元的元素的作用顺序,则是不加以规定的,因为正是不规定顺序,才能实现不同元素相互独立的计算处理进程的并行化,否则,如果强制性地规定对于元素的处理顺序,那就回到了串行序列处理的方式,无法发挥具有并行处理功能的系统的并行处理能力。当然具体的针对数组元素的运行方式,是由系统决定的,如果系统不支持并行计算,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 音乐作品创作与发行权转让协议
- 2025年建筑工程法规更新解析试题及答案
- 现代管理学课程安排与内容试题及答案
- 突破难关的建筑工程试题及答案技巧
- 市政学考察的重要性试题及答案分析
- 2024年春九年级历史下册第五单元冷战和美苏对峙的世界5.19亚非拉国家的新发展课后提分训练新人教版
- 2025年行政公文写作考试版图试题及答案
- 2025版合同终止协议书:辞职与解除劳动合同的规范化流程
- 2025借款合同模板2
- 2025私人委托合同范本
- 肺栓塞病人的术后护理
- 国开2024年秋《心理健康教育》形考任务1-9答案
- 电力运维管理平台方案设计
- 安全培训管理体系
- 机场地震应急处理与疏散预案
- 南京工业大学《化工废水处理》2022-2023学年第一学期期末试卷
- 《阻燃材料与技术》课件 颜龙 第3、4讲 阻燃基本理论、阻燃剂性能与应用
- 高三第二轮复习之文言翻译(李丽君)省公开课获奖课件市赛课比赛一等奖课件
- 服务礼仪培训课件
- 2024年江苏省盐城市中考语文真题
- 教辅资料进校园审批制度
评论
0/150
提交评论