FORTRAN语言(Formula Translation)自学笔记

FORTRAN语言是Formula Translation的缩写,意为“公式翻译”,工程界最常用的编程语言,它在科学计算中(如航空航天、地质勘探、天气预报和建筑工程等领域)发挥着极其重要的作用。
————摘自百度百科。
现将自己近期对Fortran语言的学习总结如下,主要以遇到的一些Fortran程序为例,便于后续查询与深入学习。
参考资料:
https://wenku.baidu.com/view/e2966ba0760bf78a6529647d27284b73f342365c.html?from=search

1、关系运算

+ - * / ****表示乘方)
GT:大于; GE:大于或等于; LT:小于; LE:小于或等于; EQ:等于;
NE:不等于; AND:逻辑与; OR 逻辑或; NOT:逻辑非; EQV:逻辑等; NEQV:逻辑不等

优先级次序由高到低为:
()***/+-GTGELTLEEQNENOTANDOREQVNEQV

2、变量类型

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语句应放在类型说明语句之前。

3、数组与赋值

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。

4、选择判断语句

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

5、循环语句

1. GO TO语句

标号程序行
程序块
GO TO 标号

2.无条件循环DO语句
这类循环结构的循环体中应有判断语句,通过EXITGOTO语句结束循环。

DO
<循环体>
END DO

3. 计数型DO语句
<循环体>中可包含EXITCYCLE语句。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

6、子程序与函数

主要参考博客:
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语句来实现数据共享。

7、文件

在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_variableIOMSG = char_variable子句,检查是否发生读取错误,并返回相应信息,而不引起程序异常中断。

  1. IOSTAT = int_variable
    当READ语句成功执行时,int_variable的值为0;
    当READ语句执行失败,如读取的变量不符合所定义类型时,int_variable的值为与系统错误信息相对应的正数;
    当READ语句执行至数据文件的尾部时,语句会执行失败,int_variable的值为负数。

  2. 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.

你可能感兴趣的:(编程语言,Fortran)