FORTRAN语言是Formula Translation的缩写,意为“公式翻译”,工程界最常用的编程语言,它在科学计算中(如航空航天、地质勘探、天气预报和建筑工程等领域)发挥着极其重要的作用。
————摘自百度百科。
现将自己近期对Fortran语言的学习总结如下,主要以遇到的一些Fortran程序为例,便于后续查询与深入学习。
参考资料:
https://wenku.baidu.com/view/e2966ba0760bf78a6529647d27284b73f342365c.html?from=search
+
-
*
/
**
(**
表示乘方)
GT
:大于; GE
:大于或等于; LT
:小于; LE
:小于或等于; EQ
:等于;
NE
:不等于; AND
:逻辑与; OR
逻辑或; NOT
:逻辑非; EQV
:逻辑等; NEQV
:逻辑不等
优先级次序由高到低为:
()
→ **
→ *
或/
→ +
或-
→ GT
或GE
或LT
或LE
或EQ
或NE
→ NOT
→ AND
→ OR
→ EQV
或NEQV
1. 隐含约定:I-N规则
凡是以字母I,J,K,L,M,N六个字母开头的,即为整型变量,其它为实型变量。
2. 类型说明语句
规定变量类型,可改变I-N规则。
变量类型 | 变量类型说明 |
---|---|
INTEGER | 整型 |
REAL | 实型 |
DOUBLE PRECISION | 双精度实型 |
COMPLEX | 复型 |
LOGICAL | 逻辑型,逻辑常量有“T”和“F”,“T”表示“.TRUE.”,“F”表示“.FALSE.” |
CHARACTER*N | 字符型,N为字符串长度,可以在变量名称后重新指定长度,如CHARACTER*8 STR1,STR2*10 |
3. IMPLICIT语句
将某一字母开头的全部变量指定为所需类型,例如IMPLICIT REAL (I,J)
三种定义的优先级别由低到高顺序为:
I-N规则 → IMPLICIT语句 → 类型说明语句
因此在程序中IMPLICIT语句应放在类型说明语句之前。
1. DIMENSION说明数组:
使用I-N规则时用DIMENSION说明数组,也可在定义变量类型同时说明数组,说明格式为:数组名(下标下界,下标上界),也可省略下标下界,此时默认为1。
例如:DIMENSION ND(80:99), W(3,2), A(0:2,0:1,0:3)
, REAL IA(10), ND(80:99)
注:DIMENSION语句声明的数组,其类型可按I-N规则确定,也可以使用显式声明其类型,但显式声明语句应在DIMENSION语句之前,例如:
Real (8) NUM, SUM
DIMENSION NUM (2,3), SUM(2:8)
2. 输入输出操作:
输入:
read(,格式语句标号) <输入表>
read(,<‘|”> <格式说明表> <‘|”>)<输入表>
read(45,'(i4,f12.5,1p6e16.7)')
C read后边括号内跟有两个字段,第一个是个数字,代表一个设备号,跟相应的数据文件关联;
C 第二个是一对单引号引住的一对圆括弧里的内容:i4表示输出一个长度为四位的整数,f12.5表示输出一个12位长度的实数,
C 其中五位是小数点后的位数,1p表示把数字加一输出,6e16.7表示输出6个格式为e16.7的实数,其中e16.7表示以科学计数法
C 输出一个16位长度的实数,7表示小数部分7位。
输出:
print(,格式语句标号) <输出表>
print(,<‘|”> <格式说明表> <‘|”>) <输出表>
write(,格式语句标号) <输出表>
write(,<‘|”> <格式说明表> <‘|”>) <输出表>
WRITE(*,10) ('I=',I,'A=',A(I),I=1,10,2)
10 FORMAT(1X,5(A2,I2,1X,A2,I4))
C 输出 I = I(变量I取值1,3,5,7,9),A = A(I)(输出数组A(1),A(3),A(5),A(7),A(9)的值)
FORMAT格式:
<语句标号> Format <格式说明表>
格式说明表:(格式编辑符 [ {,格式编辑符} ])
10 format(1x,3I5)
10 是标号行,一般为100,200,是一种习惯用法,用1,2也可以,
format是格式说明,里面的内容是:1x表示输出一个空格字符;3I5代表输出3个整数,每个整数占5个字符,即字符宽度为5。
3. 使用DATA语句进行赋值
变量表中可出现变量名,数组名,数组元素名,隐含DO循环,但不许出现任何形式的表达式。例如:
为变量赋值:
DATA A,B,C/-1.0,-1.0,-1.0/
DATA A/-1.0/,B/-1.0/,C/-1.0/
DATA A,B,C/3*-1.0/
C 注:三种写法,都是分别为A,B,C赋值为-1.0。
为数组赋值:
CHARACTER*6 CHN(10)
DATA CHN/10 * ' '/
C 注:定义6个字节长度的字符串CHN,并重新指定长度为10个字节,赋初值为空。
使用隐含DO循环赋值:
INTEGER NUM(1000)
DATA (NUM(I),I=1,500)/500*0/,(NUM(I),I=501,1000)/500*1/
C 注:定义1000长度的整型数组NUM,并对前500个元素赋值为0,后500个元素赋值为1。
1. 简单IF语句:
IF (逻辑表达式)
<程序语句>
2. 块IF语句:
IF (逻辑表达式) THEN
<程序语句1>
ELSE
<程序语句1>
END IF
3. 多分支IF语句
IF (逻辑表达式1) THEN
<程序语句1>
ELSE IF (逻辑表达式2) THEN
<程序语句2>
ELSE IF (逻辑表达式3) THEN
<程序语句3>
......
ELSE IF (逻辑表达式N) THEN
<程序语句N>
ELSE
<程序语句N+1>
END IF
4. CASE选择结构
SELECT CASE (测试表达式)
CASE (目标值1)
<程序语句1>
......
CASE (目标值N)
<程序语句N>
CASE DEFAULT
<程序语句N+1>
END SELECT
1. GO TO语句
标号程序行
程序块
GO TO 标号
2.无条件循环DO语句
这类循环结构的循环体中应有判断语句,通过EXIT
或GOTO
语句结束循环。
DO
<循环体>
END DO
3. 计数型DO语句
<循环体>中可包含EXIT
,CYCLE
语句。EXIT
终止循环,并转向END DO
后的第一条语句;CYCLE
语句结束当前循环并开始下一次循环。
DO 标号 <记数变量=起始值,终止值,步距>
C 例如:DO 标号 N=1,100,1 缺省步长默认为1
<循环体>
标号 CONTINUE
DO <记数变量=起始值,终止值,步距>
<循环体>
END DO
4. 条件型DO循环
DO 标号 WHILE (<循环条件>)
<循环体>
标号 CONTINUE
DO WHILE (<循环条件>)
<循环体>
END DO
DO 标号 UNTIL (<结束循环条件>)
<循环体>
标号 CONTINUE
主要参考博客:
https://www.cnblogs.com/baowee/p/9556828.html
1. 子程序 SUBROUTINE
子程序SUBROUTINE在没有经过调用之前不会被执行,它独立拥有属于自己的变量申明。主程序和子程序,以及不同的子程序之间变量名可以相同,互不干扰。在子程序的调用中会涉及到参数传递,使用传址调用,即实参和形参公用一块内存,两者改变其中之一,另一者也会发生变化。
子函数调用形式如下:
PROGRAM MAIN
......
......
CALL SUB_1(...)
......
END PROGRAM MAIN
SUBROUTINE SUB_1(...)
......
END SUBROUTINE SUB_1
2. 自定义函数 FUNCTION
自定义函数和子程序有两点不同:
1、调用自定义函数前要声明;
2、自定义函数调用后会返回一个数值。
举个栗子,用牛顿法来解方程:
PROGRAM MAIN
IMPLICIT REAL*8 (A-Z)
EXTERNAL F,G
INTEGER::MAX
INTEGER::I,K=1
REAL*8::X0,X1,X2,DX,TOL,F,G
OPEN (UNIT=11,FILE='fin.txt')
OPEN (UNIT=12,FILE='fout.txt')
READ (11,*) X0,MAX,TOL
DO
X1=X0-F(X0) / G(X0)
DX=ABS(X1-X0)
IF(DX<=TOL) EXIT
K=K+1
IF(K>=MAX) EXIT
X0=X1
END DO
WRITE(12,*) X0
END PROGRAM MAIN
FUNCTION F(x)
IMPLICIT NONE
REAL*8::F,X
F=X**3+2*X**2+10*X-20
END FUNCTION
FUNCTION G(x)
IMPLICIT NONE
REAL*8::G,X !,DX
G=3*X**2+4*X+10
END FUNCTION
注意:不要随意改变传入的参数数值,最好另开辟一个内存来存储要用的应变量。
3. 全局变量 COMMON
不同子程序之间除了可以通过传递参数的方法来共享内存,还可以通过全局变量让各函数中声明的变量使用相同内存位置。
举个栗子:
PROGRAM MAIN
IMPLICIT NONE
INTEGER::A,B
COMMON A,B
A=1
B=2
CALL SUB()
END PROGRAM
SUBROUTINE SUB()
IMPLICIT NONE
INTEGER::NUM1,NUM2
COMMON NUM1,NUM2
WRITE (*,*) NUM1,NUM2
RETURN
END SUBROUTINE SUB
由于全局变量使用的“地址对应”的方法在程序中共享数据,如果在主程序中声明了6个全域变量,而子程序中只需使用某几个,但是不得不把所有变量都写出来。
一个好的解决办法是将全域变量分区,举个栗子:
PROGRAM MAIN
IMPLICIT NONE
INTEGER::A,B,C,D
COMMON /GROUP1/ A,B
COMMON /GROUP2/ C,D
A=1
B=2
C=3
D=4
CALL SUB_1()
CALL SUB_2()
END PROGRAM
SUBROUTINE SUB_1()
IMPLICIT NONE
INTEGER::NUM1,NUM2
COMMON/GROUP1/ NUM1,NUM2
WRITE (*,*) NUM1,NUM2
RETURN
END SUBROUTINE SUB_1
SUBROUTINE SUB_2()
IMPLICIT NONE
INTEGER::NUM1,NUM2
COMMON/GROUP2/ NUM1,NUM2
WRITE (*,*) NUM1,NUM2
RETURN
END SUBROUTINE SUB_2
4. BLOCK DATA 模块
如果想给全局变量COMMON设置初值,不能直接在子程序或者主程序中使用DATA
来设置,要在BLOCK DATA程序模块中使用DATA
命令。BLOCK DATA会在主程序执行前生效,不过它的功能只在于设置全局变量COMMON的初值,不能有其他执行命令出现。具体语法如下:
BLOCK DATA NAME !NAME可以省略
IMPLICIT NONE !最好不要省略
INTEGER ... !声明变量
REAL ...
COMMON ... !把变量放在公共空间
COMMON/GROUP1/... !公共空间分区
DATA VAR1,VAR2,...,VARN/VALUE1,VALUE2,...,VALUEN/ !DATA语句赋初值
.........
.........
END BLOCK DATA NAME !可以只写END或END BLOCK DATA
注意,全局变量COMMON不能声明成常量,所以BLOCK DATA中不能出现
PARAMETER
。此外,COMMON语法在数据共享式会出现很多限制和不足,建议不要使用而采用MODULE
语句来实现数据共享。
在Fortran程序中使用文件,需要输入/输出单元(I/O单元)。
READ(* , *)
和WRITE(* , *)
语句中的第一个*
号与 I/O单元 相对应。当*
号被 I/O单元号 代替时,程序对应的读和写将指向该单元指定的设备,替代预定标准的输入或输出设备。
注:I/O单元号必须为整数类型; I/O单元号为 5 预定义为程序的标准输入设备(即键盘),为 6;
预定义为程序的标准输出设备(即显示器),此时用*
号表示。
1) OPEN
语句:OPEN( open_list )
,其中open_list是一组子句,分别指定I/O单元号、文件名和关于如何存取文件的信息。
特性:子句中各项参数的顺序可以任意安排;子句用逗号,
隔开。
子句中最重要的有以下六项:
UNIT = int_expression
,指明与文件关联的I/O单元号,int_expression为非负的整数值;
FILE = char_expression
,指定要打开的文件名,char_expression为文件名称的字符值;
STATUS = char_expression
,指定要打开文件的状态,char_expression的取值为下列中的一个:OLD,NEW,REPLACE,SCRATCH 或 UNKNOW;
ACTION = char_expression
,指定文件以读或写的方式打开,char_expression的取值为下列中的一个:READ,WRITE 或 READWRITE,如未指定则是以读写方式打开;
IOSTAT = int_variable
,指定一个整数变量名,把打开操作的状态返回到这个int_variable变量中。如果OPEN语句成功执行,则整数变量int_variable的值为0,其它数值则是未成功执行(不同处理器,相应系统错误信息会不同);
IOMSG = char_variable
,指定一个字符变量名。如果OPEN语句成功执行,字符变量char_variable的内容不会变化,如没成功执行,一个描述错误的信息将返回到该字符串中。
2) CLOSE
语句:CLOSE( close_list )
,close_list必须包含一个指定I/O单元号的子句,还可以指定其它选项。
特性:文件关闭后,相关联的I/O单元被释放,可以用OPEN将该单元号重新分配给其它任意文件;如果程序中没有包含对给定文件的CLOSE语句,这个文件将在程序结束时被自动关闭。
3) READ
在使用READ语句时,可以添加IOSTAT = int_variable
和 IOMSG = char_variable
子句,检查是否发生读取错误,并返回相应信息,而不引起程序异常中断。
IOSTAT = int_variable
,
当READ语句成功执行时,int_variable的值为0;
当READ语句执行失败,如读取的变量不符合所定义类型时,int_variable的值为与系统错误信息相对应的正数;
当READ语句执行至数据文件的尾部时,语句会执行失败,int_variable的值为负数。
IOMSG = char_variable
,
当IOSTAT变量值为非0时,IOMSG会已语句形式返回字符串来解释发生的错误。
注意:当从文件中读取数据时,READ语句应总是包含IOSTAT子句,使得程序可以检查所读取的数据是否符合要求(如类型判断),以及避免读取文件结尾之外行信息时的异常中断。
e.g.举个栗子:现有一个数据文件,有多行,每一行中有1个数据,数据类型是实数型,通过输入文件名来获取其中的数据,输出内容为实数数据及其对应行号,并交代总行数(即数据个数)。同时,需要能够返回数据读取失败时的信息提醒,以及文件打开错误时的信息提醒。代码如下:
PROGRAM read_file
IMPLICIT NONE
CHARACTER(len = 20)::filename ! 文件名称
CHARACTER(len = 80)::msg ! 打开文件错误时的异常信息
INTEGER::nvals = 0 ! 读取数据的行数
INTEGER::status1 , status2 ! 打开文件和读取数据的状态信息
REAL::value ! 需读取文件中的数据
WRITE(*,*) 'Please enter input file name' ! 读取需要打开的文件
READ(*,*) filename
WRITE(*,1000) filename
1000 FORMAT('The input file name is: ' , A)
OPEN(UNIT = 3 , FILE = filename , STATUS = 'OLD' , ACTION = 'READ' , IOSTAT = status1 , IOMSG = msg) ! 打开文件
openif:IF(status1 == 0 ) THEN ! 文件打开成功
readloop:DO
READ(3 , * , IOSTAT = status2) value
IF(status2 /= 0) EXIT ! 如果数据读取失败,跳出DO循环
nvals = nvals + 1
WRITE(*,1010) nvals,value
1010 FORMAT('Line ',I6,' :Value = ',F10.4)
END DO readloop
readif:IF(status2 > 0) THEN ! 发生了数据读取错误
WRITE(*,1020) nvals+1
1020 FORMAT('An error occurred reading line ',I6) ! 交代在哪一行读取数据失败
ELSE readif ! 到达数据结尾
WRITE(*,1030) nvals
1030 FORMAT('End of file reached.There were ' , I6 , ' values in the file.') ! 交代已经到达文件结尾
END IF readif
ELSE openif ! 文件打开失败
WRITE(*,1040) status1
1040 FORMAT('Error opening file : IOSTAT = ',I6)
WRITE(*,1050) TRIM(msg) ! 返回错误信息
1050 FORMAT(A)
END IF openif
CLOSE(UNIT = 3) ! 关闭文件
STOP
END PROGRAM read_file
由于目前一些语法暂时还没有用到,例如派生数据类型与结构体、指针与动态链表、存储关联、绘图等,这里不做过多记录。
后续会持续补充遇到的问题 = w =
开始学跳舞的乔木同学 2020.04.26.