Professional Documents
Culture Documents
• 作为一门诞生于上个世纪50年代后半期的高级计
算机语言,Fortran在这个C/C++、Java等新兴语
言大行其道的时代仍然活跃在人们的视野之中。
Fortran语言的长项在于数值计算,在科学研究和
工程设计领域有着广泛的用途。在描述数学语言
的自然性方面,Fortran同现存的其他高级语言相
比有着明显的优势。对于科研工作者和工程技术
人员而言,Fortran语言的易学性和易用性是公认
的。
1.1 Fortran起源
• Fortran是英语中“公式(Formula)”和“翻译(Translation)”
两个单词取前几个字母的缩写形式,意即“公式翻译”。Fortran
主要面向科学研究、工程设计或企事业管理中经常遇到的能够用
数学公式表达的数值计算问题。因为可以像抄写教科书里的公式
一样书写数学表达式,它比用英文书写的自然语言更接近数学语
言。这使得Fortran在科研工作者和工程技术人员中拥有庞大的使
用人群。Fortran语言是第一个被正式推广的高级计算机语言。在
四十多年的发展历程中,它始终是数值计算领域所使用的主要语
言。本小节主要介绍Fortran的起源。
• 第一代Fortran语言是在1954年提出来的,称为Fortran I。它于
1957年在IBM 704计算机上得以实现。其开发者巴科斯的目标是开
发一种容易理解、简单易学又几乎能像汇编语言一样高效运行的
计算机语言,他和他的团队在这一点上取得了极大的成功。但是
这一计划在最初阶段并不被人们看好,这其中包括巴克斯的上司
冯·诺依曼。
1.2 Windows下Fortran编译器使用
• 由于Windows操作系统有着庞大的用户群体,因此Windows系统下
的编译器开发非常活跃。目前市场上常见的Fortran编译器包括
Visual Fortran系列、Salford的FTN系列、G95/gFortran的
Windows版本、Absoft公司的Absoft Fortran,Macrovision公司
的PGI Visual Fortran等。这其中,影响较大、功能较全、性能
较好、应用最广泛的Fortran编译器提供了集成开发环境的Visual
Fortran系列。
• Visual Fortran系列编译器起源于微软公司开发的Fortran Power
Station 4.0编译系统。该系统结合了当时微软最新开发的
Develop Studio集成开发环境(IDE),使得Fortran程序的开发
方式跟上了时代的潮流。但是该编译系统在实际使用中出现的问
题较多,例如没有严格的数组越界检查功能等。这套工具随后被
卖给了数字设备(DEC)公司继续开发,随后产生了一个曾经非常
流行的Fortran编译器系统。
1.2.1 安装Compaq Visual Fortran
• Compaq Visual Fortran的安装过程比较简单。用
户将安装光盘放入CD-ROM后,计算机会自动运行
光盘中的安装程序。
编译器名称和版本号
编译器名称 版本号
厂商或组织
Absoft Absoft Pro Fortran 95,简称APF95 8.0
• 1.2节和1.5节分别介绍了几种常用的Fortran编译器和源代码编辑器。在
对软件的介绍过程中,我们已经开始了与Fortran源代码的初步接触。在
光盘\Program\chap01\文件夹中也提供了一段最简单的源代码。本节就主
要通过对一个基本程序的介绍来使大家对Fortran程序有一个初步的认识。
• 对于了解C/C++语言的程序员肯定都非常熟悉一个著名的C/C++入门程序,
即Hello! World。该程序通过最简单的几句代码使初学者对C/C++程序的
结构有了一个基本的了解。本节将借鉴这一做法,对Fortran程序的基本
结构进行讲解。
• 用UltraEdit、ZionEdit、gEdit或是Compaq Visual Fortran编辑窗,输
入如下代码段。
• PROGRAM HELLO
• IMPLICIT NONE
• I = 3000 * 3
• WRITE(*, *)l
整型数据的种别
取值范围 (-28n-1—28n-1-1) 备注
种别值n
实型数据的种别
取值范围 备注
种别值
REAL([KIND=]4) 或 10-38 到 1038,7位有效数字 缺省值
REAL*4
REAL([KIND=]8) 或 10-308 到 10308,15位有效数字 等价于双精度型DOUBLE
REAL*8 PRECISION
REAL([KIND=]16) 或 仅用于OpenVMS、Tru64 UNIX、
REAL*16 Linux操作系统
5.2.3 复型数据的种别
• 复型数据在Compaq Visual Fortran中被划分为3
种种别,如表所示。每种表示实型数据的方法都
可以用来表示复型数据的实部和虚部,数据的取
值范围可以参考前一小节。需要注意的是简写写
法与完整写法之间的差别。
复型数据的种别
备注
种别值
COMPLEX([KIND=]4) 或 COMPLEX*8 缺省值
COMPLEX([KIND=]8) 或 COMPLEX*16 等价于双精度复型DOUBLE COMPLEX
备注
种别值
LOGICAL([KIND=]1) or LOGICAL*1
LOGICAL([KIND=]2) or LOGICAL*2
所代表的数学符号 意义描述
运算符定义
.GT. > 大于
.GE. ≥ 大于等于
.LT. < 小于
.LE. ≤ 小于等于
.EQ. = 等于
.NE. ≠ 不等于
7.2.2 关系表达式
• 介绍了关系运算符之后,现在来介绍关系表达式。关系表达式是
最简单的一种逻辑表达式,它的一般形式为:
• <关系运算量> <关系运算符> <关系运算量>
• 关系元算量可以是算术量,也可以是字符量。这里只介绍算术量
的关系表达式,字符量的关系表达式将在字符表达式的小节中进
行介绍。算术量可以是数值型常量、数值型变量、数值函数,还
可以是算术表达式。下面是一些关系表达式的实例。
• A+B>3.5 等价于 A+B.GT.3.5
• 40>=40 等价于 40.GE.40
• 40<C 等价于 40.LT.C
• COS(B)<=0.5 等价于 COS(B).LE.0.5
• (M+N)*X==0.0 等价于 (M+N)*X.EQ.0.0
• X-Z.NE.(Y+X) X-Z.NE.(Y+X)
7.3 逻辑运算
• 前面一节介绍了最简单的逻辑表达式,也就是关
系表达式。关系表达式只能用于表示关系运算量
是否满足某一种关系。如果问题复杂一点,关系
表达式就无能为力了。例如关系表达式无法表示
变量A既要大于B,同时还要小于D的情况,即数学
表达式B<A<D。这时就需要用到另一种表达式——
逻辑表达式。本节介绍有关逻辑运算符和逻辑表
达式的相关知识。
7.3.1 逻辑运算符
• Fortran语言中提供了5种逻辑运算符供程序员在编程时使
用。新旧Fortran标准中的逻辑运算符没有任何区别,每个
运算符的左右两侧都有一点“.”,书写时不可以省略。逻
辑运算符中有的是双目运算符,有的是单目运算符,使用
中需要注意。表7.3给出了这几个逻辑运算符的定义和表达
的意义。
Fortran中的逻辑运算符
• IF语句和SELECT CASE语句在Fortran语言中都能够用于构造选择
结构,两者在语言功能上并无多大差别。本节简要介绍IF语句和
SELECT CASE语句的基本概念和作用,详细的用法会在后面的小节
进行介绍。
• 在Fortran 77时代,选择结构是通过IF语句和IF构造来实现的。
其中,IF语句只能提供单一选择,即条件满足就执行某种操作,
条件不满足则不执行任何操作。IF语句在有多个并列条件需要判
断时就很不方便,而且一旦某一个条件成立后,IF语句并不会跳
过其余的判断。这就使得判断的效率较低。在有多个并列条件需
要判断时,最好使用IF构造。IF构造能够提供多义选择,即构造
中存在多个条件选项,当不满足条件一时,就去检查条件二。只
要在进行检查的过程中有一个条件满足就会去执行相应的操作,
执行完相应的操作后就跳出所在的IF构造,而不会再去检查其余
的判断条件。
8.2.2 判断语句IF的基本用法
• 使用IF语句来实现选择结构通常有两种用法:语
句形式和构造形式。前者一般用于实现单一选
择,后者则可以实现二义选择。
• 1.语句形式
• 采用IF语句来实现单一选择的一般语法形式如下:
• IF(逻辑表达式) 执行语句
• 2.构造形式:除了能够使用IF语句来实现单一判
断之外,还可以使用块IF语句来组成IF构造用于
多重选择。
• 3.IF语句的使用
8.2.3 判断语句IF实现多重判断
• IF构造除了可以实现二义判断之外,还可以实现多重判断。这时
候,IF构造中需要加入新的元素——ELSE IF语句。有了ELSE IF
语句后,IF构造中可以同时存在多个判断条件和多个执行模块,
但是只有其中一个条件能够成立,并且只有一个执行模块能够执
行。使用IF语句和ELSE IF语句来实现多重判断的基本形式如下:
• IF(逻辑表达式1) THEN
• THEN 块
• ELSE IF(逻辑表达式2) THEN
• ELSE IF块
• ……
• ELSE
• ELSE块
• END IF
8.2.4 IF语句的嵌套
• IF构造除了能够单独使用外,构造中的任意一个语句块里都可以再次嵌入
另一个构造。被嵌入的构造可以是另一个IF构造,也可以是另一些形态、
功能不同的构造,如CASE构造、DO构造等。前提是必须将整个构造完整地
嵌入到IF构造的某一个语句块中,不允许被嵌入构造的一部分在一个语句
块中,另一部分在别的语句块或是不在被嵌入的IF构造中,即被嵌入的任
何构造不允许跨越两个独立的语句块。IF语句嵌套的形式可以表示如下:
• [构造名1:]IF(逻辑表达式1) THEN
• [构造名2:]IF(逻辑表达式2) THEN
• [构造名3:]IF(逻辑表达式3) THEN
• ……
• ENDIF[构造名3]
• ……
• ENDIF[构造名2]
• ……
• ENDIF[构造名1]
8.2.5 IF语句的特殊用法
• 在Fortran 77中,IF语句还能够实现一种特殊的选择方法,也就是算
术IF语句。算术IF语句根据算术表达式值得结果,有条件的将程序的
计算流程转到三条执行语句中的一句。IF语句的这种用法的基本形式
如下:
• IF (表达式) 标号1, 标号2, 标号3
• 其中,表达式为标量整型或实型表达式,两端用括弧括起来;标号1至
3必须是本程序单元中的有效可执行语句的标号。语句中所有的三个语
句标号都必须书写,但并不一定要指向三个不同的语句,在同一个算
术IF语句中允许同一个语句标号出现多次。算术IF语句在执行时,首
先会计算表达式的值,根据表达式的值来确定要执行哪一个标号指定
的执行语句:
• 如果表达式的值小于零,则程序流程转到标号1指定的语句去执行;
• 如果表达式的值等于零,则程序流程转到标号2指定的语句去执行;
• 如果表达式的值大于零,则程序流程转到标号3指定的语句去执行。
8.2.6 SELECT CASE语句的用法
• SELECT CASE语句是Fortran 90/95标准中新增加的语句。该语句
用于容纳CASE块,为程序员提供了一种从多个备用可执行分支选
项中选取一个来执行的手段。尽管多重判断的IF构造也可以实现
这一功能,但是在某些多条件选择的应用场合使用IF构造会使代
码显得比较繁琐、层次关系比较复杂。使用CASE构造就可以避免
这一问题,编写出来的代码也显得更加直观、简洁。
• CASE构造的作用和IF构造非常类似,它也用于编写分叉选择算
法,即根据判断条件的成立与否来区分操作不同的可执行模块。
不同之处在于:CASE构造只能把某个判断条件的可能结果区分成
若干个孤立的离散值或片断(这意味着CASE构造中的判断条件不
允许出现重叠),按不同的值或片断进行不同的操作。如果遇到
判断条件比较复杂或者存在多种判断条件互相交叉的情况时,
CASE构造在处理这些判断条件时显得不是很方便,在这种情况下
只能考虑使用IF构造来进行处理。
8.2.7 SELECT CASE语句的应用
• 下面给出一些SELECT CASE语句的实际应用例子。
• 首先演示的程序使用了整型表达式的CASE构造,
例子的原型就是程序TEST0802的个人所得税计算
程序。在进行改动前需要注意,CASE构造中的选
择表达式是不允许为实型表达式的。因此,需要
一个将实型数据转换为整型数据的函数,可以考
虑使用基本数学函数中的INT函数。
8.3 循环结构
• 除了顺序结构、选择结构外,实际的程序中还常
常遇到需要重复执行的操作或代码段。在这种情
况下,就需要用到一种新的控制结构——循环结
构。如果程序的世界中缺少了这种结构,那么结
果将不可想象。比如上一节最后一个实例中所作
的5次猜测,如果没有循环结构,同样功能的代码
段,即“猜测-判断”,需要重复书写5次。这种
情况至少还知道要重复书写几次,在有的应用领
域,如数值领域的迭代求解,甚至连要重复的次
数都不清楚。循环结构的出现使得这一类问题变
得不再是任何问题。本节就将介绍循环结构的相
关内容。
8.3.1 基本的DO构造
• DO构造在Fortran 77和Fortran 90/95中都提供用以执行循环操
作,但是两个标准在DO构造的具体实现形式上是不同的。尽管这
样,两个标准下的各种DO循环都可以归纳为如下所示的DO构造一
般形式:
• [构造名:] DO [标号][循环控制]
• 块
• 终止语句
• 其中,构造名选项只允许在Fortran 90/95标准中使用,用于标识
构造的起止范围;DO语句后的标号选项在Fortran 77和Fortran
90/95标准中同样适用,尽管带标号的DO构造是较老的语法形式;
循环控制用于控制循环的执行,提供循环停止或跳出循环的手段
等;中止语句用于标定循环构造的结束位置,并不是说循环执行
到该条语句就会停止执行,而是通过该条语句将流程返回到DO语
句。
8.3.2 无条件循环与DO语句
• 现实中的循环可以分为不带循环变量与带循环变量两种形
式,前者只能通过循环体中的条件判断等跳出循环体;后
者则能够通过循环变量来执行确定次数的重复操作。后者
也可以称为无条件的循环,它可以不通过条件判断来实现
循环的终止。
• 当需要执行的循环次数为已知时,使用DO语句来实现循环
比较方便。它由一个DO语句和循环体组成,在Fortran
90/95标准中的一般形式如下:
• [构造名:]DO 循环变量 = 循环初值,循环终值[,循环增
量]
• 循环体
• ENDDO [构造名]
8.3.3 条件循环与DO构造
• 在编程实践中,除了会遇到执行次数确定的循环外,还有
一种情况是执行次数未知的循环。这一类循环不能通过使
循环变量“增加”到循环终值的形式来终止,只能通过循
环体中的判断条件来控制程序的流程是否跳出循环构造。
因此,这种循环又被称为条件循环,在相应的循环构造中
不含醒 房刂票淞俊
• 1.条件循环实现方式一
• 2.条件循环实现方式二
• 除了将判断条件写在循环体内的方式(也称为直到型循环)
之外,还可以通过WHILE语句来实现当型循环。在Fortran
90/95标准中,DO WHILE语句被增加到循环构造方式中以支
持当型循环。
8.3.4 循环的署名
• 同其他的一些构造一样,循环也是可以命名的,命名的原则同变
量的命名原则完全一致。循环一旦命名,则在循环终止语句后必
须跟上循环的名字,以使编译程序明白那一个循环结构被封闭。
命名的循环在有多层嵌套或是DO构造较多的情况下会使各个循环
显得更加清晰。下面来看一段实际的例子。
• TEST0818.F90
• ! 署名循环范例
• PROGRAM TEST0818
• IMPLICIT NONE
•
• INTEGER :: I
•
• PRINT: DO I = 1, 2
• PRINT *, '第', I, '次循环…'
• ENDDO PRINT
•
• END PROGRAM TEST0818
8.3.5 循环的嵌套
• 同IF构造和SELECT CASE构造类似,DO构造也允许在自身中
再嵌入其他的DO构造。在一个DO循环中又完整地包含另一
个DO循环的方法,称为DO循环的嵌套。循环嵌套中的各层
循环变量不允许重名。循环嵌套的层数可以不限,但是循
环嵌套的层次太多会使得各层循环不容易分辨。对此,可
以通过对循环进行命名以使循环嵌套的层次更清晰。
• 要注意,循环嵌套中的内循环应当完整地嵌套在外循环之
内,也就是说内循环是外循环中循环体的一部分,内外循
环不允许交叉。例如,如下形式的循环嵌套是合法的:
• OUT: DO I = 1, 4
• IN: DO J = 1, 5
• ……
• ENDDO IN
• ENDDO OUT
8.3.6 DO循环规则
• 在使用DO循环时,需要注意循环的一些其它规则。比如,循环变
量可以在循环体中被引用,但不应当再被赋值,即使循环变量的
值保持不变。例如下面的写法都是不正确的,循环变量N和M不能
在循环体内被重新赋值:
• DO N = 1, 10
• ……
• N = N*2
• ……
• ENDDO
• DO M = 1, 10
• ……
• M = M
• ……
• ENDDO
8.3.7 隐式DO循环
• 隐式DO循环实际上是一种带控制循环变量的DO循
环,但简化成只有DO循环的第一句,并且把关键
字DO隐去。隐式DO循环的一般形式如下:
• I = m1, m2[, m3]
• 其中,m1表示循环的初值;m2表示循环的终值;
m3表示循环的增量。如果省略本项目,则默认为1。
• 隐式DO循环不是一种可以独立存在的语句。它只
能作为输入输出列表的一个组成部分,用来控制
重复读写的次数。它的应用形式如下:
• (I/O列表, 循环变量名 = 循环初值, 循环终值[,
循环增值])
8.4 循环的控制
• 在Fortran 90/95标准中,引入了两个控制循环执
行流程的语句EXIT和CYCLE。这两条语句实际上在
某些Fortran 77编译器中早已被当成了不成文的
标准之一了。
8.4.1 EXIT语句
• 在实际变成种,有许多实际问题是无法预先知道循环次数
的,比如一些数学和工程领域中的迭代算法。对于这类问
题,最常见的做法是给出一个判别条件。如果满足这个判
别条件就重复执行循环体,否则就退出循环。因此有条件
循环时循环的执行次数不是固定的。传统的做法是使用
GOTO语句来使流程跳出循环,但这种方法不符合结构化程
序设计的要求。针对这种情况,Fortran 90/95通过引入
EXIT语句来满足结构化程序设计的要求。
• EXIT语句的作用是停止循环并使流程控制退出循环结构,
因此又被称为出口语句。该语句的一般形式如下:
• EXIT [DO构造名]
8.4.2 EXIT语句与条件循环
• 通过DO WHILE语句来实现循环猜测,在这条DO WHILE语句
中的逻辑判断表达式就是简单的逻辑真(TRUE)。这种用
法是允许的,它表示当型循环的执行条件一直满足,无须
进行判断。但是在使用时应该注意,在循环体内一定要提
供跳出循环的手段,否则循环会一直执行下去形成死循环。
下面的代码通过在判断语句中加入EXIT语句,跳出循环。
• IF(Rchar == Gchar) EXIT
• 如果用户输入的字符等于系统随机得到的字符,则执行
EXIT语句退出循环。如果用户猜测的字符不正确,则会向
用户提示应该向哪个字符方向进行猜测。
8.4.3 EXIT语句与无条件循环
• 除了应用于条件循环中用作循环退出的手段外,
EXIT语句还可以应用于无条件循环中。当EXIT语
句应用于无条件循环中时,如果循环变量大于循
环终值或是与EXIT语句配合的逻辑表达式为真都
会跳出循环的执行。这种应用通常用在无法预知
循环执行的次数并且不知道循环退出条件是否能
够满足的场合。比如,计算流体力学中求解流动
问题时,通常采用这种方法来提供双重的循环退
出机制。由于这类问题的复杂性会涉及到计算方
法、流动对象的网格好坏等因素,通常不知道该
问题是否能够收敛,也不知道需要计算多少步才
能收敛。
8.4.4 CYCLE语句
• CYCLE语句是另一种常用于循环的流程控制语句。同EXIT语
句不一样,CYCLE语句的作用不是流程跳出循环,而是使流
程重新回到循环的开头。该语句的一般形式为:
• CYCLE [DO构造名]
• 当循环执行到CYCLE语句时,它会使循环的流程跳过位于它
之后的那部分DO块,重新返回到循环的第一个可执行语句
开始执行。运用CYCLE语句,可以使循环在某一次的迭代过
程中不执行该语句后面的代码,使循环的应用更为灵活多
变。
• CYCLE语句与EXIT语句一样属于特定的DO构造。如果语句引
用了DO构造名,则它属于该构造,否则它属于所出现的最
内层DO构造。
8.5 再论GOTO语句
• GOTO语句是相当古老的流程控制语句,在Fortran
77时代是主要的流程控制语句。尽管该语句功能
强大,但是滥用该语句会造成整个程序的流程杂
乱无章,不符合结构化程序设计的要求。这种说
法并不意味着GOTO语句就不能使用,而是建议在
程序中慎用该语句。当然,某些GOTO语句的形
式,如计算GOTO语句和赋值GOTO语句,在Fortran
90/95标准中是被废除、不建议使用的语句。
8.5.1 无条件GOTO语句
• 该语句的一般形式如下:
• GOTO label
• 其中,label是本程序单元中可用的语句标号,必
须出现在可执行语句之前。比如
• ……
• X = Y + 3.
• GOTO 4
• 3 Y = Y+5.
• 4 Z = X+Y
• ……
8.5.2 计算GOTO语句
• 计算GOTO语句在Fortran 95标准中是一项被废除
的语法,这里只作简单介绍以便在阅读一些旧
Fortran程序时能够有所帮助。
• 计算GOTO语句的用途是根据表达式的值来确定程
序的控制流程转向一系列设定的分支目标中的一
项。这种GOTO语句的一般形式如下:
• GOTO (标号列表)[, ]表达式
• 其中,标号列表由本程序单元中一系列可用的分
支目标语句的标号组成,标号之间通过逗号“,”
来分隔。同样的标号允许在标号列表中出现多次。
8.5.3 赋值GOTO语句
• 赋值GOTO语句在Fortran 90标准中是一种过时的语法,在
Fortran 95标准中则被废除。同无条件GOTO语句相比,赋
值GOTO语句后所跟的不是语句标号,而是表示语句标号的
整型变量。需要注意的是,这里的整型变量不允许通过赋
值符进行赋值,比如下面的赋值GOTO语句是错误的:
• ……
• I = 10
• GOTO I
• ……
• 赋值GOTO语句中的标号变量必须使用ASSIGN语句来赋值,
比如下面的形式就是正确的:
• ……
• ASSIGN 10 TO I
• GOTO I
• ……
8.5.4 用?还是不用?
• 正如前面一节提到的,GOTO语句的滥用在一定程
度上决定了这一语句目前的处境。但是GOTO语句
自身并没有错误,错误在于程序员过分依赖于
GOTO语句的强大功能而没有注意到程序自身应该
具有的逻辑性。
• 因此,在新的Fortran标准中除了计算GOTO语句和
赋值GOTO语句这两种过时的语法被废除以外,无
条件GOTO语句仍然在新标准中保留了自己的一席
之地。在实际编程过程中,用不着因为害怕破坏
程序的结构而不敢去使用GOTO语句。只要在使用
中时刻牢记GOTO语句只在一个基本程序结构中使
用,程序流程的跳转仅限于基本结构之内。
8.6 程序结束、终止和暂停
• 在Fortran中,程序的结束、终止和暂停都由相应
的语句来完成。正确的了解和使用这些语句不仅
能使程序的结构更为清晰,还能提供额外的功能
以加强程序的交互性。本节将介绍Fortran中这三
种操作的对应语句。
8.6.1 程序结束(END)
• 在Fortran中,END语句的作用主要有两点:
• 结束本程序单位的运行;
• 作为一个程序单位结束的标志。
• END语句只能出现在一个程序单元中的最后一行,
并且一个程序单元只能有一个END语句。在主程序
中,END语句的作用是使整个程序结束运行。在子
程序中,END语句一方面作为子程序结束的标志,
另一方面则使流程返回到调用程序中。
8.6.2 程序终止(STOP)
• 在Fortran中,STOP语句的唯一作用就是终止程序
的运行。同END语句不同,STOP语句可以出现在程
序可执行语句中的任意位置,并且可以有多个。
程序在运行到STOP语句时,就会停止执行。子程
序中的STOP语句并不会使流程返回到调用程序,
而是直接停止整个程序的执行。
• 当一个程序中有多个STOP语句时,为了使用户能
够辨别是哪一条STOP语句停止了程序的运行,可
以在执行STOP语句的同时输出必要的信息。其一
般形式如下:
• STOP [停止代码]
8.6.3 程序暂停(PAUSE)
• PAUSE语句又叫暂停语句,这是一个Fortran 77的遗留产物。
在Fortran 90中不推荐使用该语句,在Fortran 95中则被
废止。这主要是由于该语句可能会造成程序执行的不可预
料性,例如在Compaq Visual Fortran中进行视窗程序开发
时,如果使用了PAUSE语句,程序并不会如预期那样暂停执
行,而是反复不断的弹出控制台窗口。
• PAUSE语句的作用是使程序暂时停止执行,而不是终止或结
束执行。在程序执行到该语句时,系统只是暂时将程序的
执行挂起来,等待用户的进一步指令。PAUSE语句在程序调
试时非常有用。比如一个大的计算任务是由几个小的子计
算任务依次执行来完成的,这就可以在每一个子计算任务
的末尾加上PAUSE语句,一段一段的进行调试。当所有的子
计算任务都调试完成、排除问题之后,再将所有的PAUSE语
句去掉。
第9章 Fortran中的数组
• 数组是Fortran语言中功能最为强大、运用最为灵
活的一种数据结构。数组(ARRAY)在科学和工程
计算中通常用来表示矩阵和向量。同一般的变量
声明相比,数组能够同时保存多个数据。它是一
种使用大规模数据的方法。配合Fortran语言中的
数组操作,可用于对大量不同的数据进行处理。
在存储结构上,数组占用一片连续的存储单元。
程序中通过数组索引来对数组元素、片断进行操
作。
9.1 数组的定义
• 要在程序中使用数组,需要首先在变量声明中进行数组定
义。数组定义规定了数组的维数和大小,以及数组所能保
存的数据类型。在程序中,通过数组引用来对数组、数组
元素或者数组片断进行操作。
• 数组是类型相同、种别一致的一组变量的有序集合。它可
以是整型、实型、双精度型、复型、逻辑型、字符型以及
自定义类型等中的任意一种。组成数组的每一个变量被称
为数组元素,并由唯一的下标来进行标识。数组定义说明
了数组所能保存的数据类型、数组的维数、维的范围和数
组的大小。本节主要介绍Fortran中数组定义的几种方式。
9.1.1 定义形式一
• 第一种数组定义形式的语法格式如下所示。
• 类型说明 [::] 数组名([下标下界:]下标上界
[,…])[,…]
• 该定义形式通过类型说明来显式声明数组的数据
类型,并通过下标下界和下标上界来规定数组中
某一维的范围。下标下界和下标上界共同组成了
维说明符。当维说明符省略下标下界时,默认所
在维的下标从1开始。如下代码都是合法的数组定
义。
• REAL :: A(1:2,2:4)
• INTEGER B(10)
9.1.2 定义形式二
• 第二种数组定义形式的语法格式如下所示。
• DIMENSION [::] 数组名([下标下界:]下标上界
[,…])[,…]
• [类型说明 [::] 数组名[,…]]
• 该定义形式通过DIMENSION语句来进行数组的定
义,通过下标下界和下标上界来规定数组中某一
维的范围。在第二行通过类型说明来显式声明数
组的数据类型。当省略类型说明时,采用默认的
“I-N”规则来对数组的数据类型进行定义。如下
代码合法的对数组进行了定义。
• DIMENSION :: A(10), B(2:11)
• INTEGER :: A
9.1.3 定义形式三
• 第三种数组定义形式的语法格式如下所示。
• DIMENSION([下标下界:]下标上界[,…]) [::]
数组名[,…]
• [类型说明 [::] 数组名[,…]]
• 该定义形式通过DIMENSION语句直接说明了数组的
维数和维的范围。这种形式定义的数组全部具有
相同的维数和大小。如下代码表示了如何采用上
述形式进行数组的定义。
• DIMENSION(10, 4:10) :: A, B, N
• INTEGER :: A
• REAL(8) :: N
9.1.4 定义形式四
• 第四种数组定义形式的语法格式如下所示。
• [类型说明,]DIMENSION [::] 数组名([下标下
界:]下标上界[,…])[,…]
• [类型说明,]DIMENSION([下标下界:]下标上界
[,…]) [::] 数组名[,…]
• 该定义形式可以说是前三种定义形式的综合形式。
通过在DIMENSION语句前引入类型说明来显式的说
明数组的数据类型。下列代码演示了此种形式的
数组定义。
• REAL, DIMENSION :: I(10), M(10,5)
• INTEGER, DIMENSION(10) :: A, C
9.1.5 数组定义的特点
• 上述数组定义的形式中,中括弧内的部分可有可无。数组定义语
句必须出现在所有可执行语句之前。除了上述基本的定义形式
外,在Fortran77中可以使用COMMON语句,在Fortran90中可以用
POINTER语句、ALLOCATABLE语句等对数组定义进行加强。
• 在前面所述的四种定义形式中,定义形式因简洁直观而常见于实
际使用中。此外,如下问题是在实际编程中应该注意的。
• 在前面所述的数组定义中,I(10)、M(10,5)、A、C等称为数组说
明符。在同一个说明语句中有多个数组说明符时,用逗号进行分
隔。
• 数组说明符中的I、M、A、C等是数组名,其取名规则与变量相同
并且不应与程序中的其他变量同名。在同一个程序单元中,一个
数组名只允许定义一次,不能重复定义。例如下面的数组定义是
错误的。
• INTEGER :: A(10), A(10,20)
9.2 数组的引用方式
• 数组经过定义之后,就可以在程序中使用了。在
Fortran77标准中,数组只允许在输入输出语句中
进行整体操作。在其他场合,只能对数组的元素
通过下标索引的方式逐个进行操作。到了
Fortran90标准,这一限制被大大放宽了。数组除
了能够进行整体操作以外,还能对数组中的片断
和数组的整体进行操作。这进一步增强了Fortran
语言在数值处理方面的能力。Fortran中数组的引
用方式可以概括为以下几种:
9.2.1 引用数组元素
• 引用数组元素的语法格式如下所示。
• 数组名(下标,……)
• 这种引用方式通过下标索引来对数组中的每一个
元素进行操作。它是Fortran中最为传统的一种引
用方式。采用这种方式进行引用时,下标的值不
能超出数组定义时的下标上下界。目前市面上绝
大部分的Fortran编译器都会提供在编译时进行数
组下标越界检查的功能。而其他计算机语言的编
译器,如C/C++编译器往往不提供这样的功能,需
要程序员自行检查代码中是否存在数组越界的行
为。
9.2.2 引用数组整体
• 引用数组整体的语法格式如下所示。
• 数组名
• 这种引用方式通过数组名来对数组进行整体操作。这种引用方式是
Fortan90中新增的,大大提高了程序编写的灵活性和简单性。我们对前一
个例程TEST0901进行修改,通过整体引用来对数组进行赋值。
• TEST0902.F90
• ! 引用数组整体的范例
• PROGRAM TEST0902
• IMPLICIT NONE
• ! 变量定义
• REAL :: A, B(5,5)
• READ(*, *)A
• ! 数组整体引用
• B = A
当C为标量时
当C为数组时
DO J = 1, N DO J = 1, N
DO I = 1, M DO I = 1, M
A(I, J) = B(I, J) & C(I, J) A(I, J) = B(I, J) & C
ENDDO ENDDO
ENDDO ENDDO
9.9 常用内在函数
• Fortran语言中的内在函数通常都可以接受数组作
为参数来进行运算,此外还有一些专用的函数适
用于处理数组所特有的运算。本节就将介绍这些
内在函数在数组领域的应用。
9.9.1 内部基本函数
• 在Fortran语言的数组表达式中,允许将数组作为
内部基本函数的参数。此时,内部基本函数的函
数值就是一个同参数数组形状相同的数组,它的
每个位置上的元素值就是被操作数组对应位置上
的数组元素取该基本函数所得的值。例如数组A和
B都是形状相同的一维数组,则语句B=SQRT(A)的
执行结果可以表示如下:
9.9.2 矩阵乘积函数
• 该函数的作用是执行数值型或逻辑型数组A与B的矩阵乘法。
函数的原型为:
• C = MATMUL(A, B)
• 使用时,数组A和B必须是秩为1或2(也就是一维或二维)
的数值型或逻辑型的有值数组,且数组A和B中至少有一个
的秩为2。传入矩阵乘积函数的数组A与B的类型必须相同。
• 数组A与B的矩阵乘积规则和结果与数学上的矩阵乘法定义
一致,也就是说数组A的最后一维的长度必须和数组B的第
一维的长度相同。结果数组C的秩和形状取决于参数数组的
秩和形状:
• 如果A的形状为(n,m),B的形状为(m,k),则结果数组C的秩
为2,形状为(n,k)。
• 如果A的形状为(m),B的形状为(m,k),则结果数组C的秩为
1,形状为(k)。
• 如果A的形状为(n,m),B的形状为(m),则结果数组C的秩为
1,形状为(n)。
9.9.3 向量点乘函数
• 该函数的作用是执行数值型或逻辑型数组A与B的点积乘法。
函数的原型为:
• C = DOT_PRODUCT(A,B)
• 使用时,数组A和B必须是秩为1(即数学上所说的向量,也
即一维数组)的数值型或逻辑型的有值数组,且数组A与B
的类型必须相同。
• 一维数组A与B点乘的结果是标量,函数的点乘规则和结果
值与数学上的定义相同。如果一维数组A和B中有一个的长
度为0,且数组为数值型数组,则结果为0;如果数组为逻
辑型数组,则结果为.FALSE.。例如
DOT_PRODUCT((/1,2,3/),(/3,4,5/))的结果为26,计算过
程为(1 x 3)+(2 x 4)+(3 x 5))=26。
9.9.4 元素求和函数
• 元素求和函数属于数组规约函数中的一种。
• 数组规约函数是一组功能类似的数组函数的统称。
这组函数的主要作用就是沿着数组中的某一维,
对在屏蔽表达式中值为.TRUE.的所有数组元素进
行某种操作。这组函数包括SUM、PRODUCT、
MAXVAL、MINVAL、COUNT、ANY和ALL函数。本节将
主要介绍其中的两种。
• 元素求和函数的主要作用是沿着数组中的某一
维,对在屏蔽表达式中值为.TRUE.的所有元素求
和。函数的原型为:
• C = SUM(A[,DIM][,MASK])
9.9.5 元素连乘求积函数
• 该函数的主要作用是沿着数组中的某一维,对在屏蔽表达
式中值为.TRUE.的所有数组元素求连乘积。函数的原型为:
• C = PRODUCT(A[,DIM][,MASK])
• 其中的注意事项和说明同元素求和函数。下面是一些实例。
• 比如数组A=(/2,4,6/),则PRODUCT(A)的值是48;又如
PRODUCT(B,MASK=B<0.0)表示对数组B中的所有小于0的元素
求连乘积。同元素求和函数一样,结果C是数组还是标量也
取决于函数中维的定义和被求积数组A的大小和形状。比如
数组A(2,3)=(/2,3,4,5,6,7/),则PRODUCT(A,DIM=1)的值
是[(2×3=6),(4×5=20),(6×7=42)];SUM(A,DIM=2)的值
是[(2×4×6=48),(3×5×7=105)]。
9.9.6 数组大小查询函数
• 该函数是数组查询函数中的一种。数组查询函数一组功能
类似于数组函数的统称。这组函数包括:SIZE、SHAPE、
ALLOCATED、LBOUND和UBOUND函数。这里只介绍其中的两种:
数组大小查询函数和数组形状查询函数。数组大小查询函
数的作用是求数组沿着某一维的长度或者数组元素的总数
目。函数原型为:
• C = SIZE(A[,DIM])
• 其中A是被查询数组,可以是假定大小数组,但不能是未定
义的指针数组或未分配空间的可分配数组。当DIM等于1
时,表示查询数组有几行;当DIM等于2时,表示查询数组
有几列;当DIM被省略时,表示查询数组有多大(即有多少
个元素)。
9.9.7 数组形状查询函数
• 该函数的功能就是求数组或标量的形状。函数的
原型为:
• C = SHAPE(A)
• 其中,A表示被查询对象,可以是标量或数组,但
不能是假定大小数组、未定义的指针或未分配空
间的可分配数组;C保存查询结果,是一个一维整
型数组。
• 比如SHAPE(2)将返回一个零长度一维数组;如果
对数组B(-2:5,9:10)进行查询,则SHAPE(B)将返
回一维数组(8,2)。
9.9.8 数组合并函数
• 数组合并函数是数组构造函数中的一种。数组构造函数也
是一系列功能相似的函数的总称,它们用于从已有数组的
元素构造出新数组。这组函数包括:MERGE、PACK,UNPACK
和SPREAD函数。
• 数组合并函数的只要用途就是在屏蔽表达式的控制下,对
两个独立数组进行合并操作。该函数的原型为:
• C = MERGE(TSOURCE,FSOURCE,MASK)
• 其中,TSOURCE可以是任意类型的数组或标量,FSOURCE是
必须与TSOURCE具有相同的类型和类型参数的数组或标量。
屏蔽表达式MASK必须是逻辑型数组;若MASK值为真,则结
果是TSOURCE,若MASK值为假,则结果是FSOURCE。
9.9.9 数组压缩函数:
• 该函数的作用就是在屏蔽表达式的控制下,将数组压缩成向量数
组。数组的原型为:
• C = PACK(A,MASK[,VECTOR])
• A表示被压缩对象,可是任意类型的数组;屏蔽表达式MASK必须是
逻辑型数组,并且与数组A相容(也就是形状相同);VECTOR是可
选参数,必须为向量数组,并且与数组A具有相同的类型和类型参
数。
• 结果C是秩为1的数组(就是一维数组),其类型和类型参数与数
组A相同。若VECTOR存在,则结果C的大小等于VECTOR的大小,否
则其大小是使屏蔽表达式MASK值为真的元素的个数;若屏蔽表达
式MASK为标量并且值为真,则结果C的大小与数组A相同。结果C中
的值按数组中的元素位置排序,数组A中的第i个元素对应于屏蔽
表达式MASK的第i个为真元素。若VECTOR存在,且大小大n于符合
条件的数组A中的元素个数t,则结果C中第i个元素值为
VECTOR(i),i=t+1,…,n。
9.9.10 数组形状扩展和重构形函数
• 这是一个由两个函数组成的函数族,包括SPREAD
函数与RESHAPE函数,用于完成数组形状重构和扩
展的任务。
• SPREAD函数的主要功能就是将数组沿着某一维的
方向拷贝规定次数后扩展成一个新的数组。函数
的原型为:
• C = SPREAD(A,DIM,NCOPIES)
• 其中,A为被拷贝对象,可以是标量或任意类型的
数组。当DIM等于1时,表示沿着第一维下标变化
的方向扩展,也称为向下扩展;当DIM等于2时,
表示沿着第二维下标变化方向扩展,也称为向右
扩展。NCOPIES用于指定拷贝的次数。
9.9.11 数组转置函数
• 数组转置函数是数组运算函数中的一种。数组运
算函数是数组函数中同矩阵运算相关的一组函数
的总称,这组函数包括:TRANSPOSE、EOSHIFT和
CSHIFT三个函数。数组转置函数的用途就是对秩
为2的数组(就是二维数组)进行转置操作。函数
的原型为:
• C = TRANSPOSE(MATRIX)
• 其中,数组MATRIX必须是一个二维数组。转置后
的结果数组C的形状正好与数组MATRIX的形状相反。
也就是说MATRIX(n,m)转置后的结果为C(m,n)。
9.9.12 去端移动函数
• 该函数的作用是对秩为1的数组作去端移位处理,
或沿着某一维对秩大于1的数组在所有秩为1的完
整数组片段上作去端移位处理。函数的原型为:
• C = EOSHIFT(A,SHIFT[,BOUNDARY][,DIM])
• 其中,A为被进行去端移位处理的数组。SHIFT表
示移动的位数,必须为整数;当SHIFT为正时,表
示去端左移,当SHIFT为负时,表示去端右移。在
数组或数组片段的一端被移出的元素被丢弃,并
在另一端移入相同数量的BOUNDARY的值。DIM表示
要进行去端移位处理的数组的维,默认为1。不同
的片段可以有不同的BOUNDARY值,并可在不同的
方向上移动不同的位数。
9.9.13 循环替换函数
• 该函数的作用是将秩为1的数组的所有元素或高维
数组的指定维上的元素进行循环移动。在一端上
移走的元素被插到另一端。函数的原型为:
• C = CSHIFT(A,SHIFT[,DIM])
• 其中,A为被操作数组。SHIFT为正值时被移向左
端,负值时则移向右端。DIM可以指定要进行操作
的数组的维,默认值为1。
• 比如数组A=[1,2,3,4,5,6],则CSHIFT(A,SHIFT=2)
的结果是[3,4,5,6,1,2];而CSHIFT(A,SHIFT=-2)
的结果则[5,6,1,2,3,4]。
9.9.14 最大值元素定位函数
• 该函数是两个数组定位函数之一,另一个不用说也知道是
最小值元素定位函数(MINLOC)。函数的原型为:
• C = MAXLOC(A[,DIM][,MASK])
• 函数根据屏蔽表达式MASK的真值条件确定数组A中的所有元
素或沿某一维DIM所有元素中第一个最大值元素出现的位置。
结果C的形式取决于数组A的秩:当秩为1时,C为标量;当
秩不为1时,C为一维数组。
• 例如MAXLOC(/1,8,8,7/)的值为[2]。
• 再如数组A(3,4)的形式为:
⎡4 0 −3 2 ⎤
⎢3 1 − 2 6 ⎥
⎢ ⎥
⎢⎣− 1 − 4 5 − 5⎥⎦
9.10 Fortran90/95的数组操作语句
• Fortran 90/95中提供了许多新的数组操作语句,
例如FORALL、WHERE语句等。这些语句大大提高了
Fortran语言中的数组操作特性,使其在数值计算
领域的优势中得到进一步加强。本小节将介绍这
两种新的数组操作语句的特性和用法。
9.10.1 WHERE语句和WHERE构造
• 在Fortran 90/95中提供了一种新的屏蔽数组操作语句
WHERE,该语句可用于从数组中提取出部分内容进行设置。
实质上,WHERE语句是一种带判断条件的数组操作语句,也
就是说该语句只对那些符合条件要求的数组元素进行操作。
在使用上,可以分为语句形式和构造形式两种。
• 1.WHERE语句
• WHERE语句的一般形式为:
• WHERE(屏蔽表达式) 赋值语句
• 2.WHERE构造
• 除了上面这种语句声明形式的用法外,还可以使用WHERE构
造。
• 3.WHERE构造的嵌套
• 同IF构造相似,WHERE构造也允许进行嵌套。
9.10.2 FORALL语句
• FORALL语句也是Fortran 90/95标准中新增的数组操作语句,是数
组屏蔽赋值功能(WHERE语句和WHERE构造)的一对一元素的推广。
从对数组的作用形式来看,该语句同隐式DO循环操作数组的过程
类似,但在功能上更为强大。
• 1.FORALL语句
• FORALL语句的一般形式为:
• FORALL(循环三元下标[,循环三元下标]…[,屏蔽表达式]) 赋值语
句
• 2.FORALL构造
• 除了前面介绍的FORALL语句外,FORALL也能像IF构造和WHERE构造
一样以构造的形式进行实用。FORALL构造的一般形式为:
• [构造名:] FORALL(循环三元下标[,循环三元下标]…[,屏蔽表达
式])
• [块]
• END FORALL [构造名]
第10章 Fortran程序单元
• 一个Fortran程序中通常不是只由一个主程序组
成,而是由几个按某种方式划分的不同程序单元
来共同组成。尽管Fortran程序中允许只有主程序
而没有子程序,但绝不允许只有子程序而没有主
程序。在Fortran中,程序的执行总是从主程序开
始的。
• Fortran中的程序单元可以大体划分为主程序、子
程序两种,其中子程序又可以进一步划分为函数
子程序、子例行子程序和数据块子程序。数据块
子程序通常用于实现变量的初始化赋值,函数子
程序和子例行子程序在用途上基本是一致的,但
是也有许多不同之处。本章将详细介绍Fortran中
的程序单元和它们的基本用法。
10.1 主程序
• 顾名思义,主程序是一个实际程序中的主体,其
他类型的程序单元都是以某种方式来辅助主程序
的执行。在Fortran语言中,一个程序的执行始终
是从主程序的第一条可执行语句开始的,所以每
个完整的Fortran程序都必须有且只允许有一个主
程序。主程序定义的一般语法形式如下:
• [PROGRAM [程序名]]
• [说明部分]
• [可执行部分]
• [CONTAINS
• 内部过程]
• END [PROGRAM[程序名]]
10.2 语句函数
• 语句函数通过一句代码定义来实现某种特定的处
理功能,它是Fortran 77时代的遗留产物。严格
来说,语句函数不属于程序单元的范畴。但是在
实际应用中,语句函数以其灵活的应用、小巧的
结构在程序中发挥着重要的作用。
• 在实际的编程过程中,程序员往往会遇到这种情
况:一些简单的函数会在一个程序单元中的不同
地方重复用到,而Fortran系统并不提供这种内部
函数;如果采用函数子程序的形式来描述这些简
单的函数又会觉得没有这种必要。例如,要求解
函数的值,将其编写成函数子程序可以顺利解决。
但是,Fortran语言提供了一种更为简单的手段—
—语句函数。
10.2.1 语句函数的定义
• 在Fortran中定义一个语句函数的形式如下:
• fun ([d-arg [, d-arg] ...]) = expr
• 1.Fun 2.d-arg 3.Expr 4.语句函数示例 5.需要注意的问题:
在使用语句函数进行编程时,下面一些问题是需要引起注意的:
• 语句函数通常在函数比较简单,能够用一条语句(包括换行)就能进行定
义时才使用;
• 语句函数是一种非执行语句,需要放置在所有可执行语句之前和相关的类
型说明语句之后;
• 语句函数的作用范围仅限于定义它的程序单元之内,不允许跨程序单元进
行语句函数的调用;
• 语句函数不能作为子程序调用时的实参,也不允许在EXTERNAL语句中出
现;
• 语句函数中出现的虚参必须是变量名,不能是常量、表达式或是数组元
素;
• 语句函数通过表达式得到的函数值的类型必须与函数名的类型一致。
10.2.2 语句函数的引用
• 语句函数在完成定义后,就可以在程序单元中进
行引用了。实际上,在前一小节的例程中已经演
示了语句函数引用的一般方式。本小节将对语句
函数的引用方式进行具体的说明。
• 语句函数的引用方式与Fortran中内部函数的引用
方式完全一致,就是用程序中定义的实参替换掉
语句函数定义中的虚参。实参必须是与虚参类型
相同的常量、变量或表达式。
10.3 函数子程序
• 函数子程序和子例行子程序是子程序的两种常用
基本形式。它们的共同特征就是作为数据处理过
程的集合。但是这两种子程序也不完全相同,函
数子程序会返回一个函数值,且通常不会改变哑
元的数值。因此,函数子程序更像是数学上的一
个函数。而子例行子程序通常用于完成一项更为
复杂的任务,通过哑元或者其他手段返回几个结
果,哑元的数值通常会在程序的执行过程中改变。
10.3.1 定义函数子程序
• 下面给出函数子程序的一般形式为:
• [prefix] FUNCTION name ([d-arg-list]) [RESULT (r-name)]
• ...
• END [FUNCTION name]
• 1.prefix说明项
• prefix说明项是一个可选参数,可以使用如下两种形式来书写:
• type [keyword]
• 或
• keyword [type]
• 2.d-arg-list
• d-arg-list表示函数的哑元列表。如果函数子程序不包含哑元,
则哑元列表可以省略,但是函数名后的括号不能省略。
• 3.RESULT关键字
• RESULT关键字用于声明将函数的返回值保存在其后的变量名中,
称为函数结果名。
10.3.2 调用函数子程序
• 函数子程序的调用与内在函数的调用形式一样。
在主调程序的任意位置,可以通过下面的语句形
式将函数子程序的计算结果赋值给变量:
• V = 函数名(实元表)
• 其中,V表示用于接收函数计算结果的变量;实元
表是程序中实际传入函数子程序的变量列表,除
非有特殊说明,变量列表中的实元个数以及类型
必须与函数子程序定义时的虚参在个数和类型上
一致。如果函数不包含哑元,则调用形式是在表
达式中直接写上函数名再跟空括号即可:
• V = 函数名()
10.3.3 函数子程序示例——进制转换
• 下面来看一段函数子程序的实例,代码将一个4字节的整数用16进制的形式表示出来。
首先给出的是程序的函数子程序单元HEX。
• FUNCTION HEX(n)
• IMPLICIT NONE
•
• 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
• END DO
•
• END FUNCTION
10.3.4 函数子程序示例——分形
• 在数学上有一个特殊的分支——分形(fractal),所谓分形是
Mandelbrot将自然界的一些特殊复杂图形(如海岸线、树叶外形、
雪花结晶类型等)进行数学理想化后提出的一种概念,其核心思
想是图形的任意细小部分都与图形的整体具有自相似性,这种图
形的维数不是整数,而是分数维。分形的一个典型例子就是Koch
曲线,它具有雪花的外形,可以通过对一段直线反复进行某一简
单的操作而得到。把这个过程用数学语言来描述,就是在复空间
内定义的一种简单迭代过程,它是一个图形的缩小映射,从而产
生自相似曲线。
10.4 子例行子程序
• 同函数子程序相比,子例行子程序通常用于完成
更为复杂的任务。子例行子程序接受外界传入的
参数并对其进行处理,子例行程序名不会用来返
回处理结果。形象一点来说,函数子程序像检验
机,它不改变参数的值但会告诉外界一个检测结
果;而子例行子程序更像一个加工机器,外界来
的参数经过它的加工会以新的形象出现。本节主
要介绍子例行子程序的相关知识。
10.4.1 定义子例行子程序
• 子例行子程序同函数子程序非常相似,但是子例
行子程序不会有返回值。这种形式的子程序是以
SUBROURTINE语句开始,END语句结束的过程。其
一般语法形式如下:
• [前缀] SUBROUTINE子程序名 [([哑元列表])]
• ……
• END [SUBROUTINE[子程序名]]
• 1.哑元列表
• 2.前缀
• 3.子程序名
• 4.END语句
10.4.2 子例行子程序示例
• 下面直接来看一段例子,这段代码依次读入三个
实数,并按它们的大小重新开始排序。
• 程序的执行结果如下:
• 请输入三个实数:
• 1.345 2.71828 2.71827
• 三个实数的先后次序如下:
• 2.718280 2.718270 1.345000
• 调用子例行子程序时的实元必须是与哑元类型相
同的变量、数组、数组元素和常数。当用CALL语
句进行调用时,哑元和实元才按哑元列表中的顺
序一一对应,取得同一数值。
10.5 子程序的多入口点和多折返点
• 尽管子程序中不允许直接定义其他的子程序,但
是在Fortran 77时代,可以通过特殊的方式在同
一个子程序中定义多个不同的过程入口。通过调
用不同的过程定义来实现调用同一个子程序中的
不同执行段。除了提供多入口点外,Fortran 77
时代也提供特殊的多折返点来实现特定条件的子
程序调用返回方式。
10.5.1 ENTRY语句与多入口点
• Fortran语言中的子程序中可以通过ENTRY语句来提供多个
入口点。
• 程序的执行效果如下:
• 请任意输入一个实数:
• -30.0
• 这是一个负数
• 它的立方根为: -3.107233
• 在上面的代码中,子程序SIGN内部通过ENTRY语句为一段执
行代码定义为一个入口点Negative(A)。在主调程序中,可
以根据情况选择子程序SIGN中的不同执行段:直接调用
SIGN将会执行入口点Negative(A)前的执行代码,并在
ENTRY语句前的RETURN语句返回主调过程;如果调用
Negative将执行入口点Negative(A)后的执行代码,并在下
一个RETURN语句返回主调过程。
10.5.2 子程序的多折返点
• 一般来说,当子程序执行完成之后,通常会直接返回主调
程序的调用处继续进行执行。关于这一点,Fortran语言中
也提供了一种特殊的返回方式来改变子程序的折返点,将
子程序的返回点指定到主调程序的其他位置。
• 程序的执行结果如下:
• 请输入一个正整数[负数-退出]:0
• 计算结果S = 0.0000000E+00 [=0]
• 请输入一个正整数[负数-退出]:2
• 计算结果S = 0.9092974 [>0]
• 请输入一个正整数[负数-退出]:5
• 计算结果S = -0.9589243 [<0]
• 请输入一个正整数[负数-退出]:0
• 需要注意,能够实现多折返点的子程序仅限于子例行子程
序,不包括函数子程序,函数子程序通过RETURN语句只能
返回到主调程序中的调用点处。
10.6 Fortran 90/95中的特殊子程序类型
• 在Fortran 90/95标准中,除了继续对前述的一般
子程序类型提供支持外,还新增了三种特殊的子
程序类型。这三种子程序类型就是前述章节中曾
经提到过的RECURSIVE、PURE和ELEMENTAL三种属
性。RECURSIVE属性允许过程进行自身调用,也就
是常说的递归调用;PURE和ELEMENTAL属性都用于
数组的并行处理。
10.6.1 RECURSIVE属性
• 在Fortran 90/95标准之前,Fortran中的子程序
是不允许进行自身调用的。在新标准中,Fortran
子程序开始允许进行自身调用,也就是经常在编
程中听到的“递归”。能够进行递归调用的一个
前提条件就是递归过程在被调用时,其中的局部
变量会使用不同的内存地址,以便在完成递归后
能够依次统计不同内存地址上的结果。
• 1.递归函数子程序
• 2.递归子例行子程序
10.6.2 PURE属性
• 在函数子程序或是子例行子程序的定义语句前添加PURE语句,将
使子程序具有PURE属性。一般来说,并不需要使用这种属性,它
通常适用于并行计算并在使用上有较多的限制。
• 具有PURE属性的子程序,其参数必须是只读的,即INTENT(IN)。
• 具有PURE属性的子程序,其参数都必须有赋值属性。
• 具有PURE属性的子程序,其中的变量不允许具有SAVE属性。
• 具有PURE属性的子程序,其包含的内部过程也必须具有PURE属性。
• 具有PURE属性的子程序,不能够使用STOP以及输入输出相关语
句,如READ、WRITE等。
• 具有PURE属性的子程序,只能够读取而不能改变全局变量的值。
10.6.3 ELEMENTAL属性
• ELEMENTAL属性与PURE属性非常相似,只不过它是
一个针对数组的应用。在具有ELEMENTAL属性的过
程中,不允许出现数组参数。该属性主要用于配
合Fortran 90/95中对于数组的整体操作。
• 程序的执行结果如下:
• 1.000000 3.000000 5.000000
7.000000 9.000000
• 0.5403023 -1.714717 0.6342880
1.994638 -2.733391
10.7 数据块程序单元
• 由于COMMON语句中的变量不能够在子程序或主程序中通过
DATA语句来直接设置初始值,需要在一个统一的程序单元
中进行数据的初始化工作。这种统一的程序单元就是数据
块程序单元。
• 数据块子程序单元是一种为有名公用块中的变量定义初始
值的程序单元。它只允许包含变量声明和变量初始值,不
可以包含可执行语句。数据块程序单元是一种落后的程序
设计手段,在新的Fortran 90/95标准中已经有新的模块程
序单元可以完全提供数据块的所有功能。但是一些早期的
大型Fortran程序往往会使用这种程序单元来进行变量的初
始化工作,因此有必要对这种过时的语法进行简单的介绍。
10.7.1 定义数据块子程序
• 数据块子程序的一般形式如下:
• BLOCK DATA [块数据名]
• [说明部分]
• END [BLOCK DATA [块数据名]]
• 数据块子程序中的变量一般通过DATA语句来进行
初始化。公共块中命名的变量只能在数据块子程
序单元或某个过程中初始化一次。更好的编程方
法是使用模块程序单元而不是数据块单元来进行
全局变量的声明和变量的初始化工作。
10.7.2 使用数据块子程序
• 数据块子程序在整个程序结构中是一个独立的单
元,不能出现在其他的程序单元之中。同时,数
据块子程序也不需要进行显示的引用,编译程序
会在编译阶段根据数据块子程序的定义为其中的
变量做好赋初值的工作。
10.7.3 数据块子程序示例
• 下面来看一段数据块程序单元的代码实例。这段
代码没有什么实际的用途,主要用于说明数据块
在实际编程中如何应用。
• 程序的运行结果非常简单,如下所示:
• This is a demo of BLOCK DATA
• 1.000000 -34.00000 0.7800000
3.141593
• 1.00000000000000 2.00000000000000
2.00000000000000
• 2.00000000000000 1.00000000000000
• -1 -2 -2
-2 -1
10.8 子程序的参数
• 函数子程序和子例行子程序在有些时候都会涉及
到大量外部传递的参数。这些参数在传递过程中
的行为如何?有些什么特性?在过程中如何发挥
作用?这就是本节将要介绍的基本内容。
10.8.1 参数传递规则
• 子程序中的哑元在与实元进行哑实结合时的一条
基本规则就是对应位置上的数据要类型正确。参
数类型如果不一致,则很可能发生不可预料的结
果。由于Fortran语言在进行参数传递时采用的是
传地址的方式,即传递变量所占的第一个地址。
进行参数传递的哑元和实元会用自身的数据类型
规则来解读同一片存储单元,一旦数据类型不一
致就很容易发生解读错误的问题。
• 1.参数传递错误示例
• 2.类型不匹配解决方法
10.8.2 子程序的接口
• 在前一节的最后一个例程中,介绍了一种新的程
序单元——接口。通过ITERFACE语句可以向调用
程序单元说明过程的某些信息。
• 1.显示接口
• 2.隐式接口
• 3.接口块
• 4.过程接口块
10.8.3 接口块使用情况
• 在实际编程中,接口块并不是每个主调程序都具有的。如果仅使用Fortran 77语言编
写子程序,则无需在主调程序单元中编写接口块;但是如果使用Fortran 90/95中提
供的一些现代化手段来编写程序,通常需要在主调程序单元中写入调用程序的接口
块,否则在编译过程中很容易出错。此外,Fortran 90/95中不提倡使用COMMON语句
进行程序单元间的数据传递和共享,该语句的功能已经由模块中的接口块代替。确切
一点,如果遇到下列情况时,在主调程序中必须声明被调过程的接口块。
• (1)如果外部过程具有以下特征:
• 过程的哑元有可选择属性。
• 过程的哑元是假定形数组、指针变量、目标变量。
• 过程的结果是数组或指针。
• 对于字符型函数过程的结果,其长度不是常数,也非假定长度。
• (2)如果调用过程时出现下列情况:
• 使用了变元关键字(如范例TEST1009)。
• 使用类属名来进行调用。
• 使用超载赋值号(对于子程序)。
• 使用超载操作符(对于函数)。
• 在要求纯过程的上下文中。
• (3)如果过程前缀关键词是ELEMENTAL。
10.8.4 INTENT属性应用
• 在Fortran语言中,虚实结合是在不同程序单元之间进行数值传递
的主要手段。例如,主程序中实元A与子程序中的哑元X结合,就
是将实元A在内存中的地址传递给哑元X,也就是将主程序中A的值
传递给子程序中的X,该值可供子程序运算;反之,如果子程序中
的变量Y在子程序执行完后有值M,则Y与实元R结合后会使主调程
序单元中的实元变量R的值也变成M。
• 在Fortran 77时代,在编写程序时无法确切地说明过程中哑元的
目的。过程中的哑元到底是用来将数据传入到过程中的,还是用
来将数据传出到主调程序单元中,或者是两种功能都兼而有之。
这个概念是含糊的。在进行调用时只能由程序员自行记住过程中
各个哑元的性质。进入到Fortran 90/95时代,为了避免当过程内
部变量的值发生变化后返回到主调程序单元时可能造成的混淆情
况,在过程的变量类型定义中,可以为哑元指定INTENT属性(字
面信息就是意图属性)。哑元按照其在参数传递过程中的作用可
以分为输入输出两用、仅用于输入和仅用于输出。
10.8.5 关键字变元
• 关键字变元其实已经在前面提到过了,这里单独进行更详
细一点的讲解。一般来说,哑实结合必须遵循三个一致的
原则,否则会出现错误。所谓三个一致,是指:哑元与实
元的位置一致;哑元与实元的个数一致;哑元与实元的类
型一致。
• 上述要求需要程序员记住每个哑元的名称及位置,在书写
或阅读过程中的实元表时要对其中每个表达式追溯到它原
来的哑元是什么,非常不方便。针对这一问题,Fortran
90/95中通过三种方法来放宽这三个一致的原则:
• 用关键字变元放宽位置一致;
• 用可选择变元放宽个数一致;
• 用类属过程放宽类型一致。
10.8.6 可选择变元与OPTIONAL属性
• 在调用的三个一致原则中,实元与哑元个数一致
是另一个比较严格的要求。但在某些过程中,虽
然哑元列表中列出了好几个哑元,但在实际调用
时不一定每次都需要全部用到。对于这种情况,
Fortran 90/95标准中允许只对哑元表中的部分哑
元进行哑实结合,另一部分哑元则按需要进行有
选择的结合,这部分哑元又被称为可选择变元。
例如Fortran语言中的内在数组函数SUM,它的完
整函数及哑元表如下:
• SUM(ARRAY,DIM,MASK)
10.8.7 哑元改名
• 广泛的通用性是过程的一大优点。一旦针对某个通用处理
操作的过程被编好,求解具体问题的主程序就可以对它进
行调用。但是在应用于不同的目的时,具体问题的物理名
称可能是不同的。为了加强程序的可读性与可维护性,在
不同的场合使用某一个过程时,需要将哑元名称改为与该
领域械奈锢砻 埔恢隆TFortran 90/95中,允许改变过程
中变元的名称。变元名称的改变是在接口块中进行的,所
以在主调程序中需要写出相应的接口块。
• 例如上面求多边形边长的子程序,如果调用时想要将表示
边长的哑元名A、B、C和D改为物理意义明确的名称Upper、
Down、Left和Right,只需在主调程序中编写相应的接口
块,在接口块的哑元表中使用新的哑元名称就可以了。
10.8.8 INTRINSIC属性
• 与EXTERNAL语句或属性说明的实元是外部过程相
对应,INTRINSIC语句或属性用来说明实元实际上
是内在过程。说明一个对象名具有INTRINSIC属性
可以有两种方法,一种就是在类型说明语句中加
入INTRINSIC属性,具体的形式如下:
• 类型说明语句, INTRINSIC :: 内在函数名[,内在
函数名]…
• 另一种方法是直接使用INTRINSIC语句进行说明,
具体的形式如下:
• INTRINSIC :: 内在过程名[,内在过程名]…
10.8.9 数组作为参数
• 除了将单独的变量作为过程的参数以外,还可以将数组作
为过程的参数进行传递。由于数组在内存中会占用一片连
续的存储单元,因此在传递数组参数时,实际上是传递数
组元素中的某一个内存地址。
• 1.数组传递示例1
• 掌握上述原则可以在程序设计中加以巧妙的应用。
• 2.数组传递示例2
• 3.字符串变量传递
• 字符串变量在作为实元进行参数传递时,可以像数组一样
不必特别声明字符串的长度。在进行传递时,也是将字符
串中第一个字符在内存中的物理地址传递到过程之中。
(详细内容请参照本书)
10.8.10 过程作为参数
• 在Fortran语言中,能够进行哑实结合的数据除了
变量、数组、数组元素、字符等之外,还允许将
函数子程序名或子例行子程序作为参数来进行传
递。
• 1.内部函数作为参数进行传递
• 2.外部函数作为参数传递
• 3.子例行子程序作为参数传递
10.9 局部变量和SAVE属性
• 在过程中除了通过哑实结合方式传递进来的参数
外,还有其他一些在过程中需要使用的变量。这
些变量在过程中具有的特性、作用范围就是本节
将要介绍的内容。
10.9.1 变量的作用范围
• 函数子程序或是子例行子程序中的变量(不包含
通过哑实结合方式传进来的参数)都有自身的
“生存周期”。它们只能在子程序执行的过程中
保持活力,当子程序执行完成后,这些变量就随
着子程序的退出而消亡,它们本身保存的数据通
常也会随之而消失。
• 正因为上述原因,仅在子程序执行过程中有效的
变量也被称为局部变量,它们的“生命周期”又
被称为局部变量的作用范围。
10.9.2 SAVE属性
• 前一小节提到了,在过程中,局部变量取值在过程被调用
结束后有可能变为不确定的,因此当过程被再次调用时,
局部变量的取值在不同编译器下可能是不同的。为了避免
这种情况的出现,在Fortran 77中可以使用SAVE语句,在
Fortran 90/95中则可以在变量的类型声明中为变量声明具
有SAVE属性。在Fortran中说明一个局部变量在程序运行后
保留原来的值可以通过两种方法来实现,一种就是在类型
声明中添加SAVE属性,形式如下:
• 类型说明语句, SAVE [,其它属性] :: 变量名表
• 或者直接使用SAVE语句进行说明,形式如下:
• SAVE [变量名表]
第11章 高级输出与输入
• 在第3章中,介绍了简单的输入输出操作(也称为
表控输入输出、直接列表输入输出)语句READ、
WRITE和PRINT。这些输入输出操作语句简单易学、
使用方便。但是如果想要使输入输出的数据更为
美观、易读易用或是想要实现一些特殊的效果,
这些简单输入输出语句就显得力不从心了。在本
章中,将介绍同高级输入输出相关的语句设置、
不同种类的格式编辑符等内容。通过本章的介
绍,就可以使程序在输入输出这方面显得更加专
业。
11.1 输入输出语句的格式化设置
• 在第3章中,提到过简单输入输出语句同高级输入输出语句在形式
上没有太大差别。高级输入输出语句之所以高级,主要因为其在
于句说明中添加了丰富的格式控制说明项。正是这些格式控制说
明项使输入输出语句在进行数据的输入输出操作时显得丰富多彩。
本节将介绍同输入输出语句相关的一些详细设置。
• 在第3章中,已经介绍了三种常用的简单输入输出语句:WRITE语
句、PRINT语句和READ语句。实际上高级输入输出语句也是这三个
语句。只是在使用高级输入输出操作时,需要设置更多的输入输
出控制选项。而简单输入输出语句无需设置这些控制选项,只要
用星号“*”就可以表示系统默认的输入输出操作方式了。
• 要在输入输出语句中使用高级的输入输出选项,就要使用专门的
语句来定义格式的形式。这个语句就是FORMAT语句,也称为格式
说明语句。该语句的语法形式为:
• FORMAT (format-list)
11.2 输入输出语句与格式语句
• 在前一小节中,详细介绍了FORMAY语句的相关知
识。在介绍的过程中,提到了FORMAT语句必须是
有标号的,以便在输入输出语句中进行引用。在
这一节中,就要介绍在输入输出语句中如何引用
FORMAT语句。当FORMAT语句定义完全并编上标号
后,就可以在输入输出语句中通过引用标号的形
式来引用格式说明语句。三种输入输出语句引用
格式说明语句的形式罗列如下所述。
11.2.1 WRITE语句引用格式说明语句
• 在向外部设备输出时,语法形式如下:
• WRITE(设备号, [FMT=]格式说明语句标号) [变量
列表]
• 语句中,设备号表示要在其上输出数据的设备,
当设备号为星号“*”时,表示向默认的设备输
出;关键字段“FMT=”用来显示说明其后所跟的
标号为格式说明语句的标号,当WRITE语句说明项
中只含有一项时,“FMT=”可以省略;语句中允
许变量列表为空,此时WRITE语句的作用是输出一
个空白行。
11.2.2 PRINT语句引用格式说明语句
• PRINT语句只能向计算机的默认设备(即屏幕)上
输出数据。因此PRINT语句引用格式说明语句的形
式中不含有设备号这样的参数。语句的语法形式
如下:
• PRINT 格式说明语句标号[, 变量列表]
• 需要注意的是,PRINT语句中在引用格式说明语句
标号时,不允许使用关键字段“FMT=”。这和
WRITE语句有一个重要的区别。语句中允许变量列
表为空,作用与WRITE语句相同。
11.2.3 READ语句引用格式说明语句
• 在从外部设备中输入时,语法形式如下:
• READ(设备号, [FMT=]格式说明语句标号) [变量
列表]
• 语句中,设备号表示要在其上输入数据的设备,
当设备号为星号“*”时,表示从默认的设备(一
般是键盘)输入;关键字段“FMT=”用来显示说
明其后所跟的标号为格式说明语句的标号,当
WRITE语句说明项中只含有一项时,“FMT=”可以
省略;语句中允许变量列表为空,此时READ语句
将等待输入,指导用户键入回车键。
11.2.4 第一个输入输出综合应用
• 下面来看一段代码,这段代码综合应用了上述三种输入输出语句和FORMAT语句的交互作用。
• TEST1101.F90
• ! WRITE & FORMAT 语句的范例
• PROGRAM TEST1101
• IMPLICIT NONE
• ! 变量定义
• INTEGER :: I, J, K
• REAL :: A, B, C
• ! 可执行段
• WRITE(*, *)'Input:'
• READ (*, 100)I, J, K
• READ(*, 200)A, B, C
• WRITE(*, *)'Output:'
• WRITE(*, 300) I, J, K
• PRINT 400, A, B, C
• ! 格式说明
• 100 FORMAT(1X, 3(I3, 1X))
• 200 FORMAT(1X, 3(F6.2, 1X))
• 300 FORMAT(1X, 'I=', I3, 'J=', I3, 'K=', I3)
• 400 FORMAT(1X, 'A=', F6.2, 'B=', F6.2, 'C=', F6.2)
•
• END PROGRAM TEST1101
11.3 格式编辑符概述
• 用户在指定输出格式时,需要特定的方式向系统
说明格式的“相貌”。在Fortran中,描述格式
“相貌”的工作由“格式编辑符”(或“编辑描
述符”)来完成。格式编辑符的作用就是将数据
进行类似书报编辑对文字进行编辑排版一样的处
理,使数据以更美观的形式进行显示。在Fortran
中,格式编辑符按照用途可以分为数据格式编辑
符、控制格式编辑符和字符串格式编辑符三大类。
下面将对这三类编辑符的使用和功能进行讲解。
11.4 数据格式编辑符
• 顾名思义,数据格式编辑符主要针对程序中的整
型、实型、复型、逻辑型和字符型数据的输入输
出格式控制。下面将对其中最常用的几种编辑符
的作用和用法进行讲解。
11.4.1 I编辑符
• I编辑符适用于整型数据的输入输出,其一般形式为:
• Iw[.m]
• I是英文单词“Integer”的第一个字母,表示“整型数编
辑符”。
• w用来指示以w个字符的宽度来输出数据(通常称一个数据
所占的宽度为“字段宽度”),负数的符号也包含在字段
宽度内。如果要输出的数据实际宽度超出了w规定的宽度,
则不输出有效数据,而在该字段宽度范围内用星号“*”填
充。
• m用来指示至少需要输出m个字符宽度的数字。如果输出数
据的实际宽度小于m,则会在数据前面不足部分用0填充;
如果输出数据的实际宽度超过m,则按输出数据的实际宽度
进行输出(但不能超过w)。
11.4.2 F编辑符
• F编辑符适用于实数的小数形式输出,其一般形式为:
• Fw.d
• F是英文单词“Fixed point number”的首字母,表示“浮
点数编辑符”。
• w仍然表示要输出的“字段宽度”,包含一个小数点和负数
的负号。如果要输出的数据实际宽度超出了w规定的宽度,
则不输出有效数据,而在该字段宽度范围内用星号“*”填
充。
• d表示要输出数据的小数位数。如果要输出的实际数据的小
数位数小于d,则会在小数后不足的部分补充0;如果要输
出的实际数据的小数位数大于d,则会将实际数据中多余的
小数部分按四舍五入规则去掉。
11.4.3 E编辑符
• E编辑符用于输出指数形式的实数,其一般形式为:
• Ew.d[Ee]
• E是英文单词“Exponent”的首字母,表示“指数编辑符”。
• w还是表示要输出的“字段宽度”,包含指数部分所占的4个字符
的宽度和负数的负号。如果要输出的数据实际宽度超出了w规定的
宽度,则在该字段宽度范围内用星号“*”填充;如果输出的数据
实际宽度小于w规定的宽度,则在输出数据的前面用空格填充。
• d表示要输出数据的小数位数。小数部分的位数可以由公式w≥d+7
来确定,公式中的“7”表示一个小数点、小数点前的0、一个负
号和指数部分所占的4位。如果实际数据在指数形式下的小数位数
大于d,则多出的小数部分按四舍五入规则进行舍入;如果实际数
在指数形式下的小数位数小于d,则不足的小数部分用0进行填充。
• e表示指数部分中指数所占的位数。
11.4.4 D编辑符
• D编辑符适用于双精度数据的输出。其一般形式为:
• Dw.d
• D是英文单词“Double Precision”的首字母,表示“双精
度编辑符”。
• w仍然表示输出数据所占的字段宽度;d表示指数部分所占
的位数。具体的含义同E编辑符。
• 在使用方法上,D编辑符与E编辑符相似。只是把字母“E”
换成“D”,在数据输出时,指数部分的字母“E”用“D”
来代替。实际上,F编辑符也可用于双精度数据的输出,和
用于实型数据输出相似。但此时可能会由于不能确切估计
实际数据的大小而出现“大数印错,小数印丢”的情况。
11.4.5 A编辑符
• E编辑符用于输出指数形式的实数,其一般形式为:
• Ew.d[Ee]
• E是英文单词“Exponent”的首字母,表示“指数编辑符”。
• w还是表示要输出的“字段宽度”,包含指数部分所占的4
个字符的宽度和负数的负号。如果要输出的数据实际宽度
超出了w规定的宽度,则在该字段宽度范围内用星号“*”
填充;如果输出的数据实际宽度小于w规定的宽度,则在输
出数据的前面用空格填充。
• d表示要输出数据的小数位数。小数部分的位数可以由公式
w≥d+7来确定,公式中的“7”表示一个小数点、小数点前
的0、一个负号和指数部分所占的4位。如果实际数据在指
数形式下的小数位数大于d,则多出的小数部分按四舍五入
规则进行舍入;如果实际数在指数形式下的小数位数小于
d,则不足的小数部分用0进行填充。
• e表示指数部分中指数所占的位数。
11.4.6 L编辑符
• L编辑符适用于逻辑型数据的输出。其一般形式为:
• Lw
• L是英文单词“Logical”的首字母,表示“逻辑
编辑符”。
• w表示输出的逻辑型数据所占的字段宽度。由于逻
辑型数据在输出时只显示一个字符,即.TRUE.打
印为“T”,.FALSE.打印为“F”。因此,当w大
于1时,字符的左端用空格进行填充。
11.4.7 G编辑符
• 既然单独使用F编辑符和E编辑符都有这样或那样的缺点,
那么有没有一种编辑符足够“聪明”,可以自行判别哪种
数应该用F编辑符进行输出还是用E编辑符进行输出呢?
• Fortran提供了这样一种“聪明”的编辑符,即G编辑符。
该编辑符对F编辑符和E编辑符的长处进行了综合,能够根
据要输出的实数大小来决定用何种格式进行输出,即F型格
式和E型格式。当输出的数值过大或过小时会自动采用E型
格式,当输出的数值能够用小数形式表达时则用F型格式。
G编辑符的一般形式为:
• Gw.d[Ee]
• G是英文单词“General”的首字母,表示“通用编辑符”。
• w仍然表示输出数据所占的字段宽度;d表示指数部分所占
的位数;e表示指数部分数字的位数。具体的含义参考E编
辑符。
11.4.8 B、O、Z编辑符
• 二进制(B)、八进制(O)和十六进制(Z)编辑符是
Fortran 90标准中新增的编辑描述符,用于整数、实数、
字符和逻辑量的输出。其一般形式为:
• Bw[.m]
• Ow[.m]
• Zw[.m]
• B、O和Z分别表示“二进制编辑符”、“八进制编辑符”和
“十六进制编辑符”。
• w表示输出数据的字段宽度;m表示需要输出的最少数字位
数,缺省值为1。如果实际的输出数据宽度少于指定的字段
宽度,则数据的左端用空格填充。但对于二进制数,如果
以0填补可读性会更好一些。例如00010101显示了l0101所
有的8位,此时可以令m=w的方法来强迫数据的开始以0填补。
11.4.9 EN、ES编辑符
• 工程计数法(EN)和科学计数法(ES)也是Fortran 90标准中新
增的编辑描述符。两种编辑符的的一般形式为:
• ENw.d[Ee]
• ESw.d[Ee]
• 其中E是英文单词“Exponent”的首字母,N是英文单词
“Engineering”的第二个字母,S是英文单词“Scientific”的
首字母。
• EN和ES编辑符中的w、d、e与E编辑符中的基本类似,可以参考E编
辑符中的相关描述。梁柱编辑符与E编辑符的区别在于:
• 采用EN编辑符输出数据时,数据的非指数部分的绝对值一定在1到
1000的范围内(除非数据的数值为0),且指数可以被3整除。包
括指数部分和负号,整个数据的输出字段宽度是w个字符,小数点
后d个字符,指数宽度e是可选的。
• 采用ES编辑符输出数据时,数据的非指数部分的绝对值一定在l到
10的范围内(除非数据的数值为0),而非E编辑符的0到1。
11.5 控制格式编辑描述符
• 控制格式描述编辑符在格式语句中的作用是确定
文本的显示方式,比如数据在所在行的什么位置
进行输出、统计记录中剩余的字符数目、是否输
出数据的加号等。下面分别对常见控制格式编辑
描述符进行介绍。
11.5.1 X编辑符
• X编辑符是控制格式编辑描述符中最常使用的一种。
该编辑符用来在输出数据时产生空格。前面的数
据格式编辑描述符在输出数据时,数据之间没有
空格。为了避免读数困难,前面的例程在输出
时,尽可能每行只输出一个数据。有了X编辑符之
后,数据的输出就更为方便了。X编辑符的一般形
式为:
• nX
• 其中n表示要插入的空格数量。Fortran 77标准中
允许n为负数,但是Fortran 7的子集以及很多
Fortran 90/95编译系统,例如Compaq Visual
Fortran、gFortran和G95,并不支持这种用法。
11.5.2 纵向走纸控制符
• 在介绍X编辑符时,提到了格式语句中第一项如果是1X,则
在打印设备上可以作为纵向走纸控制符。那么什么是纵向
走纸控制符呢?
• Fortran中规定:把格式记录中的信息传送到打印设备上
(如打印机或终端)时,格式说明中的第一个字符用作纵
向间隔控制标志,称为纵向走纸控制符。格式说明中的第
一个字符不再被打印出来,而从格式说明中的第二个字符
开始打印。
纵向走纸控制符及功能
纵向走纸控制功能 常用形式
格式说明的首字符
(空格) 移到下一行开头 1X, ‘ ‘
• PRINT 1,3
• PRINT 2,13
• 1 FORMAT (' I=',I2,' J=',I2)
• 2 FORMAT (' K=',I2,:,' L=',I2)
• 反斜杠“\”编辑符和美元“$”编辑符在格式输入输出语
句中的作用相同:都是在输出一个记录行后取消回车符,
接着输出的记录会紧接在前一个记录的后面位于同一行。
• 这两个编辑符常用于输出的字符串与输入数据需要显示于
屏幕同一行的情形。这两种编辑符在使用方式上完全一
样,只要放在格式说明列表的最后就可以了。例如下面的
一段代码:
• WRITE(*, 100)
• 100 FORMAT (' ENTER RADIUS VALUE: ',$)
• READ(*, *) RADIUS
• 该段代码首先会在屏幕的第一行显示如下内容:
• ENTER RADIUS VALUE:
11.7.2 可变格式编辑符
• 在前面的格式说明语句中,控制编辑描述符中多是常数。
例如整型变量对应的I编辑符中,规定的字段宽度是固定
的,如果数据的实际长度没有那么宽,则多出的部分就只
能用空格填充。这就使数据的输出不太美观。
• 在Compaq Visual Fortran中提供了可变格式编辑符来解决
这类问题,即用尖括号括起来的数值表达式来表示可变的
格式。该编辑符的一般形式如下:
• <数值表达式>
• 其中的数值表达式可以是常量表达式,也可以是变量表达
式。可变格式编辑符通常用作I编辑符中的可变字段宽度定
义、可变重复系数等。
11.8 I/O列表
• I/O列表也叫做输入/输出列表,它罗列了需要进
行输入/输出操作的所有变量。在Fortran中,针
对I/O列表有一些特殊的用法和语句,如这里介绍
的NAMELIST语句。本节将主要介绍同I/O列表相关
的知识。
11.8.1 NAMELIST语句
• NAMELIST语句是Fortran 90/95标准中正式收录的一种特殊
输入/输出方法。其实早在Fortran 77时代,一些编译器就
已经开始支持这种输入输出用法。但是,Fortran 77时代
的NAMELIST语句没有统一的标准,各家编译器厂商大多各
行其道,怎么方便怎么来。这种情况直到Fortran 90/95时
代才得到了改观。在新标准中,NAMELIST语句作为一种正
式的标准语句,开始有了统一的使用格式。
• NAMELIST语句也称为名称列表语句,其作用就是将一组变
量同一个列表组名相关联。该语句将一组相关的变量封装
在一起,在对这些变量进行输入/输出操作时,只需要确定
在输入/输出语句中确定用哪一个NAMELIST就可以了。也就
是说这个列表组名可以在输入/输出语句直接被引用,其中
所封装全部变量会依次被输入/输出相应的数据。
11.8.2 I/O列表实体
• 在使用输入/输出语句(如READ、WRITE、PRINT语
句)进行输入输出操作时,需要知道如何进行数
据传递和传递什么数据的信息。其中如何进行数
据传递已经在前面的小节中进行了较为详细的介
绍,而传递什么数据则是由I/O列表(io-list)
中列出的将要进行输入/输出操作的项来确定的。
也就是说,I/O列表提供了将要进行输入/输出操
作的数据的相关信息。其实在前面的章节中早就
已经遇到过I/O列表,只是没有专门列出来进行说
明。本小节专门将其罗列出来,将I/O列表实体的
有关知识进行一个简单的总结。
第12章 文件操作
• 在前面的章节中,大部分的程序在运行时,总是从键盘上
输入数据,程序的输出也几乎总是显示在计算屏幕上。对
于一些小程序,输入的数据不多、数据结构也不复杂,采
用这种方式是可行的。但是,对于一些大型的应用,例如
CFD(Computational Fluid Dynamic,计算流体力学)、
CAE(Computer Aided Engineering,计算机辅助工程)
等,都涉及到几百兆甚至上G的数据输入与输出。在这种情
况下,如果仍然采用终端输入输出的方式就很难想象了。
另一方面,使用终端输入输出方式时,程序在运行中可以
通过屏幕查看到输出的结果,一旦退出程序,此时要想再
次查看输出的结果就很困难了,除非再次运行程序。使用
文件就可以避免上述问题,同时文件还能避免重复处理,
保存起来也很方便。本章将介绍文件的基本功能和主要操
作。
12.1 文件与逻辑设备
• 在Fortran中,文件和逻辑设备总是成对出现的。
文件总是同特定的逻辑设备相关联,不管这个文
件是外部文件还是内部文件。文件同逻辑设备的
连接是通过设备描述符来进行的,外部文件的设
备描述符是数字,而内部文件的设备描述符则是
字符。但是有一点要注意,逻辑设备的概念是大
于文件的。逻辑设备不仅仅同文件相关联,而且
同计算机的外部设备也相关联。本节将介绍文件
和逻辑设备的相关内容。
12.1.1 逻辑设备
• 逻辑设备是与文件操作密切相关的重要概念。在Fortran语
言中对文件和外部设备的操作都要通过逻辑设备才能进行。
在对文件和外部设备进行操作之前,都要把它们连接到相
应的逻辑设备上。逻辑设备通过设备描述符与相应的文件
或外部设备相关联。
• 在Fortran语言中,内部文件的设备描述符和外部文件的设
备描述符是不同的:内部文件通过一个字符型变量或其它
变量名来进行描述;外部文件则使用OPEN语句打开文件时
的数字(也称为设备号)作为文件的设备描述符,或是用
默认的设备号(例如前面经常用到的星号“*”)作为文件
的设备描述符。
12.1.2 外部文件
• 在Fortran中,文件又分为外部文件和内部文件。如果文件
的保存介质是计算机上的内存,则称这种类型的文件为内
部文件。如果将内存中的数据记录到磁盘文件或输入/输出
到其他的外部设备(如打印机、显示器、键盘)上时,则
称为外部文件。
• 在Fortran语言中,同一个外部文件相关联的设备描述符必
须是—个正整数(整型常量、整型变量或整型表达式)或
是星号“*”。设备描述符的数值范围为0到2,147,483,640。
例如下面的代码段将外部文件external.dat与设备描述符
10连结起来,并往其中书写数据。
• OPEN(UNIT = 10, FILE = ‘external.dat’)
• WRITE(10, ‘(A)’) ‘The data:’
12.1.3 内部文件
• 保存在计算机内存中的数据也可以像硬盘上的文件一样进行操作。
在Fortran语言中,把同设备描述符连接、能够像外部文件一样进
行输入输出操作的保存在一块内存中的数据称为内部文件。能够
同内部文件进行连接的设备描述符只能是字符串或是字符数组。
• 在实际的编程实践中,常会遇到两种基本类型的内部文件。这两
类内部文件的基本情况如下:
• 第一类内部文件是以单个存储空间为操作单位的,它可以是一个
字符型变量、字符型数组元素或者单个的非字符型数组元素。其
中的非字符型数组元素的记录长度至少应该与要写入数据的字段
宽度一致,否则会在进行写入操作时因为空间不足而发生运行错
误。这是在使用内部文件时最容易鱿值拇砦蟆
• 第二类内部文件是以多个连续存储空间为操作单位的,它可以是
一个字符型数组、基于字符的派生数据类型或是非字符型数组。
其中的非字符型数组的记录排列顺序和记录的长度至少应该与要
输入数据的字段宽度保持一致。
12.2 外部文件分类
• 外部文件按照文件中数据的保存结构,可以分为
有格式文件、无格式文件和二进制文件。按照文
件中数据的存取方式,可以分为顺序存取文件和
直接存取文件。本节将简要介绍这几种文件的特
点,至于其中涉及到的一些语句操作会在后面的
小节中相继介绍。
12.2.1 有格式文件
• 在格式化文件中,数据内容的记录是以ASCII码字符的方式
进行的,在日常的计算机应用中经常遇到的“文本文件”
就属于这一类文件。
• 格式化文件中的每一条记录都是以ASCII码中的回车符(CR)
加换行符(LF)来结束的。使用一般的文本编辑软件打开
有格式文件就可以直接看到其中的内容,即存放在文件中
的数字就是平时所看到的数字,字符就是平时所看到的字
符。而用文本编辑软件打开无格式文件或二进制文件时,
看到的则是一些十六进制的字符。因此如果要使文件中的
内容可以被人直接炊 詈檬褂糜懈袷轿募
• 在实际编程中,可以使用OPEN语句来创建或打开一个有格
式文件。在使用OPEN语句创建文件时,如果省略FORM参
数,则默认建立的文件为有格式文件,或是将FORM参数设
置成“FORM=’FORMATTED’”的形式也可以建立有格式文
件。
12.2.2 无格式文件
• 无格式文件由物理块组成的一系列记录,所存储的每一个记录都
保存了一系列的数据,这些数据的存放方式与它们在内存中的存
放非常相似。因此,无格式文件中同类型的数据如果种别参数相
同,则在文件中的记录长度也是一样的。这种数据存放方式使无
格式文件在进行输入输出操作时只需要做很少的数据转换工作。
由于去掉了格式控制,同存储相同信息的有格式文件相比,无格
式文件在使用数据信息时所做的处理更简洁更迅速,文件体积也
比较小。基于同样的原因,如果无格式文件中存放着数字,用户
使用文本编辑软件打开这些文件时将无法看到这些数字。但是,
对于其中保存的字符型数据,则可以通过文本编辑软件进行查看。
• 使用OPEN语句可以创建或打开一个无格式文件。在使用OPEN语句
创建文件时,通过将FORM参数设置成“FORM=’UBFORMATTED’”
的形式就可以常见一个无格式直接存取文件。
12.2.3 编译器支持的二进制文件
• 二进制文件是处理最快、最简洁的一种文件,也是最紧凑
的存储格式,适合于大批量数据的存储。同无格式文件相
比,二进制文件中所包含的记录就只是数据,其他所有的
格式控制字符(如回车符、换行符)都不会出现在文件中。
在进行二进制文件的读取操作时,变量和数据的读取顺序、
类型、种别必须和输出的变量和数据完全一致,否则会发
生错误的数据读取。
• 在Fortran标准中并不直接支持这种类型的文件,能够支持
这类文件格式的编译器大多是计算机厂商的自行扩展。例
如在Compaq Fortran中,可以使用OPEN语句来创建和打开
一个二进制文件。使用OPEN语句创建文件时,只需将FORM
参数设置成“FORM=’BINARY’”的形式就可以创建一个二
进制文件。
12.2.4 顺序存取文件
• 存放在顺序存取文件中的数据必须一个记录接一个记录地
按顺序被访问(除非使用REWIND或BACKSPACE语句来改变读
取的位置)。打个比方来说,如果程序中需要读写第N条记
录,则必须已经对前面的N-1条记录进行过读写操作。
• 在输入输出操作中,有些操作只能在顺序存取文件中才可
以使用。这些操作包括非推进式的输入输出、直接列表输
入输出和名称列表输入输出。内部文件也必须是顺序文件。
键盘、显示器和打印机等要求顺序访问的外部设备也必须
以顺序文件的方式进行连接。
• 采用OPEN语句的默认设置来打开或创建的文件都是顺序文
件,当然也可以将OPEN语句中的ACCESS参数设置成
“ACCESS=’SEQUENTIAL’”的形式来显式说明要创建的顺
序文件。
12.2.5 直接存取文件
• 存放在直接存取文件中的记录可以以任意顺序进行读写操作。这
种文件中的记录从1开始进行连续编号,所有记录的长度都是一致
的,它通过OPEN语句中的RECL参数来描述。
• 在直接存取文件中对记录进行存取是通过指定要访问的记录号来
实现的。因此,直接存取文件通常用于需要对数据进行随机访问
的应用场合,一个最常见的应用就是数据库。
• 采用OPEN语句可以创建或打开一个直接存取文件。在OPEN语句中
通过将ACCESS参数设置成“ACCESS=’DIRECT’”的形式就可以向
系统说明创建或打开的文件时直接存取文件。
• 直接存取文件中的每个记录必须具有相同的长度。如果实际输出
的记录长度不等,则要取所有输出记录中最大的长度作为每个记
录的长度。如果要使用一个已经存在的直接存取文件,则在OPEN
语句的RECL参数中说明的记录长度必须与原文件中的实际记录长
度一致。使用中要特别注意,尾随的空格符会占用一个字节,回
车符、换行符不计入记录的长度。
12.3 文件操作语句
• 前面介绍了Fortran中外部文件的分类和这几类文
件的基本特点。从本节开始,将开始介绍同具体
的文件操作有关的内容。在介绍文件操作之前,
有必要先行介绍文件操作常用的语句。在熟悉了
文件操作语句之后,才能更好的理解不同的文件
存取操作是如何实现的。
12.3.1 操作语句概述
• 在Fortran语言中,同文件操作有关的语句包括输入输出语
句、文件连接语句、文件查询语句和文件定位语句等几
类,通过这些语句可以完成文件的连接与关闭、文件记录
的输入与输出、文件状态的查询和文件记录指针的定位等
操作。通过这些语句,Fortran程序能够自由的操作大多数
类型的文件。下面给出各类操作所包含的具体操作语句。
• 同文件连接和关闭相关的语句包括:OPEN语句、CLOSE语句。
• 同文件查询相关的语句包括:INQUIRE语句。
• 同文件定位相关的语句包括:BACKSPACE语句、ENDFILE语
句、REWIND语句。
• 同文件输入和输出相关的语句包括:READ语句、WRITE语句。
12.3.2 OPEN语句
• 在Fortran语言中,OPEN语句将设备描述符和具体的外部或
内部文件连接起来,可用于建立一个新文件并使其与一个
设备描述符相连,或者改变某一个连接的属性。OPEN语句
有着丰富的参数选项,能够对文件的各种性质进行指定,
是Fortran语言中最为复杂的一种语句。熟悉和掌握OPEN语
句中各个参数项的意义和作用,是进行文件操作的基础。
OPEN语句的一般形式如下:
• OPEN ([UNIT = ]io-unit [, FILE = name] [, ERR =
label] [, IOSTAT = i-var], slist)
• 语句中括号内的部分就是OPEN语句的参数项,也称为说明
项。包括设备号说明符、文件名说明符、错误处理说明符、
I/O状态说明符和属性说明符slist,其中属性说明符又包
括了一系列文件属性说明。
12.3.3 CLOSE语句
• CLOSE语句可以理解成OPEN语句的“逆语句”,它
用于解除设备号与文件之间的连接状态,又称关
闭文件。CLOSE语句的参数选项也较为丰富,提供
的功能也较多。CLOSE语句的一般形式为:
• CLOSE([UNIT = ]io-unit [,STATUS = p]
[,ERR=label] [,IOSTAT=i-var])
• 语句中括号内的部分就是CLOSE语句的参数项,也
称为说明项。包括设备号说明符、文件状态说明
符、错误处理说明符、I/O状态说明符。
12.3.4 OPEN语句和CLOSE语句示例
• 下面给出一段代码,来演示OPEN语句和CLOSE语句
以及其中的一些常用关键字在程序中的应用。
• (详细内容参照本书)
12.3.5 READ和WRITE语句
• READ和WRITE语句在高级输入与输出一章中已经介
绍过了,但还不够详细。在进行文件的写入或读
取操作时,READ和WRITE语句通过设定不同的参数
内容,可以实现有格式、无格式、二进制、直接
或是顺序的数据存取方式。
• READ和WRITE语句的完整形式几乎完全一致,控制
的参数类型也相当。下面列出这两个语句的通用
形式:
• READ/WRITE (eunit, format [, nml-group] [,
rec] [, advance] [, size] [, iostat] [, err]
[, end] [, eor]) [io-list]
12.3.6 REWIND语句
• REWIND语句也被称为反绕语句,其作用是使与指定设备号连接的
文件位置指针指向文件的开头,通常用于顺序文件的读出操作。
• REWIND语句的一般形式如下:
• REWIND ([UNIT=]io-unit [, ERR=label] [, IOSTAT=i-var])
• 其中,括号内的部分称为控制说明符,用于控制REWIND语句的行
为方式。下面分别对各个说明项进行解释。
• 1.设备号说明符:设备号说明符用来指出要进行操作的文件的设
备号。其语法形式如下:
• [UNIT = ]io-unit
• 2.错误处理说明符:错误处理说明符用于指定在REWIND语句执行
出错时将要运行的语句,其语法形式为:
• ERR = label
• 3.I/O状态说明符:I/O状态说明符指定语句执行操作的状态,其
一般形式为:
• IOSTAT = i-var
12.3.7 BACKSPACE语句
• BACKSPACE语句也称为回退语句,其作用是使与指定设备号连接的
文件位置指针后退一个记录的位置,一般用于顺序文件的存取操
作。BACKSPACE语句的一般形式如下:
• BACKSPACE ([UNIT=]io-unit [, ERR=label] [, IOSTAT=i-var])
• 其中,括号内的部分称为控制说明符,用于控制BACKSPACE语句的
行为方式。下面分别对各个说明项进行解释。
• 1.设备号说明符:设备号说明符用来指出要进行操作的文件的设
备号。其语法形式如下:
• [UNIT = ]io-unit
• 2.错误处理说明符:错误处理说明符用于指定在BACKSPACE语句
执行出错时将要运行的语句,其语法形式为:
• ERR = label
• 3.I/O状态说明符:I/O状态说明符指定语句执行操作的状态,其
一般形式为:
• IOSTAT = i-var
12.3.8 ENDFILE语句
• ENDFILE语句的作用是在顺序存取文件中写入一条文件结束记录并
将文件的位置指针定位在该记录之后。ENDFILE语句的一般形式如
下:
• ENDFILE ([UNIT=]io-unit [, ERR=label] [, IOSTAT=i-var])
• 其中,括号内的部分称为控制说明符,用于控制ENDFILE语句的行
为方式。下面分别对各个说明项进行解释。
• 1.设备号说明符:设备号说明符用来指出要进行操作的文件的设
备号。其语法形式如下:
• [UNIT = ]io-unit
• 2.错误处理说明符:错误处理说明符用于指定在BACKSPACE语句
执行出错时将要运行的语句,其语法形式为:
• ERR = label
• 3.I/O状态说明符:I/O状态说明符指定语句执行操作的状态,其
一般形式为:
• IOSTAT = i-var
12.3.9 INQUIRE语句
• INQUIRE语句又称为文件查询语句,其作用就是获得指定文
件的特定属性的相关信息。要查询的文件可以通过文件名
来指定,也可以通过与文件相连的设备号来指定。INQUIRE
语句的一般形式为:
• INQUIRE (FILE = name[or UNIT = io-unit] [, ERR =
label] [, IOSTAT = i-var], slist)
• 语句中括号内的部分就是INQUIRE语句的参数项,也称为查
询项。包括设备号说明符或文件名说明符、错误处理说明
符、I/O状态说明符和属性查询符slist,其中属性查询符
又包括一系列文件属性查询。文件属性中有相当一部分已
经在OPEN语句中解释过了,在INQUIRE语句中它们大多有新
的定义。
12.4 文件存取
• 前面介绍了Fortran中外部文件的分类和这几类文
件的基本特点以及同文件操作相关的各类语句。
从本节开始,将开始介绍如何操作不同类型文件。
在本节中,首先会用一小段篇幅来介绍文件的构
成,即文件是由什么构成的、组织结构又是什么
样的。在对这两个问题有了一个清晰的概念后,
再来讨论具体不同类型的文件操作。
12.4.1 文件的构成——记录
• 在本章的第二节中,已经介绍了外部文件的基本分类情况。
介绍中已经提到了不少概念,如记录、格式、存取方式等
等,但并没有具体给出这些概念的含义。本节就将补上这
一课。
• 首先是记录。文件都是由一个个记录组成的。所谓记录是
指数字或字符的序列,一个记录就是一系列数字或字符组
成的一个集合。在进行存取操作时,基本的操作单位就是
记录。一个记录会被看作一个整体,其中的数字或字符会
被一次性读出或写入,而不管具体有几个数字、有几个字
符。
• 根据数字或字符在记录中保存的方式或保存的内容,可以
将记录分成三种形式,即格式化记录、非格式化记录和文
件结束记录。
12.4.2 记录的组合
• 在讲解了记录的相关概念后,接下来的问题就是记录的组
合问题了。也就是说,记录与记录之间是以什么方式来排
列的呢?这就涉及到文件的结构问题了。
• 在Fortran支持的文件中,有两类文件结构,即顺序存取和
直接存取。在顺序文件中,各个记录依次排列,记录的长
度可长可短。读取这种类型的文件时,计算机或者需要知
道每条记录的长度(无格式文件的作法),或者需要知道
正在读取的记录在哪儿结束(有格式文件的作法)。而对
于直接存取文件,各个记录的长度都是一定的,计算机只
要知道该读取第几条记录就可以了,不再需要知道每条记
录的长度信息。
12.5 有格式顺序存取文件
• 所谓有格式顺序存取文件是指由按顺序排列在一
起的一系列有格式记录组成的文件。图是有格式
顺序文件的组织结构示意图。
12.5.1 有格式顺序存取文件的创建
• 程序首先需要用户输入用户名,并将用户名字作为数据文
件的名字。在提示了数据的输入方式后,程序会打开数据
文件开始记录用户的输入。在第17行,程序使用READ语句
来单独执行一次输入操作,用于初始化循环判据Name。如
果Name的输入值为“END”,则程序不会进行数据记录,而
是直接关闭已经打开的文件;否则,程序进入一个当型循
环(DO WHILE语句)之中,不断读取用户的输入,直到用
户键入“END”。
• 为了实现上述形式的操作,在WRITE语句的格式说明符中使
用了冒号“:”编辑描述符。在高级输入与输出一章中曾介
绍过这种编辑描述符,其作用就是在输入数据数量少于与
格式说明符中的数据数量时使后继的格式说明无效。这就
使用户只输入“END”后就可以不再进行数据输入成为可能。
需要注意,程序在第19行代码:
• WRITE(*, '(I5,":",$)') I
12.5.2 有格式顺序存取文件的查询
• 对于有格式顺序存取文件而言,也可以实现对文
件内容的查询,前提是文件中有足够的信息提供
查询操作并且明确的知道文件的保存格式。下面
的例子就将演示有格式顺序存取文件的查询实例。
这个程序由三个部分组成:数据文件建立子程
序,用于建立数据文件;数据文件查询子程序,
实现文件查询功能;主程序,用于管理两个子程
序。程序会采用单一程序结构,两个子程序会以
内部过程的形式在主程序单元中定义。
12.6 有格式直接存取文件
• 在有格式直接存取文件中,所有记录的长度都相
同并且可以以任意顺序进行读写。如图是有格式
直接存取文件的组织结构示意图。
12.6.1 简单有格式直接存取文件的建立
• 下面一段短小的代码可以加深对有格式直接存取文件组织结构的
理解。
• TEST1210.F90
• ! 有格式直接存取文件的范例
• PROGRAM TEST1210
• IMPLICIT NONE
• ! 文件创建
• OPEN(UNIT = 10, FILE = 'FDF.DAT', FORM = 'FORMATTED',
ACCESS = 'DIRECT', RECL=10)
• WRITE(10,'(A8)', REC=1) 'RECORD 1'
• WRITE(10,'(A7,I1)', REC=3) 'RECORD ', 3
• CLOSE(10)
• 在文件的操作上,无格式直接存取文件与有格式
的同类文件没有什么区别。
• (详细内容请参照本书)
12.9 二进制顺序存取文件
• 二进制文件以一类特殊的文件。标准Fortran并不支持此类
文件的应用,但是这类文件在存储空间或存储精度上有着
别的类型文件所不具备的优势。因此,仍然有必要对此进
行介绍。在Compaq Fortran中提供了对此类文件进行操作
的手段:通过将“FORM=”参数段声明为
“FORM=’BINARY’”的形式,就可以对二进制文件进行存
取操作。
• 二进制顺序存取文件同其他格式的顺序存取文件在组织结
构上没什么区别,都是由一系列按一定顺序排列起来的记
录组成的。但是,二进制文件记录中的数据是以数据在计
算机内部的保存形式,即二进制的形式直接保存在记录中
的。系统也不会在记录之间使用分隔符(如回车符、换行
符等)来区分不同的记录,也不会使用保留字节来说明记
录之间的联系,更没有说明文件结构用的特殊字节。文件
中记录的长度可以是不相等的。
12.9.1 二进制文件的创建
• 下面来看一段实际的例子,代码会生成一个简短
的二进制直接存取文件。为了检验一些控制符在
文件中的作用,程序在写入数据的过程中特意调
用了空WRITE语句,试图写入回车和换行符。
12.9.2 二进制顺序存储文件的查询
• 下面用二进制顺序存储文件的格式来改写程序
TEST1208,以便加深对这种文件存取操作的理解。
• (详细内容请参照本书)
12.10 二进制直接存取文件
• 二进制直接存取文件与前面提到的两种直接存取文件在文件组织
结构上是不同的,这就需要在实际操作过程中时刻注意。
• 二进制直接存取文件也存储一系列二进制数记录,它们也可以按
任何顺序进行访问。与二进制顺序存取文件不同的是,这些记录
的长度是相等的,由OPEN语句中的“RECL=”选项指定。在二进制
直接存取文件中可以写入部分记录,记录中末使用的部分将以未
定义数据填充;也可以将一条有多余数据的记录写入到两条记录
中,这样的操作在别的直接存取文件中是不允许的。
• 在二进制直接存取文件中可以使用一条读或写的语句来读写多于
一条的记录,而这样的操作在其他类型的直接存取文件中将引发
错误。这一特性虽然极大的提高了二进制直接存取文件的操作灵
活性,但也要在进行二进制直接存取文件的操作时需要加倍小
心,否则很容易出错。在其他类型的苯哟嫒∥件中所能进行的一
切操作在二进制直接存取文件中都是合法的。此外,在二进制直
接存取文件中任何未使用的记录空间都将保持为未定义状态,而
不会用空格去填充。
12.10.1 二进制直接存取文件的创建
• 下面来看一段二进制直接存取文件的创建实例。
• TEST1218.F90
• ! 二进制直接存取文件的创建
• PROGRAM TEST1218
• IMPLICIT NONE
• ! 文件创建
• OPEN(UNIT = 10,FILE = 'BDF.DAT', FORM = 'BINARY',
ACCESS = 'DIRECT', RECL = 10)
• WRITE(10, REC=1) 'abcdefghijklmno'
• WRITE(10, REC=3) 4,5
• WRITE(10, REC=4) 'pq'
• CLOSE(10)
键盘、屏幕、外部磁盘、内存等 设备名 说明
F ( a + (i − 1) ⋅ h) + F (a + i ⋅ h)
Si = ⋅h
2
14.1.3 Simpson法
• Simpson(辛普森)法的基本思路就是用一段抛物
线来代替子区间内原来的曲线。这样就用一系列
的二抛物线段来对原来的曲线进行拟合,计算这
些抛物线所在子区间的面积,最后就可以得到所
需要的积分值。
14.1.4 数值积分计算子程序的应用
• 有了前面介绍的三个数值积分计算子程序之后,可以考虑将其放入同一个模块中。当需要调用其中
的某一个子程序时,只要引用该模块就可以了。由于积分计算子程序已经在前面详细给出了,这里
只给出模块程序单元的代码结构:
• MODULE INTEGRAL
• IMPLICIT NONE
• CONTAINS
• 除了上面介绍的基本高斯消去法外,还有一种高
斯消去法的改进方法:Gauss Jordan消去法。该
方法的基本思路与高斯消去法完全一样,但在消
元这一步做的更为彻底。Gauss Jordan消元后的
系数矩阵就是一个主对角矩阵,只有主对角线上
的元素才有值。做完这一步后,回代的过程更为
简单。
• Gauss Jordan方法涉及到矩阵的上下三角化操作。
通过对矩阵的上下三角化操作来使方程的系数矩
阵变为对角化矩阵,即矩阵中除了对角线上的元
素外,其余的元素都为零。
14.3 非线性函数求解
• 本节主要介绍非线性函数的基本数值解法。对于一元二次
或三次方程,已经有求根公式可供使用。但是对于更高次
数的方程而言,推导理论上的求根公式并不容易。反过
来,借助数值方法就可以很容易的得到这些高次非线性方
程的数值解。
• 假设非线性函数的一般形式为,则在特定区间[a,b]上求
解的几何意义就是找曲线与直线的交点。求解的基本思路
是一致的,就是通过迭代来不断逼近要找的交点。但是在
具体的实现方法上各种方法是有区别的。根据迭代方式细
节的不同,可以分为四种基本的迭代方法:最简单的一种
方法就是迭代法,也可以称为简单迭代;一种是牛顿迭代
法;一种是二分法;一种是弦截法。
14.3.1 迭代法
14.3.2 牛顿迭代法
• 同样是求一元非线性函数 F ( x) = 0 的根,牛顿迭代法
(简称牛顿法)则不需要对原始的公式进行变
形,可以直接使用到程序中,但需要求出原函数
的一阶倒数作为辅助函数。该方法的基本想法是
通过曲线上任意一点的切线来逼近函数的根,
14.3.3 二分法
• 二分法是一种相当巧妙和简单的非线性函数求解方法。这种方法
不仅应用在非线性函数的求解上,而且还在其他领域有着广泛的
用途,是一种通用的基础搜索算法。
• 二分法求解非线性函数的前提是要确定一个区间[a,b]。在这个区
间内,非线性函数应该有一个实根。在此基础上,二分法才能有
效地进行工作。如果在给定区间[a,b]内,非线性函数都没有实
根,那么求解就无从说起。
14.3.4 弦截法
• 弦截法(也称为割线法,英文原名为Secant)的
基本思想和二分法是一致的。只不过搜索区间不
再简单的划分成两半,而是由原区间两个端点的
连线与x轴的交点来决定。弦截法的思路可以简单
的用图来表示。
14.3.5 非线性函数求解子程序的应用
• 现在可以将上面介绍的4种不同的非线性函数求解
子程序统统打包进一个模块程序单元中。由于具
体的计算子程序代码在前面已经详细给出,因此
下面只给出模块程序单元的结构,其中的具体函
数可以自行添加。
14.4 常微分方程的基本解法
• 常微分方程是科学研究和工程实践经常遇到的问
题,也是数值分析中一个重要的应用领域。在实
际应用中,一些偏微分方程在最后也会通过某种
形式转化成常微分方程来进行求解,例如计算流
体力学中的一些算法。
• 本节主要介绍一种常用常微分方程的数直解法—
—龙格库塔法。假设要求解的常微分方程的一般
形式如下:
dy
= f ( y, x)
dx
14.5 插值与拟合
• 插值与拟合是数值分析中极为重要的一个方面,
它在科学研究和工程分析领域有着非常广泛的应
用。
14.5.1 拉格朗日多项式插值方法
• 插值的方法虽然多种多样,但是基本的思想都是一致的——即构
造各种类型的插值多项式。例如已知在个自变量上的函数值,则
惟一存在一个不超过次的多项式,它在这个自变量上具有与函数
相同的值。但是在实际应用中,构造一个次数较高的插值多项式
并不实用,而较多地采用低次插值多项式进行分段插值。
• 在众多的插值方法中,拉格朗日(Lagrange)多项式插值是实际
使用中最为常见的一种。假设有n个离散的数据点,则采用拉格朗
日多项式插值方法生成通过这n个离散数据的多项式函数如下:
( x − x 2 )( x − x3 ) L ( x − x n )
f ( x) = y1 +
( x1 − x 2 )( x1 − x3 ) L ( x1 − x n )
( x − x1 )( x − x3 ) L ( x − x n )
y2 +
( x 2 − x1 )( x 2 − x3 ) L ( x 2 − x n )
L+
( x − x1 )( x − x 2 ) L ( x − x n −1 )
yn
( x n − x1 )( x n − x 2 ) L ( x n − x n −1 )
14.5.2 牛顿前向插值方法
• 在插值方法中,牛顿前向插值可以解决拉格朗日
方法中遇到的效率问题。牛顿前向插值同样可以
得到一个通过n个离散数据的多项式,但在进行插
值时它会事先创建插值过程中会用到的差分表格
以加快插值的计算速度。牛顿前向插值方法中用
来生成插值多项式的公式如下:
k
⎛s⎞ n
f ( x) = ∑ ⎜⎜ ⎟⎟Δ f 0
n =0 ⎝ n ⎠
第15章 文件处理举例
• 文件处理和应用在有些大量数据需要进行处理的
问题中往往是不可避免的。灵活应用文件及其相
关操作能够使一些问题得到更好的处理。希望本
节的例子能够加深对文件操作的理解。
15.1 临时文件的应用
• 临时文件是一类特殊的文件,这种文件在使用完之后会被系统删
除。在某些应用背景下,这种文件能够帮助大量数据进行临时处
理。本节主要利用临时文件来存储需要的数据,并将保存在临时
文件中的数据最终保存在正式的数据文件中。
• 本节的例程首先利用随机函数生成一系列整数并将其保存在一个
无格式文件中,然后从这些数据中找出所有的非素数并从原文件
中删除。最后文件中的素数应该保持其在原文件中的排列顺序,
并且需要将文件中所有的素数都打印出来以供用户阅读。这一过
程可以通过下面的步骤来实现。
• 从原文件中读取一个数据到变量Prime中;
• 如果Prime是一个素数,就将该数存入一个临时的无名文件;
• 重复上述两个步骤,直到原文件中的数据全部被处理完;
• 将临时无名文件中数据放回原文件。
15.1.1 主程序
• 首先给出的是主程序,主程序模块中需要管理各
个功能子程序并打印必要的显示信息。
• (详细内容请参照本书)
15.1.2 随机数生成子程序
• 下面给出随机数生成子程序的代码。
• SUBROUTINE Random_File(Filename, Number)
• IMPLICIT NONE
• ! 变量定义
• CHARACTER(256) :: Filename
• INTEGER :: Number
• INTEGER :: I
• REAL :: Random
• ! 保存随机数文件
• OPEN(10, FILE=Filename, FORM='UNFORMATTED')
• DO I = 1, Number
• CALL RANDOM_NUMBER(Random)
• Number = INT(100*Random)
• WRITE(10) Number
• ENDDO
• CLOSE(10)
•
• END SUBROUTINE Random_File
15.1.3 素数挑选和文件重写子程序
• 下面介绍本节范例中的核心部分:素数挑选和文
件重写子程序。
• 在这段代码中,涉及到了较多的文件操作。这些
操作在文件一章中已经讲述过,下面再解释一下
用法以加深理解。子程序首先将临时文件与设备
号20相连,接下来将无格式的原始数据文件与设
备号10相连。接着,通过一个无循环变量的DO循
环配合READ语句中的END关键字来获取文件结束时
所读取的数据总数,并将其保存在整型变量
Number中。获得文件中的数据数量后,通过
REWIND语句将原始数据文件中的读取位置指针重
新射到文件的开头。
15.1.4 素数判断子程序
• 最后给出素数判断子程序,这是一个非常简单的程序。只需要按照素数的
定义照做就可以了,即只能被1和它自己整除的数就是素数。
• LOGICAL FUNCTION IsPrimeNumber(Number)
• IMPLICIT NONE
• ! 变量定义
• INTEGER :: Number
• INTEGER :: I
• ! 素数判断
• DO I = 2, Number - 1
• IF(0 == MOD(Number, I)) THEN
• IsPrimeNumber = .FALSE.
• RETURN
• ENDIF
• ENDDO
• IsPrimeNumber = .TRUE.
• RETURN
•
• END FUNCTION IsPrimeNumber
15.2 多个文件的处理
• 经常会遇到这一类问题:同一个实验会进行好几
次,从而获得好几批数据;接下来的工作就是将
这些数据文件合并组织成一个新的数据文件,以
便在作图软件(如Tecplot等)中绘出不同批次数
据的图像进行直观的比较。这就涉及到多个文件
的处理,本节将介绍简单的多个文件的合并问题。
• 对多个文件进行处理,关键是需要清楚的知道各
个文件是以何种形式进行保存的。也就是说,只
有对文件保存的内容有一个清晰的认识和了解,
才能正确地读出想要的数据并将其放置到新文件
中需要的位置。
第16章 搜索与排序
• 搜索与排序是计算机程序设计中经常会遇到的一
类实际问题。搜索一词,简单来说就是从一堆事
物中找出需要的东西。对计算机而言,这堆事物
就是计算机要处理的数据。而排序就是将一堆物
品按照一定的规则排列起来。一般来说,搜索的
目的往往就是为了排序。本节将介绍程序设计中
会用到的一些常见搜索和排序算法。
16.1 搜索算法
• 搜索是为了从一堆数据中找出符合要求的数据,
计算机能够按照人们的要求不知疲倦的去搜索符
合的数据。常见的搜索算法包括简单的顺序搜索、
二分搜索及散列搜索。
16.1.1 顺序搜索
• 顺序搜索是最简单的一种搜索算法,简单来说,
就是将数据依次拿出来查找看是否有符合要求的
结果。例如对于一组数据(n1,n2,n3,…,nn),想要
确定其中是否有数据m,则顺序搜索的做法如下:
• 先看第一个数据n1,如果n1=m则找到了所要找的
数据,否则继续搜索;
• 接着看第二个数据n2,如果n2=m则找到了所要找
的数据,否则继续搜索;
• ……;
• 来看最后一个数据nn,如果nn=m则找到了要找的
数据,否则打印没有搜索到合适的数据。
16.1.2 二分搜索
• 顺序搜索思路简单,但效率不高。二分法是一种常见的高
效、通用的搜索算法,但是必须配合排序好的数据才能正
常的使用。如果将要搜索的数据使用数组进行保存并按照
从小到大的顺序进行排列,则二分法的基本步骤如下:
• 取数组的中间值M与要搜索的数据G对比,如果M=G则完成了
搜索。如果M>G,在数据进行了排序的情况下,则要搜索的
数据G一定在数组的下半部分;如果M<G,则要搜索的数据G
一定在数组的上半部分。
• 根据数据G在数组中的位置来重新分组,重复执行前一步继
续进行寻找。如果重新分组已经执行到不能再继续下去,
此时还没有找到要搜索的数据G,则表示在搜索的数据中不
存在要寻找的数据。
16.1.3 散列搜索
• 散列搜索是一种相当高效的搜索算法,在使用恰
当的情况下,几乎只需要做一次对比操作就能判
断某一个数据是否在一组目标数据中。
• 散列搜索的主要精力放在了如何将数据放置在内
存中。当数据在内存中排列好之后,搜索数据只
需要计算它可能出现在内存中的具体位置并与该
位置的实际数据进行对比即可。散列搜索没有现
成的计算公式可供使用,基本的思想就是一个内
存位置对应一个具体数据,并且两者之间存在某
种联系。
16.2 排序算法
• 排序的目的就是为了将数据以某种特定的形式排
列起来,以方便某种应用。排序的算法较多,这
里只介绍几种较为常见且有效的算法。
16.2.1 冒泡法
• 冒泡法是最简单的一种排序方法。尽管该方法的思想较为
简单,但实际的排序效率较低。如果有N个数据需要进行排
序,则需要对数据作N-1次扫描工作。
• 冒泡法的基本步骤如下:
• 从第一个数字开始,依次比较相邻两个数据的大小。如果
第一个数比第二个数大,则交换两者的位置;
• 重复上述工作,直到倒数第二个数据;
• 从第一步开始,重新进行新的扫描。
• 在上面的算法中,由于每次扫描都将剩余数据中的最大值
放到了最后边,因此每次扫描的数据会越来越少,最后形
成的新数据排列是按从小到大的方式进行的。这就好像水
里的气泡,越轻的越在上面。当然,上面的排序结果也可
以反过来,从大到小进行排列也是可以的。
16.2.2 选择排序法
• 选择排序法在某种形式上与冒泡法较为相似,冒泡法如果找到两
个数中较大的一个就将其放到第二个数字的位置;而选择排序法
则会将一组数据中的第一个数字与其后所有的数据进行比较,如
果这个数字较小则将其放置于这组数据的第一个位置上。
• 对于一个有N个数字的一组数组,选择排序法的基本步骤如下:
• 找出N个数据中最大或最小的一个,并将其与这组数据中的第一个
数字交换位置;
• 找出剩余N-1个数据中最大或最小的一个,并将其与这组数据中的
第二个数字交换位置;
• 找出剩余N-2个数据中最大或最小的一个,并将其与这组数据中的
第三个数字交换位置;
• ……;
• 找出最后两个数据中最大或最小的一个,并将其与这组数据中倒
数第二个数字交换位置。
16.2.3 快速排序法
• 快速排序法名如其人,一般来说,快速排序法是目前最快的排序方法。对
于一个有N个数据的序列而言,快速排序的基本步骤如下:
• 以序列中的第一个数字作为键值K,并令L=2、R=N;
• 以K为基准,将小于K的数字往前移,大于K的数字往后移。移动完成后,
自然会发现K在原始序列中的大小排名,这时将K放置于正确的位置上;
• 如果K的大小排名为M,则移动后的序列会分成大于K和小于K的两组。此
时,再将这两组数字按上述方法继续进行排序。
• 在快速排序法的第二步中,可以进一步详细的进行说明:
• 从序列中的第二个数字开始,依次拿下一个数字与键值K比较,直到找到
大于等于K的数值L为止。此时,将L重新设置为数值L在序列中的位置;
• 从序列中的倒数第二个数字开始,依次拿钱一个数字与键值K比较,直到
找到小于等于K的数值M为止。此时,将N重新设置为数值M在序列中的位
置;
• 如果L小于R,则把序列中L和R这两个位置的数据交换,再回到上一步继续
执行;如果L大于R,则将序列中的第一个数字与第R个数字互相交换。
第17章 Fortran与其他语言
• 由于纯粹的Fortran语言在图形操作能力上的缺陷,为了使
数值计算程序能够通过图形用户界面(GUI)与用户进行即
时的交互,通常会采用多种语言混合编程的方式来处理。
• 不同的程序设计语言在处理不同的变量类型特别是子程序
的参数传递处理方式上有很大的不同,只要在混合编程时
注意变量类型和参数传递的处理方式,就能够充分利用不
同程序设计语言的优势。Fortran语言在数值计算方面具有
突出的优势,能够利用Fortran语言进行混合编程以提高数
据处理方式的其他程序设计语言。包括:C/C++、Delphi、
MASM、Visual Basic等。其中较为常见的是与MATLAB、
Visual Basic语言进行混合编程,提高这两种语言工具在
进行大批量数据处理时的效率。
• 本节主要介绍Fortran语言同MATLAB、Visual Basic语言的
混合编程方法以及编程过程中需要了解的一些注意事项。
17.1 Fortran与MATLAB
• MATLAB的名称来源于Matrix Laboratory(即矩阵实验室),它是
MathWorks公司开发的一款优秀科学计算软件。MATLAB将优秀的数
值计算、符号运算和图形环境结合起来,广泛的应用于科学计算、
控制系统、信息处理等领域的分析、仿真和设计工作之中。自
1984年第一代MATLAB软件问世以来,MATLAB就一直在不断的发展
和丰富,功能和应用领域都有了很大的扩展。目前最新的MATLAB
版本已经发展到了R2007b(7.5.0)版。
• Fortran语言的一大弱点就是图形操作能力较差,通常需要借助别
的程序设计语言或是利用编译器扩展来实现图形图像的操作。尽
管MATLAB在工程和科研领域应用较多,而且图形处理能力相当突
出,但是在一些规模较大的实际应用上面缺乏足够的效率。如果
配合Fortran语言在数值计算方面的优势,加上MATLAB在图形显示
方面的长处,则可以在Fortran程序中调用MATLAB的功能来实现计
算结果的实时显示。
17.1.1 系统配置
• 在MATLAB中通过MEX文件来使用Fortran,需要对系统配置
有一个必要的了解:系统需要安装MATLAB应用程序的接口
组件及相应的工具;使用的Fortran编译器必须能够支持32
位的动态链接库(即能够编译生成Windows系统下的DLL文
件或Linux系统下的O文件)。具备了上述基本软件条件
后,还需要对MATLAB进行一些必要的配置。首先,在
MATLAB命令行窗口中输入如下命令来对MEX文件的应用进行
配置:
• mex -setup
• 接下来MATLAB会询问用户是否需要它自动定位已经安装的
编译器:
• Please choose your compiler for building external
interface (MEX) files:
•
• Would you like mex to locate installed compilers
[y]/n?
17.1.2 MEX文件的编写
• 配置成功MATLAB的外部文件接口之后,就可以使
用Fortran语言编写MEX文件了。Fortran语言版本
的MEX文件源程序主要由两大部分组成——入口子
程序和计算子程序。这两种子程序在外部文件接
口程序中分别用于完成不同的任务,下面分别进
行说明。
• 1.入口程序和计算子程序
• 2.MEX文件的实例一
• 3.MEX文件的运行
• 4.MEX文件的实例二
• 5.MEX文件的实例三
17.1.3 MEX文件调用MATAB
• Fortran语言的MEX文件除了可以调用Fortran源代
码外,还可以通过MATLAB提供的API函数
mexCallMATLAB来完成对MATLAB内部函数、运算符、
M文件的调用。
17.1.4 Fortran调用MATLAB
• 除了在MATLAB中通过MEX文件来调用Fortran源代码外,还可以通过对编译
器进行设置来实现在Fortran中调用MATLAB引擎。这种应用通常用于
Fortran语言作为计算前端进行数据的处理,而使用MATLAB作为图形处理
或是其他处理功能后端的领域。这一类型的文件接口又被称为MAT文件。
• 1.MATLAB引擎的配置
• 下面就来说明在系统中如何配置使用MATLAB引擎。首先也需要在MATLAB命
令常口中使用如下命令来配置相应的环境:
• mex –setup
• 2.配置Compaq Visula Fortran编译环境
• 3.应用实例
• 4.实例的运行
• 在Compaq Visual Fortran中编译通过上述程序代码或是在MATLAB命令行
窗口中输入如下命令进行编译:
• mex –f %MATLAB%\bin\win32\mexopts\cvf66engmatopts.bat
%PATH%fengdemo.f
17.2 Fortran与VB
• 在视窗程序开发工具中,Microsoft公司推出的Visual
Basic在易学性、易用性和开发速度方面的优势较为突出。
配合使用Fortran编写计算内核,使用Visual Basic编写用
户界面是工程开发中比较好的选择。而且使用Visual
Basic同样可以实现漂亮的图形显示结果,当然这需要对
Visual Basic的图形编程较为熟悉。
• 微软公司开发的Visual Basic是相当流行的视窗应用程序
开发工具。这是一个完全基于事件编程模式的可视化程序
设计语言,在功能上已经远远超出了它的原型——BASIC语
言。目前该软件开发工具已经发展了好几版,最新的版本
是Visual Basic 2008,该版本中的Express版是一个供个
人用户免费使用版本,可以在Microsoft公司的主网上免费
下载。
17.2.1 供VB调用的Fortran源代码
• 下面先给出将要在Visual Basic中应用的Fortran源代码,
其中的各个子函数将会分别用于处理变量、数组、字符串
等常用数据类型。
• (详细内容请参照本书)
• 上面的代码中总共编写了3类函数,涉及了程序设计中经常
会遇到的变量、数组和字符串等基本数据类型的处理。
Fortran语言在编译后会将过程名默认转化为大写的形式,
如果不想采用这种形式,则可以在源代码中加入编译设置
选项。
• !DEC$ ATTRIBUTES ALIAS : “Calculate_Area” ::
CALCULATE_AREA
• 这段编译器设置语句的目的就是通过ALIAS选项将过程名
CALCULATE_AREA改写成Calculate_Area。
17.2.2 VB中的管理代码
• 在Visual Basic中调用Fortran语言编写的过程
时,只需要在声明过程时写清楚要调用的过程放
在哪个DLL文件中,并且将参数的类型正确编写出
来就可以了。
• 下面给出使用Visual Basic语言编写的主程序,
该程序用于响应用户在图形界面上的输入,例如
单击按钮、输入数据等,并负责调用正确的计算
函数来进行相应的数值处理。
17.2.3 程序的运行说明
• 程序的初始运行界面如图所示: