Fortran95第6章-构造数据.docx_第1页
Fortran95第6章-构造数据.docx_第2页
Fortran95第6章-构造数据.docx_第3页
Fortran95第6章-构造数据.docx_第4页
Fortran95第6章-构造数据.docx_第5页
免费预览已结束,剩余21页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第6章 构造数据固有数据类型只是描述了问题当中出现的基本数据形式,但在实际的计算当中,计算对象往往并不只是限于那些固有数据类型,而是一些数据结构。例如:线性数学问题里面的计算对象很少是单纯的标量,而是诸如行列式,向量这样的由一系列标量组成的数据结构。甚至在计算过程中,还需要以这种数据结构的某个特定部分作为计算对象,例如行列式或向量的单独的行与列,或对角线,部分块等又例如:样品的属性值可以是多方面的,例如在测量一个导体样品的交流输运性质时,需要考虑的物理量包括电流,电压,相位等,如果以每个样品的属性值作为数据对象,那么这种数据对象就至少包括3个成员:电流,电压,相位。它们的取值的类型不同,精度要求也可能不同,这3个数值同样构成一个数据结构。因此,进一步FORTRAN 95需要以固有数据对象为基础,能够构造一定的数据结构,从而能够作到基于数据结构进行运算。本章讨论的就是FORTRAN 95构造数据结构的几种方式: 派生数据类型; 数据结构的子对象; 数据结构的成员; 数组; 指针。特别的,我们首先讨论FORTRAN 95关于数据对象的一个基本分类:变量与常量。它们是数据在程序当中的2种基本行为方式,是我们理解FORTRAN 95语言范畴的关键。6.1 数据的2种基本行为变量与常量首先我们给出几个抽象的基本概念,在这里只是给出大意而不作更加形式化的讨论,完全只是为了后面行文的方便。读者不用深究它们的精确定义,只需要在后面行文当中遇到这些概念时,知道回到这里稍加体会即可。 数据对象:计算过程当中,任何充当计算对象的数据内容,都称为数据对象,是最一般意义上的数据个体,今后提到数据对象,我们可以理解为一个固有数据类型,一个属于某固有数据类型的变量,一个常量,一个标量,一个数据结构,一个数据结构的成员或子对象,表达式求值的结果,或函数引用的执行结果(即函数值)。数据对象有数据类型(固有的或派生的),可以有数据值。可以是一个数据的集合,集合的元素数目称为秩,每个数据对象有一个秩。它可以是一个标量或是一个数组。有名数据对象的类型可以显式或隐式地规定。 子对象:子对象是某些有名对象的一部分,可由程序的其他部分引用和独立地定义。包括数据组的部分,字符串的部分(子串)和结构的部分(成员)。作为子对象的来源的数据对象称为该子对象的父对象。子对象只能用子对象描述符来引用。变量的子对象是变量。 标量:任何单一的数据对象,无论它是属于固有数据类型还是属于派生数据类型,就称为标量。 数组:一个标量的集合,如果其中每一个元素都具有相同的数据类型,相同的种别参数,那么无论它们组织成什么形式,例如行,列,块,甚至更高维度,都把这种集合称为数组。 派生数据类型:一个标量的集合,如果其中元素的种别参数不同,或者数据类型不同,那么把这个集合称为结构,或派生数据类型。从程序运行这么一个动态过程的角度来看,特别是基于下面的基本计算模式:输入数据 程序运行输出数据数据可以有2种基本行为方式: 数据对象在整个程序运行过程当中保持不变; 数据对象在整个程序运行过程当中发生变化。保持不变的称为常量,发生变化的称为变量。FORTRAN 95中常量与变量,标量与数组这些概念与早期的FORTRAN版本有微妙的差异。请熟悉FORTRAN77的读者注意。 常量有两种,一种是字面常量,早期的FORTRAN版本称为常量;另一种是命名常量,早期的FORTRAN版本称为常数符号名,显然新的名称突出表达了该数据作为常量的特点,它与字面常量的最大差别在于有一个名字。这个名字可以在它的定义作用域当中任意引用。它们的共同点在于,它们的值在可执行程序执行期间是不变的。 字面常量是标量,根据它的字面的句法构成来表明其数据类型,种别参数以及值。在一个可执行程序中,所有具有相同形式的字面常量具有相同的值。命名常量则是一个与具有PARAMETBR属性的名字相结合的常量。它的数据类型,种别参数和取值,都需要预先定义,而一旦定义之后,在其定义的作用域内都不会改变,而且这个名称也必须接受唯一解释的。 变量的值即使用DATA语句给以初始化也能在程序运行过程当中发生变化。而FORTRAN 95的变量包括了早期FORTRAN标准里面所谓的变量和数组。具有DIMENSION属性的名字在FORTRAN 95标准里面也称为变量,属于数组。 数组是标量的有序集合,它在FORTRAN 95标准当中有许多新的扩充,将在后面作详细讨论。在程序运行过程当中,对数据的使用首先必须是定义,然后才能是引用,所谓定义和引用的差别在于: 对于一个变量,当程序中确定其值时称为定义,而使用其值时称为引用,已经定义了的变量可以重定义或者回复为无定义。例如,在程序单元中使用DATA语句使得一个变量具有初值,在程序执行过程中又可以对它赋值而得到重定义;如果该变量不具有SAVE属性的话,当该程序单元执行RETURN语句或END语句后,它就回复为无定义状态。 对于一个常量,它的值可以被引用但不能被重定义。命名常量虽然出现的形式是一个名字,而不是一个具体的值,但它只是常量的一个便于引用的标记。【例6-1】X = 0N = X+1在第一个语句当中,变量X被赋值为常量值0,这个语句的执行与X是否在此之前被定义为其他值无关,总之此时X得到一个定义,此后只要X没有被重定义,它就能用值0被程序引用。在第二个语句当中,X被引用,并且在执行加1的运算之后,得到的值被赋值给N。在第一个语句当中的X和在第二个语句当中的N都不是被引用,而是被定义,而在第二个语句当中的X属于被引用。变量(variable)的句法形式(R601)为;scalar-variable-namearray- variable-namesubobject其中子对象(subobject)的句法形式(R602)为:array-elementarray-sectionstructure-componentsubstring变量可以是任意的数据类型,在一定的上下文当中,变量也必须取一定的数据类型。常量父对象的子对象不可能是变量。6.2 数据的结构-派生数据类型五个固有数据类型已经能够作到描述,或者近似描述整数,实数,复数,逻辑值,字符串这五类常用数据对象。考虑一下FORTRAN建立这五种相对独立的数据类型的出发点,是有助于我们进一步理解语言的数据描述这个重要任务的。首先考虑整数。似乎整数是一切计算的基本出发点,这里是没有省略的余地的,想像一下,没有了整数,我们还能做什么计算呢?别忘了Kronecker的名言:“上帝创造了自然数,其余都是人造的”。然后就是实数。由于FORTRAN本质上只能表示有限位有理数,因此实际上也可以通过整数的除法来得到实型数据的近似表示,比方说用一个整数二元组(37,71)来表示37/71这个有理数,相应的也可以用来近似地表示实数,我们可以来衡量一下这样做的利弊: 好处就是可以不用建立实型数据类型,也就是不用引入小数点,减少了一个数据类型,也就简化了FORTRAN语言的实现; 但同时坏处就是把麻烦留给了程序作者,在处理涉及实数的问题时,肯定会因为不能直接表达实数而增加算法的复杂性,甚至在语言本身,也有可能不得不增加一些函数之类的语言成分,用来解决由此产生的一些问题。所以FORTRAN的解决方案就是直接引入实数的有限有理小数表示(实际上一般的程序语言都是这样做的),对于语言本身来说,无非就是增加了对小数点的语义解释,增加了对浮点数值的多种存储模式(FORTRAN使用种别参数来进行分类)的建立等,由此而可以在语言当中自然的使用有限有理小数所表示的实数。再来分析一下复数。本质上复数就是一个实数的二元组,而且这个二元组与复数是一一对应的,因此可以很完美的通过运用二元实数组来代替复数,这也是大多数程序语言的处理方式。但是FORTRAN的选择是建立一个独立的复型数据类型,尽管得为这个独立的数据类型定义相应的运算,但是由于FORTRAN主要就是面向科学计算的语言,因此FORTRAN能够直接写出复数,成为FORTRAN的一个优点,极大地方便了程序作者。由此可见,建立一个数据类型的主要功能就是面向问题,以能够直接描述算法为目的。那么我们是否有可能从实际问题当中归纳出一些数据类型,然后可以期望这些数据类型就可以满足任何问题的需要呢?显然是不可能的。一方面,程序语言必须能够精确描述任何需要用它来解决的问题当中出现的数据;另一方面我们不可能给每一个新问题当中出现的独特的数据结构形式规定一种相应的数据类型,唯一的解决方法,就是在FORTRAN里面约定一种语法机制,可以依据这种语法机制来描述与问题要求相适应的数据类型。反过来只要是依据这个语法机制构造出来的语言结构都能被FORTRAN编译器辨认为一种数据类型,从而知道按照什么存储模式来存储该数据,也知道应该对该种数据施加什么运算。 这就是FORTRAN从90版标准以来设置派生数据类型的出发点。鉴于固有数据类型被认为是真实世界最基本的数据类型,任何的数据对象,或者就是属于某种固有数据类型,或者就是可以由一系列固有数据描述,因此构造派生数据类型的基本思想就是认为一个派生数据等于一系列成员的集合,这个集合构成一种数据结构,而每一个成员或者仍然属于一个派生数据类型,或者就是属于某种固有数据类型。这样对一个派生数据类型的描述,就归结到对它的成员的描述,另外再加上给出该数据类型的名称,并可选择性地描述其他属性。这样得到的所谓派生数据类型,和五种固有数据类型不同的地方主要在于: 每一种固有数据类型都拥有一个特定的名称,用来作为数据声明的关键词;而派生数据类型则需要程序作者自己首先定义一个名称,然后才能在后面作为关键词进行声明。 每一种固有数据类型都已经定义好了自己的常量书写方式;而派生数据类型则依据其成员的常量形式来确定自己的常量的形式。 每一种固有数据类型都已经定义好了自己的特定的运算;而派生数据类型则需要程序作者自己预先依据实际问题的需要来定义好相应的运算。 每一种固有数据类型都已经定义好了自己的取值范围以及相应的多种存储模式;而派生数据类型,则完全依据其成员的取值范围与存储模式,来确定自己的取值范围和存储方式。 派生数据类型可以在数据类型的定义里,就给出该数据类型的默认初始值,使得今后属于该类型的数据对象都一定取该初始值;而固有数据类型的定义里面不含有定义初始值的部分,要想显式地给出变量初始值,得需要使用数据类型声明语句或者DATA语句。 固有类型的名称是全局性的,在任何环境总是可访问的,而派生类型只可在其定义的作用域内访问它们。FORTRAN定义的数组同样是数据对象的集合,不过派生数据类型与数组的差别在于组成数组的各个成员必须是相同的数据类型,而派生数据类型的特点就在于它的成员可以是属于不同的数据类型。派生数据类型的成员可以仍然是某个派生数据类型,但数组的成员就不能是数组。要想让数组的成员为数组只能通过间接的方式,即取数组的唯一成员为一个派生数据类型,然后该派生数据类型的成员取为数组。对于这样构造出来的与具体问题密切相关的派生数据类型,由于其所具有的内部结构个性太强,显然我们需要定义一些与这种结构相关的特征属性,可以用来描述这种个性: 正是由于一个已经定义好的派生数据类型具有完全个性化的内部结构,因此如果一个派生数据类型是定义在一个模块内部的话,自然的问题就是这个派生数据类型的面向模块外部的可访问性如何控制。在默认情形下,一个在模块内部定义的派生数据类型可以被模块外部的任意程序单元访问,这个默认状态等价于使用PUBLIC语句。反之,也可以通过使用PRIVATE语句来阻止模块外程序单元的访问。不过,由于一个模块包含许多不同层次的数据对象,因此PRIVATE的作用对象必须是精确指定的,这里分为以下几种情况: 每一个对象都可以单独地通过使用属性PUBLIC或PRIVATE来指定其可访问性。 对于一个派生数据类型,可以在定义类型的语句当中设置可选的PUBLIC或PRIVATE描述符。 使用单独的针对类型名称的PRIVATE语句或在类型定义当中使用PRIVATE描述符,可以把对该类型访问控制在本模块之内。 在类型定义的内部使用单独的PRIVATE语句,可以只控制该派生类型的特定的组元的模块可访问性,而不影响整个类型的默认下的可访问性。 组成一个派生类型的各个成员可以按照一定的顺序进行存储,这是通过在派生类型的定义当中运用SEQUENCE语句得到的。一个派生类型的定义当中,成员的叙述上的顺序并不表示它们具有相应的存储上的顺序,只有在定义当中设置一个SEQUENCE语句,才能对这些成员的存储进行排序。从而可以进一步对这些成员应用COMMON语句或EQUIVALENCE语句。 一个派生数据类型的取值范围显然就是它的所有成员的取值范围的组合,使用集合论语言就是各成员值集的乘积。FORTRAN规定了一个语法机制来给派生数据类型构造值的形式,例如用来定义命名常量,这样就可以在类型声明语句或PARAMETER语句当中使用其命名常量。这种语法机制称为派生数据类型的结构构造器。至此我们可以看到FORTRAN语言对派生数据类型的定义,不是以某种真实世界的自然的数据集合为依据,而是提供一种描述方式,并且在语言当中约定,只有运用这种方式进行描述,就可以成立为一种数据类型。而对于通过这种方式成立的数据类型,同样包含四个方面的基本属性: 拥有名称,以备引用; 取值在一定的集合上; 可以施加的运算; 常量的写法。下面就从这四个方面来说明派生数据类型。6.2.1 派生数据类型的构造给出一个派生数据类型的名称,同时意味着要给出该派生数据类型的完整定义或着说描述。一个完整的派生数据类型的句法形式(R422)为:TYPE , access-specification : type-name private-sequence-statementcomponent-definition-statement component-definition-statementEND TYPE type-name其中: 可选的访问说明(access-specification)为关键词PUBLIC或PRIVATE。 类型名称(type-name)需要由程序作者自己给出,最好是有意义的英文词汇或缩写。 私用序列语句(private-sequence-statement)为PRIVATE语句和SEQUENCE语句。每一条成员定义语句(component-definition-statement)(R425)包含一个数据对象的类型说明(R502),其句法形式为:type-specification , component-attribute-list : component-declaration-list 其中: 类型说明(type-specification)表示一个固有数据类型,或者是一个已经定义过的派生数据类型,不过如果这个描述符后面带有POINTER属性,由于指针的赋值特性,那么该成员就可以表示任意可访问的派生类型,包括正在被定义的派生类型自身。 成员属性(component-attribute)(R426)只能是POINTER或DIMENSION,即对于指针性质的成员,使用可选的属性POINTER;而对于数组成员,使用可选的属性DIMENSION,后面还可以再加上数组描述。POINTER和DIMENSION这两个属性可以同时出现。 成员声明(component-declaration)给出成员的名称。成员声明(R428)的句法形式为:component-name(component-array-specification) & *character-length component-initialization其中: 成员数组说明(component-array-specification)是可选项,放置在括号当中。如果该数组具有指针属性,那么它的形状未定,否则就是显形状。在显形状的描述当中,数组的界都必须是标量整型常量表达式。如果在这里没有指出数组的界,那么一定要在后面的DIMENSION属性里说明。 字符长度参数(character-length)是一个可选的标量整型字面常量,它必须以星号开头。当然这个参数只能用于修饰字符型成员。成员初始化(component-initialization)(R429)取下面2种形式之一:=initialization-expression=NULL()其中: 初始化表达式(initialization-expression)用来说明非指针成员的默认初始值。 =NULL()即把成员作为指针赋值为不带变元的固有函数NULL的结果,表示指针成员的未结合或空置这2种初始结合状态。初始化指针状态的用处在于,很多情形下,要设置一个指针,都必须首先要求该指针具有确定的结合状态。构造了一个派生数据类型之后,要使用这个派生类型,自然需要声明属于该类型的变量,对于派生类型来说,变量的声明采用如下句法形式:TYPE(type-name),attribute-list: entity-list注意这个TYPE语句和定义派生类型的TYPE语句在句法上有差别:声明变量时,TYPE后面必须接括号里面的变量所属派生类型名称;而定义派生类型时,后接不带括号的派生类型名称。构造了一个派生数据类型之后,经常需要引用其中的成员,引用的句法形式为: parent-structure % component-name其中父结构(parent-structure)指需要引用的成员所属的派生数据类型(或数据结构)。派生数据类型构造的一般规则如下: 派生数据类型的定义当中给出的类型名称不能与任何固有数据类型的名称重合,也不能与任何本模块能够访问的其他派生数据类型的名称重合。必须保证该名称在它的有效作用域内是指称同一个类型对象。而派生数据类型内部成员的名称的作用域只局限于该派生类型内部,因此不同派生类型的成员可以使用相同的名称,即使这些不同的派生类型属于同一个作用域。同样的,不同作用域当中如果出现了相同的派生类型名称,那么它们实际上是不相干的数据对象。 END TYPE语句后面或者省略类型名称,或者后接该类型的名称,不能出现不一致的情况。 一个作用域内,一个派生类型的名称只能定义一次。 在一个派生类型的定义当中,PRIVATE语句只能出现一次。 在一个派生类型的定义当中,SEQUENCE语句也只能出现一次。 只有当一个类型定义是放置于一个模块的规则说明部分时,才能使用访问控制符PUBLIC或PRIVATE。 只要派生类型的定义当中出现SEQUENCE语句,就说明该派生类型的所有成员都必须具有顺序属性 (即其中也必须出现SEQUENCE),并且在定义中成员定义的次序就说明了该类型的对象的存储序列。 在一个派生类型的定义当中,必须至少包含一个成员的定义。 在成员定义当中,任何特定的成员属性都只能出现一次。 一个成员只有在具有指针属性时,才可声明为属于被定义的派生类型本身。 任何非指针的数组成员,都必须表示为显形状形式,其上下界都是整型常量表达式。 如果成员是指定了长度的字符型数据,那么它的长度必须是整型常量描述表达式。如果没有指定长度值,那么默认值为1。 如果进行成员初始化,那么必须使用双分号间隔符。 在2种初始化方式中,初始化表达式只能用于非指针变量,也可以说等价赋值符号(=)只能用于非指针变量。而固有函数NULL()则是用于指针变量的。 初始化表达式只能在派生类型的作用域当中取值。 一个派生类型内部的成员当中,可以只有部分成员是默认初始化的,即取得默认初始值,或取得默认初始结合状态,而不是一定要求所有成员都必须初始化。 一个成员如果是数组,那么它的默认初始值必须满足数组的定义,可以是一个数组构造器,也可以是一个标量,这个标量表示了该数组成员的每个元素的初始值。下面给出不同情形下构造派生数据类型的例子:【例6-2】TYPE SETINTEGER N, MEND TYPETYPE (SET), DIMENSION (2, 2) : a, b(3) 在这个派生类型的定义当中,a和b都是属于派生数据类型SET的数组变量。【例6-3】TYPE CURRENTREAL:HIGH=5.5, LOW=1.2END TYPE CURRENT在这个派生类型的定义当中给出了其唯一实型成员的初始值.【例6-4】 TYPE employee_name CHARACTER(25) last_name CHARACTER(15) first_nameEND TYPETYPE employee_addrCHARACTER(20) street_nameINTEGER(2) street_numberINTEGER(2) apt_numberCHARACTER(20) cityCHARACTER(2) stateINTEGER(4) zipEND TYPE在上面2个派生类型的定义当中,一个派生数据对象是另一个派生数据对象的成员。【例6-5】 例6-4中定义的数据对象还可以被第3个数据类型引用,如:TYPE employee_dataTYPE (employee_name) : nameTYPE (employee_addr) : addrINTEGER(4) telephoneINTEGER(2) date_of_birthINTEGER(2) date_of_hireINTEGER(2) social_security(3)LOGICAL(2) marriedINTEGER(2) dependentsEND TYPE【例6-6】TYPE ARTICLE CHARACTER(LEN=100)ABSTRACT INTEGER LINES CHARACTER,POINTER:TEXT(:) END TYPE ARTICLE在这个派生数据类型里面包含一个指针成员。【例6-7】TYPE LINKINTEGER VALUETYPE(LINK),POINTER:PREVIOUS=NULL()TYPE(LINK),POINTER:NEXT=NULL()END TYPE LINKTYPE (LINK),POINTRE:A_LINKALLOCATE(LINK)在这个派生数据类型当中,指针成员的类型已经被定义,这种派生数据类型被广泛应用于链表与树结构。【例6-8】TYPE,PRIVATE:FILEINTEGER FILE_NOCHARACTER(LEN=30) FOLDER_NAMECHARACTER(LEN=10) ACCESS_LEVELEND TYPE这个派生数据类型定义在一个模块当中,但是对这个模块保持私用状态。 6.2.2 派生数据类型的取值,运算,以及常量表达式一个派生数据类型的值集就是它的成员的值集的乘积,也就是它的成员的所有可能取值的所有可能的组合. 我们知道每个固有类型都有一个固定的值集,在任何环境下引用固有类型,这个值集都是一致的,而派生类型的数据值集则完全是根据它的成分的数据值集确定的。由于派生数据类型本质上是一种数据结构,因此对派生类型对象的运算的定义,需要使用具有OPERATOR界面的函数,而赋值则使用具有ASSIGNMENT界面的子例行程序。详细的有关子例行程序及其界面的讨论参见第13章。上面给出的派生数据类型的定义过程,实质上就是给出了一个语法机制,根据这个语法机制构造出来的语言结构肯定能够被FORTRAN编译器识别为派生数据类型对象,即或者是属于该派生数据类型的变量,或者是属于它的变量的具体取值。因此派生数据类型的定义自动给出它的取值的结构构造器,该结构构造器生成一个数据序列,其中每一个数据对应着该类型数据对象的一个成员的取值。而该类型数据对象的所有成员的所有取值的任意组合,正是该结构构造器所能生成的所有常量。通过这样的结构构造器生成的常量,也可以用来给定义为该派生类型的命名常量赋值。由于派生数据类型的成员除了可以是固有数据类型之外,还可以是任意的数据结构,特别是设置为 FORTRAN的一种重要的数据结构-数组。在这种情况下,特别定义了一种用来描述派生类型的数组成员的语法机制,称为数组构造器。 当数组构造器的取值全是常量表达式时,得到数组值常量表达式,这样一个表达式可以作为一个命名常量的数组成员。数组构造器不仅是可以用来给出一个数据结构的成员的值,也可以用来给出任意数据类型的数组值。6.2.3 数据结构构造器一个属于派生数据类型的常量的句法形式(R431)为:type-name (expression-list)其中类型名称(type-name)为某个派生数据类型的名称。表达式列表(expression-list)为给出派生数据类型的各个成员值的表达式序列。结构构造器的一般规则如下: 结构构造器不能在它所代表的派生数据类型被定义之前出现,也就是说它给出的派生类型名称不能置空。 结构构造器给出的值必须与type-name所指的派生类型的成员一一对应,书写顺序也必须和派生类型定义里各成员的排列顺序保持一致。为了保证与各个成员在类型,种别参数,长度以及秩的一致,有时可能需要对值进行适当的转换。 如果派生类型包含数组成员,那么在各个成员值的表达式序列当中数组的形状,必须与类型定义当中的该数组成员的形状保持一致。 如果派生类型包含指针成员,那么在各个成员值的表达式序列当中,该指针所对应的值,必须是相应指针赋值语句当中的有效目标。 如果结构构造器的所有成员值都是常量表达式,那么该构造器实质上就构成一个派生类型的常量表达式。【例6-9】 考虑下面的派生数据类型:TYPE EMPLOYEE INTEGER ID CHARACTER(LEN=40) NAMEEND TYPE EMPLOYEE可以得到下面的结构构造器: EMPLOYEE(5645, 陈辉)【例子6-10】 下面的派生数据类型包含另外的派生类型作为成员:TYPE ITEMREAL COSTCHARACTER(LEN=30) SUPPLIERCHARACTER(LEN=20) ITEM_NAMEEND TYPE ITEMTYPE PRODUCEREAL MARKUPTYPE(ITEM) BOOKEND TYPE PRODUCE这时,就需要使用内嵌的结构构造器来得到成员取值: PRODUCE(.70, ITEM (.25, XIN HUA, A BOY)6.3 子串从符号的意义上来看,下面两个数据对象都是属于字符串:ASDFJASDF12349123123如果我们把12349123123看成是标量,那么没有理由不把ASDFJASDF也看成是标量。然后进一步由标量构造成数组,同样可以用字符串作为数组的分量,由于字符串的参数就是它的长度,因此在用字符串构成数组时,有必要限制它们的长度保持一样。要以长度为参数构造字符串,一个自然的途径就是从一个字符串当中,按照长度截取其连续的某个部分,这就提出了子串的概念。子串的句法形式(R609)为:parent-string(substring-range)其中父串(parent-string)的句法形式(R610)为以下诸种形式之一:scalar-variable-namearray-elementscalar-structure-componentscalar-constant子串范围(substring-range)的句法形式(R611)为:starting-position:ending-position其中子串的始点和终点为标记父串里字符位置的下标的整型表达式,始点为子串最左边的字符在父串里所处的下标表达式,终点为子串最右边的字符在父串里所处的下标表达式,显然,始点和终点都处于1到LEN之间,LEN为父串的长度。如果始点值大于终点值,那么系统认为该子串的长度为0。给出始点和终点,就能从父串截取到相应的子串。子串的一般规则如下: 字符串属于字符型数据类型,父串与子串都是字符串。 字符串的下标是按照从左到右的顺序,从1开始,一直到LEN,LEN为字符串的长度。 子串是父串里从第i个字符开始,到第j个字符结束的连续的一段字符串,I和j都大于等于1,小于等于LEN,下标为i的字符称为子串的始点,下标为j的字符称为子串的终点。 如果始点与终点的下标表达式的值不是整型,则必须转换为整型。 如果i没有给出,默认i为1;如果j没有给出,默认j为LEN。 子串的长度可以是0,但不能是负值,如果给出的i大于j,则系统认为子串的长度为0。计算子串的长度使用函数MAX,公式为:MAX(ending-position - starting-position + 1,0) 如果父串的长度为0,那么它的任意子串的长度都是0。【例6-11】CHARACTER(10)signal_peptidesignal_peptide = “HLA-A*0301”signal_peptide(1:3) = “HAI”PRINT *, signal_peptide(7:10)在这个例子里,signal_peptide被定义为长度为10个字符的字符型变量,在第一个赋值语句里,该变量被赋值为字符串“HLA-A*0301”,在第二个赋值语句里,该变量的下标从1到3的子串被引用,并且被赋值为字符串“HAI”,这样signal_peptide的值就变成了“HAI-A*0301”,接下来的打印语句的执行结果是打印下标从7到10的子串,为“0301”。【例6-12】CHARACTER*8 A, Asteroid Asteroid = 2000 DG8A = Asteroid (1:4)在这个例子里,首先定义了长度为8的2个字符型变量A和Asteroid,然后给出2个赋值语句,对字符型变量Asteroid赋值为2000 DG8,而对A赋值为Asteroid的一个子串2000。【例6-13】TYPE nearly_isotropic_cometsINTEGER inclinationREAL semi-major_axis, eccentricity, perihelion_distance, Absolute_MagnitudeCHARACTER*12 designationEND TYPE nearly_isotropic_comets TYPE(nearly_isotropic_comets) 2000YEARCHARACTER*12 ASTEROID, BIG_INCLINATION(5)由上面定义的字符串,可以得到如下合法子串:BIG_INCLINATION(2) (1:4) !父串为数组元素2000YEAR % designation(6:12)!父串为结构成员ASTEROID (1:4)!父串为标量变量“ABCDEFGH”(N:N+1) !父串为字符常量如果一个数组的分量为字符串,那么由这个数组构造子串,可以有很多种合法的可能性,例如,BIG_INCLINATION(:) (1:4),表示对一个由子串组成的数组片断的引用;BIG_INCLINATION(1:4) (1:4),同样表示一个由子串组成的数组片断的引用。因此如果要从一系列长度一致的字符串构造相同下标范围的子串,可以通过对数据组的片断引用而实现。不过必须注意数组片断下标放置于子串下标之前。由于表示数组片断的句法形式和表示子串下标范围的句法形式一样,因此必须使用这个顺序安排,才能得到无歧义的表示。【例6-14】下面2个引用含义完全不同:BIG_INCLINATION(:) (1:4)BIG_INCLINATION (1:4)前者表示由5个子串构成的数组,后者表示由4个父串构成的数组。 由“ABCDEFGH”(N:N+1)得到的子串既非常量,也非变量,因为它的下标是一个变量,得到的子串称为常量子对象。参见表达式的形式。6.4 结构成员所谓结构就是一个集合,其元素可以是任意数据类型,也可以是标量或数组,一个结构至少包含一个元素,这样的结构本身就是一种派生数据类型,由同一类型的多个结构又可以组成一个数组。因此我们可以看到,结构的定义包含递归的成分,这样能够保证提供最大的灵活性,以便足够充分地描述实际问题当中出现的数据结构,减少因为数据结构复杂而导致算法复杂的压力。定义一个结构之后,如果要引用其成员,可以直接使用如下形式:parent %component (section-subscript-list) . %component (section-subscript-list)其中:parent为父结构名称。百分符号(%)称为成员选择符。component为其左边邻接父结构或成员的成员 section-subscript-list表示片断下标列,如果该下标列包含下标三元组或者向量下标,那么就表示引用一个数组片断。由于结构定义具有递归成分,因此要引用结构的成员,就会出现很多的情形,更加抽象的理解是把结构成员的引用(R614)看是一种数据引用(R612)的形式。所谓数据引用,是把任何被引用的数据看成某个数据集合的部件,这种包含关系可以任意多层地嵌套,因此结构成员的引用也可以运用句法形式(参见附录B)递归定义如下:part-reference %part-reference其中部件引用(part-reference)(R613)的形式定义为: part-name (section-subscript-list)其中片断下标(section-subscript)(R618)的形式定义为以下几种情况之一: subscriptsubscript-tripletvector-subscript有关数组片断的下标参见6.5.5节。结构成员引用的一般规则如下: 结构成员的引用作为数据部件的引用,要求被引用的部件是属于多个部件之中的一个。 作为结构成员引用的数据引用的最右边的被引用的部件,必须是一个部件名称,如果该部件引用具有形式:part-name (section-subscript-list)那么被引用的就是一个数组片断,在最简单的情形下,是一个数组元素。 在上述数据引用定义里,除了最右边的部件名之外,每一个部件名称都必须是指称一个派生类型数据对象。 数据引用时,除了最左边的部件名之外,每一个部件名称都必须是其左边邻接部件名称所表示的派生类型的成员。 在上述数据引用定义里,任意位置的部件都可以是数组,一个数组右边邻接的成员不能具有指针属性。 如果父结构具有INTENT, TARGET, 或PARAMETER属性,则其结构成员也具有同样的属性。 在父结构还没有被声明之前,其结构成员不能被引用。 结构成员如果含有非0秩的部件,那么它的秩就是非0秩的部件的秩,否则秩为0。结构成员的类型和类型参量(如果存在的话),与最右边的部件保持一致。【例6-15】 下面定义一个包含2个成员的派生数据类型:TYPE EMPLOYEEINTEGER IDCHARACTER(LEN=40) NAME END TYPE EMPLOYEE然后声明变量CONTRACT 属于派生数据类型EMPLOYEE,并且引用该变量的一个成员。 TYPE(EMPLOYEE) : CONTRACT CONTRACT%ID【例6-16】 下面定义一个派生类型作为另一个派生类型的成员:TYPE DOTREAL X, YEND TYPE DOT.TYPE SCREENTYPE(DOT) C, DEND TYPE SCREEN然后声明变量M属于派生类型SCREEN,并且引用该变量的几个成员:TYPE(SCREEN) MM%C M%D (both of type DOT); M%C has components M%C%X M%C%Y of type REAL.【例6-17】 下面定义一个包含数组成员的派生类型:TYPE CAR_INFOINTEGER YEARCHARACTER(LEN=15), DIMENSION(10) : MAKERCHARACTER(LEN=10) MODEL, BODY_TYPE*8REAL PRICEEND TYPE.TYPE(CAR_INFO) MY_CAR【例6-18】下面定义派生类型以及属于该类型的2个变量:TYPE CHARGEINTEGER PARTS(40)REAL LABORREAL MILEAGEEND TYPE CHARGETYPE(CHARGE) MONTHTYPE(CHARGE) YEAR(12)如下引用的数组都是合法的:MONTH%PARTS(I) ! An array elementMONTH%PARTS(I:K) ! An array sectionYEAR(I)%PARTS! An array structure component (a whole array)YEAR(J)%PARTS(I) ! An array elementYEAR(J)%PARTS(I:K) ! An array sectionYEAR(J:K)%PARTS(I) ! An array sectionYEAR%PARTS(I)! An array section【例6-19】 下面定义的派生类型包含一个已有定义的指针成员: TYPE NUMBERINTEGER NUMTYPE(NUMBER), POINTER : START_NUM = NULL( )TYPE(NUMBER), POINTER : NEXT_NUM = NULL( )END TYPE这样的结构可以用来构造链表,注意其中的指针为默认的非结合初始状态。【例6-20】 下面定义一个私用派生类型:TYPE, PRIVATE : SYMBOLLOGICAL TESTCHARACTER(LEN=50) EXPLANATIONEND TYPE SYMBOL这个派生类型属于模块私用类型,该模块可以被其他作用域访问,但该类型只能被本模块引用。6.5 数组数组同样是一个集合,它的元素必须是标量,其标量元素可以属于任何的固有数据类型,派生数据类型,甚至是结构,但数组最关键的一个特征是:数组的所有标量元素必须属于同一个数据类型,并且具有同样的种别参数。这个特征决定了数组是一种功能强大的数据结构,因为存在大量的实际问题,需要用同一个计算过程来处理大规模的同种类型的数据,因此FORTRAN 95提供了强大的内部运算和固有函数,专门用来处理整个数组,或者数组元素,或者数组片断,同时硬件的并行化发展,也为这一类通常是极大规模的运算,提供了优化的解决方案。在FORTRAN的早期版本里面,数组是作为数据的一种属性加以描述的,所使用的关键词为DIMENSION。这个规则一直被保留下来,因此任何数据对象只要是具有DIMENSION属性,就一定为数组。6.5.1 数组的结构数组的结构可以抽象地理解为多维离散空间的点的集合,即这些点都有相同的维度,在每一维上的坐标取值都是离散的标量。一个具体的例子就是表格。实际上表格总是可以用二维数组加以描述,例如取上章的如下表格: 表6-1表格可以看成数组 作用单元的种类语句 主程序模块数据块外部子程序模块子程序内部子程序接口块USE语句YYYYYYYENTRY语句NNNYYNNFORMAT语句YNNYYYN其他声明YYYYYYYDATA语句YYYYYYN导出类型定义YYYYYYY接口块YYNYYYY语句函数YNNYYYNCONTAINSYYNYYNN可执行语句YNNYYYN如果该表格的行,即作用单元的种类看成是一个维度a;把该表格的列,即语句的种类,看成是另一个维度b,那么该表格的每一个元素都可以用符号E作用单元的种类,语句的种类,或者Ea,b,来表示,其中a可以取7个值:主程序,模块,数据块,外部子程序,模块子程序,内部子程序,接口块。b可以取10个值:USE语句,ENTRY语句,FORMAT语句,其他声明,DATA语句,导出类型定义,接口块,语句函数,CONTAINS,可执行语句。而每个元素Ea,b取值为符号Y或N。这样我们就把一个表格完全的用一个数组加以描述了。从上面的例子可以看到,数组的结构是由维度,和

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论