Fortran初探

参考

1、[Fortran权威指南/(英)奇弗斯(Chivers,I.),(英)斯莱索尔姆(Sleightholme,J.)著;陈宝国等译.–北京:人民邮电出版社,2009.10]

2、https://www.yiibai.com/fortran/fortran_basic_input_output.html

3、[Fortran 程序设计:第四版/(美)史蒂芬·查普曼著;王志强等译.-北京:中国电力出版社,2018.10(2022.8重印)]

基础内容

! 作为注释标记

Fortran规则:

  • 自由源格式
  • 不区分大小写
  • 存在语句顺序
    • PROGRAM
    • 类型声明语句
    • 处理与I/O语句
    • END PROGRAM
  • !用于引入注释
  • 变量名称长度最多31字符,包括_
  • 每行最多132个字符
  • 最多可以允许39个连续行
  • 如果不使用IMPLICIT NONE语句,则没有显式声明的变量被看成默认的类型。即:首个字符是A-H或O-Z,则默认为REAL型。若为I-N,则默认INTEGER型。

1、输出你的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
PROGRAM ch0701      !程序命名
!
! This program reads in and prints out a name
!
IMPLICIT NONE ! 表示使用的每个数据项的类型必须是显式的
CHARACTER*20 :: First_Name ! 类型声明
!
PRINT *,' Type in your first name.' ! 输出语句
PRINT *,' up to 20 characters'
READ *,First_Name !读取键盘输入的字符
PRINT *,First_Name
!
END PROGRAM ch0701 ! 终止程序

2、计算平均值

遵循的原则:声明所有变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

PROGRAM ch0702 !程序命名
!
! This program reads in three numbers and sums
! and averages them
!

IMPLICIT NONE ! 表示使用的每个数据项的类型必须是显式的
REAL :: N1,N2,N3,Average = 0.0,Total = 0.0
INTEGER :: N = 3
PRINT *,' Type in three numbers.'
PRINT *,' Separated by spaces or commas'
READ *,N1,N2,N3
Total= N1 + N2 + N3 ! 三个变量值相加
AVERAGE=Total/N
PRINT *,'Average of the numbers is ',Average
END PROGRAM ch0702

运算

  • 加:+
  • 减:-
  • 乘:*
  • 除:/
  • 乘方:**

注意:整型运算最终结果也是整型!
注意:实型运算最终结果需要考虑精度!尤其是结果不是有限小数的情况

字符常数和变量

  • 显式变量:
    需要定义
    1
    INTEGER :: VAR1[,VAR2,VAR3]
  • REAL 定义实型
  • INTEGER 定义整型
  • CHARACTER 定义字符型
    字符型可以定义长度,缺省时为1。
    1
    CHARACTER(len=<len>) ::

注:实际上,‘::’是可选的。其有无不影响程序运行。存在的目的是为了兼容早期的Fortran。不过,::看起来有种独特的美感(某本教程的作者说它很可爱),所以还是加上。

  • 默认式变量:i,j,k,l,m,n为开头的变量名为INTEGER整型;其他为REAL实型。

常数定义:

1
2
3
type,PARAMETER :: name=value [,name1=value1 ...]
!例如
real,parameter :: pi=3.1415926535

注:大写小写都可以。大写看起来好看,小写看起来更舒服(对于像我这样英语不好的人)。

运算顺序

  • 圆括号优先。
  • 从右到左做指数运算
  • 从左到右做乘法和除法运算
  • 从左到右做加法和减法运算

运算法则与C或Python相同,但是需要注意:

  • 求幂运算:从右往左的顺序
  • 加减法:不需要注意运算顺序
  • 乘法除法:非整型运算不一定按照从左到右的顺序,需要加括号保证顺序
    注意:使用时加括号时最好的方式。

个人更喜欢多加括号。有些教程说熟练掌握,记住顺序。但是对于非计算机专业使用而言,加上括号的可读性更高,不需要纠结自己是否用对了。例如++i这种运算,个人认为十分“反人类”。

字符格式转换函数

  • INT(x) 实数-整数 截尾取整
  • NINT(x) 四舍五入取整
  • CEILING(x) 大于或等于x的最小整数
  • FLOOR(x) 小于或等于x的最大整数
  • REAL(X) 整数转化实数
    前面即行x表示实数,X表示整数

内置函数

内置函数为最通用的函数。

函数名 参数类型 结果类型 说明
SQRT(X) real real X的平方根
ABS(X) real/integer * 绝对值
ACHAR(I)
SIN(X) 弧度制
SIND(X) 角度制
COS(X)
COSD(X)
TAN(X)
TAND(X)
EXP(X)
LOG(X)
LOGIO(X)
IACHAR(C)
MOD(A,B)
MAX(A,B)
MIN(A,B)
ASIN(X)
ASIND(X)
ACOS(X)
ACOSD(X)
ATAN(X)
ATAND(X)
ATAN2(Y/X)
ATAN2D(Y,X)

注:*指输出类型与输入类型相同。

表控输入与输出

表控输入:变量列表中的变量类型决定输入数据需要的格式。

READ(*,*)input_list
WRITE(*,*)output_list
PRINT *. output_list

# (*,*)表示任意长度,使用``,``隔开

WRITE与PRINT等价。

关于PRINT:

PRINT fmt, i/o_list

fmt是读取格式说明,示例如下:

PRINT 100, x , y
100 FORMAT (2F10.2)
string = '(2F10.2)'
PRINT string,x,y

IMPLICIT NONE语句

加入

IMPLICIT NONE

所有变量需要显式定义,因此可以避免计算机因为变量名出错而输出错误的答案。

debug

养成良好编程习惯:

  • 使用IMPLICIT NONE 语句。
  • 返回所有输入值以及计量单位。(这也是为什么在mineos中看到运行时返回输入值的原因)
  • 初始化所有变量:显式输入,不要忘记初始化变量。
  • 圆括号使赋值语句更清晰。

截断

注意截断。整型截断为舍入

PARAMETER 语句

REAL , PARAMETER :: value=12

类似于C语言中的固定变量,即在类型声明语句之前将某个变量名与变量关联,从而不可更改该变量名的值。
类似于#define

精度问题

变量状态

  • 已确定的状态 defined:已定义值
  • 未确定的状态 undefined:未定义值

思考题

运行示例4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PROGRAM ch0804
implicit none
real :: light_minute, distance, elapse
integer :: minute, second
real, parameter :: light_year=9.46*10**12

light_minute = light_year/(365.25 * 24.0 * 60.0)
distance = 150.0 * 10 ** 6
elapse = distance / light_minute
minute = elapse
second = (elapse - minute) * 60
print *,' light takes ' , minute, ' Minutes'
print *,' ' , second, ' seconds'
print *,' To reach the earth from the sum'
END PROGRAM ch0804

在运行ch0804出现错误:

Error: Result of exponentiation at (1) exceeds the range of INTEGER(4)

表示结果超出整型范围。

疑问:??? 未解决

计算摆的周期

1
2
3
4
5
6
7
8
9
PROGRAM period_of_pendulum
implicit none
real :: length,T
real , parameter :: pi = 3.1415926535
print *,'Please input the length of pendulum:'
read *, length
T = 2 * Pi * (length / 9.81) ** .5
print *,'The period is ', T, 'seconds.'
end program period_of_pendulum
1
2
3
4
 Please input the length of pendulum:
1
The period is 2.00606656 seconds.

在计算器中的输出值为:2.006066681。

进制转换

1
2
3
4
5
6
7
8
9
10
11
12
program base_conversion
real :: x1=1.0
real :: x2=0.1
real :: x3=0.01
real :: x4=0.001
real :: x5=0.0001
print *,' ',x1
print *,' ',x2
print *,' ',x3
print *,' ',x4
print *,' ',x5
end program base_conversion

输出结果如下:

1
2
3
4
5
 1.00000000    
0.100000001
9.99999978E-03
1.00000005E-03
9.99999975E-05

1.0保持不变,但是其他值均发生了改变。

简单减法

1
2
3
4
5
6
7
8
9
program subtract
real :: a=1.0002
real :: b=1.0001
real :: c
c=a-b
print *,a
print *,b
print *,c
end program subtract

输出结果:

1
2
3
1.00020003    
1.00010002
1.00016594E-04

理论结果为0.0001,但是结果输出不同了,而且a和b的值也发生了变化。这与十进制与二进制的转换有关。

fortran下的表达式等价原则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program expression_equivalence
real :: x=1.002
real :: y=1.001
real :: t1,t2,t3,t4,t5
t1=x-y
t2=x+y
print *,t1
print *,t2
t3=t1*t2
t4=x**2-y**2
t5=x*x-y*y
print *,t3
print *,t4
print *,t5
end program expression_equivalence

输出结果

1
2
3
4
5
x-y=9.99927521E-04
x+y=2.00300002
(x-y)*(x+y)=2.00285483E-03
x^2-y^2=2.00295448E-03
x*x-y*y=2.00295448E-03

计算器:

1
2
3
4
5
x-y=0.001
x+y=2.003
(x-y)*(x+y)=2.003E-3
x^2-y^2=2.003E-3
x*x-y*y=2.003E-3

LibreOffice:

1
2
3
4
5
x-y=0.001
x+y=2.003
(x-y)*(x+y)=0.002003
x^2-y^2=0.002003
x*x-y*y=0.002003

ch0804变形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PROGRAM ch0804changed
implicit none
real :: light_minute, distance, elapse
integer :: minute, second
real :: light_year ! 改变的为这一句和下面一句,将其改变为可变变量
light_year=9.46*10**12
light_minute = light_year/(365.25 * 24.0 * 60.0)
distance = 150.0 * 10 ** 6
elapse = distance / light_minute
minute = elapse
second = (elapse - minute) * 60
print *,' light takes ' , minute, ' Minutes'
print *,' ' , second, ' seconds'
print *,' To reach the earth from the sum'
END PROGRAM ch0804changed

输出结果:

1
2
3
    6 | light_year=9.46*10**12
| 1
Error: Result of exponentiation at (1) exceeds the range of INTEGER(4)

疑问: ???未解决

数组

DIMENSION 属性

使用dimension属性将变量定义为数组:

real , dimension(len) :: wages
integer , dimension(len) :: sample

赋值数组内的元素:

wages(1)=1.
wages = [1.,2.,33.]
wages = 1 # 赋值为相同值
(arg1, arg2, ... , index = istart, iend, incr) # 隐藏的循环初始化赋值

示例1:

integer, dimension(25)::array4=[(0,i=1,4),5*j,j=1,5]

结果:

0,0,0,0,5,0,0,0,0,10,0,0,0,0,15,0,0,0,0,20

示例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
program ch0901
implicit none
real :: total=0.0, average=0.0 !定义变量
real , dimension(1:12) :: rainfall
integer :: month

print *,'Type in the rainfall values'
print *,'one per line'
do month=1,12 ! 使用do进行循环
read *, rainfall(month)
enddo

do month=1,12
total = total + rainfall(month)
enddo

average = total / 12
print *,'average monthly rainfall was'
print *, average
end program ch0901

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 Type in the rainfall values
one per line
23
22
24
23
21
13
14
12
10
45
40
44
average monthly rainfall was
24.2500000

改变数组下标取值范围

一般使用1,2,...,N表示下标索引。但是有时以0开始更为方便。
指定下标的方式为:

real, dimension(lower_bound : upper_bound) :: array

数组宽度=上界值-下界值+1

数组下标越界

边界检测消耗运算资源。debug时开启,运行时关闭。

开启边界检测器在运行时输入:

# windows情况下
ifort -check filename.f90 # 编译
filename.f90 # 运行

使用命名常数

REAL, DIMENSION(MAX_SIZE)::array1

MAX_SIZE可以修改。

使用整个数组

数组a与数组b相加,为两个数组中的逐个元素相加。但是两个数组纬度必须相同!标量值与数组相同,将作用于每个元素。

使用部分数组

部分数组,即数组的子集。

subscript_1 : subscript_2 : stride  # 最小下标,最大下标,下标增量

若缺省,则分别为:数组的最小下标,数组的最大下标,1。

可变数组

定义可变数组:

1
real, dimension(:,:), allocatable :: array1,array2

分配内存空间:

1
allocate(array1(len1,len2),array2(len3,len4))

释放内存空间:

1
deallocate(array1,array2)

数组输入输出

数组元素输入输出

可以使用WRITE语句。

1
2
WRITE (*,100)a(1),a(2),a(3)
100 FORMAT ('a=',3F10.2)

隐式DO循环:

1
2
WRITE (*,100) (a(i), i=1,3)
100 FORMAT ('a=',3F10.2)
1
WRITE (unit, format)(arg1, arg2,...,index = istart, iend, incr)

arg为要输入或者输出的数值。index控制循环。READ同上。
arg如果为数组,可以使用:arg(index)表示输出。

嵌套隐式DO循环:

1
2
write(*,100) ((i,j,j=1,3),i=1,2)
100 FORMAT (I5,1X,I5)

输出:

1
2
3
4
5
6
1 1
1 2
1 3
2 1
2 2
2 3

整个数组的输入输出

1
2
3
4
5
real, dimension(5)::a=[1,2,3,4,5]
WRITE(*,100) a
100 FORMAT (6F8.3)
WRITE(*,100) a(2::2) # 输出指定下标
WRITE(*,100) a([3,2,3,4]) # 输出数组指定的下标

do循环

do counter = start, end, increment
......  !语句
enddo

其中,increment默认为1,可以省略。

使用参数设置数组大小

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
program ch0902
implicit none

integer ,parameter :: number_of_people = 10
real :: total=0.0, average=0.0 !定义变量
integer :: person
real , dimension(1:number_of_people) :: weight

do person = 1, number_of_people
print *,'type in the weight for person ', person
read *,weight(person)
total = total + weight(person)
enddo
average = total/number_of_people

print *,'the total of the weights is ', total
print *,'average weight is ', average
print *,' ',number_of_people, ' weights were '

do person = 1, number_of_people
print *,weight(person)
enddo
end program ch0902

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 type in the weight for person            1
85
type in the weight for person 2
76
type in the weight for person 3
85
type in the weight for person 4
90
type in the weight for person 5
69
type in the weight for person 6
83
type in the weight for person 7
64
type in the weight for person 8
57
type in the weight for person 9
65
type in the weight for person 10
76
the total of the weights is 750.000000
average weight is 75.0000000
10 weights were
85.0000000
76.0000000
85.0000000
90.0000000
69.0000000
83.0000000
64.0000000
57.0000000
65.0000000
76.0000000

语句:

real , dimension(number) :: weight

若下界未指定,则为1。

格式化输出形式

format输出

常用描述符号与举例:
参考:https://www.yiibai.com/fortran/fortran_basic_input_output.html

例句:

1
2
3
4
5
6
7
8
print 100
100 format (20x, 'Length', 20x,'period')

do number = 1,10
print 200, length(number), T(number)
200 format (20x, i5, 20x, f5.2)
! 输出为length和T,其中20x表示空间格数,i5指整数5个长度。f为实数。
enddo

思考题

2、计算5个数的平均值

注意! 定义数组时使用:分隔上下界,使用do循环使用,分隔上下界。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
program average_5

integer , parameter :: quantity=5
real , dimension(1 : quantity) :: numbers
real :: total=0.0, average=0.0
integer :: range
do range=1,quantity
print *,'Please input ',quantity,'numbers, the ', range,'one is:'
read *,numbers(range)
total = total + numbers(range)
enddo

average=total/quantity
print *,'The average of ',quantity,'numbers is:',average

end program average_5

输出:

1
2
3
4
5
6
7
8
9
10
11
 Please input            5 numbers, the            1 one is:
5
Please input 5 numbers, the 2 one is:
3.4
Please input 5 numbers, the 3 one is:
0.6
Please input 5 numbers, the 4 one is:
2.8
Please input 5 numbers, the 5 one is:
8.9
The average of 5 numbers is: 4.14000034

3-1、改写体重总数,计算身高总数和平均数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
program ch0902
implicit none

integer ,parameter :: number_of_people = 10
real :: total_weight=0.0, average_weight=0.0,total_height=0.0, average_height=0.0
integer :: person
real , dimension(1:number_of_people) :: weight, height

do person = 1, number_of_people
print *,'type in the weight and height for person ', person, 'use space as the interval:'
read *,weight(person), height(person)
total_weight = total_weight + weight(person)
total_height = total_height + height(person)
enddo
average_weight = total_weight/number_of_people
average_height = total_height/number_of_people

print *,'the total of the weights is ', total_weight, 'the total of the heights is ', total_height
print *,'average weight is ', average_weight, ' average height is ', average_height

print *,' ',number_of_people, ' weights and heights were '
print *,'^I','number_of_people', '^I weights',' ^I heights'
do person = 1, number_of_people
print *,person,'^I',weight(person),'^I', height(person)
enddo

end program ch0902

忘了改program名了,但是也证明了内部program的名称和外部文件名称可以不同,以外部文件名为准。
输出:
(ps:输出有些杂乱,因为还没有掌握输出书写的一些方式。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 type in the weight and height for person            1 use space as the interval:
85 1.85
type in the weight and height for person 2 use space as the interval:
76 1.80
type in the weight and height for person 3 use space as the interval:
85 1.85
type in the weight and height for person 4 use space as the interval:
90 1.70
type in the weight and height for person 5 use space as the interval:
69 1.75
type in the weight and height for person 6 use space as the interval:
83 1.67
type in the weight and height for person 7 use space as the interval:
64 1.55
type in the weight and height for person 8 use space as the interval:
57 1.63
type in the weight and height for person 9 use space as the interval:
65 1.79
type in the weight and height for person 10 use space as the interval:
76 1.78
the total of the weights is 750.000000 the total of the heights is 17.3700008
average weight is 75.0000000 average height is 1.73700011
10 weights and heights were
^Inumber_of_people^I weights ^I heights
1 ^I 85.0000000 ^I 1.85000002
2 ^I 76.0000000 ^I 1.79999995
3 ^I 85.0000000 ^I 1.85000002
4 ^I 90.0000000 ^I 1.70000005
5 ^I 69.0000000 ^I 1.75000000
6 ^I 83.0000000 ^I 1.66999996
7 ^I 64.0000000 ^I 1.54999995
8 ^I 57.0000000 ^I 1.63000000
9 ^I 65.0000000 ^I 1.78999996
10 ^I 76.0000000 ^I 1.77999997

3-2 计算BIM值

使用语句:
a.out < data.dat > results.txt

实现文件的输入和输出操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
program ch3_2
implicit none

integer ,parameter :: number_of_people = 10
real :: total_weight=0.0, average_weight=0.0,total_height=0.0, average_height=0.0
integer :: person
real , dimension(1:number_of_people) :: weight, height, BMI

do person = 1, number_of_people
print *,'type in the weight and height for person ', person, 'use space as the interval:'
read *,weight(person), height(person)
total_weight = total_weight + weight(person)
total_height = total_height + height(person)
BMI(person)=weight(person)/(height(person)**2)
enddo

average_weight = total_weight/number_of_people
average_height = total_height/number_of_people

print *,'the total of the weights is ', total_weight, 'the total of the heights is ', total_height
print *,'average weight is ', average_weight, ' average height is ', average_height

print *,' ',number_of_people, ' weights and heights were '
print *,'number_of_people', 'weights','heights' ,'BMI'
do person = 1, number_of_people
print *,person ,weight(person), height(person), BMI(person)
enddo

end program ch3_2

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
文件 result.txt
type in the weight and height for person 1 use space as the interval:
type in the weight and height for person 2 use space as the interval:
type in the weight and height for person 3 use space as the interval:
type in the weight and height for person 4 use space as the interval:
type in the weight and height for person 5 use space as the interval:
type in the weight and height for person 6 use space as the interval:
type in the weight and height for person 7 use space as the interval:
type in the weight and height for person 8 use space as the interval:
type in the weight and height for person 9 use space as the interval:
type in the weight and height for person 10 use space as the interval:
the total of the weights is 750.000000 the total of the heights is 17.3700008
average weight is 75.0000000 average height is 1.73700011
10 weights and heights were
number_of_peopleweightsheightsBMI
1 85.0000000 1.85000002 24.8356457
2 76.0000000 1.79999995 23.4567909
3 85.0000000 1.85000002 24.8356457
4 90.0000000 1.70000005 31.1418667
5 69.0000000 1.75000000 22.5306129
6 83.0000000 1.66999996 29.7608395
7 64.0000000 1.54999995 26.6389179
8 57.0000000 1.63000000 21.4535751
9 65.0000000 1.78999996 20.2865086
10 76.0000000 1.77999997 23.9868717

6、计算摆的周期(制表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
program ch6
implicit none
real , parameter :: pi = 3.1415926535
integer ,dimension(1:10) :: length
integer :: number
real ,dimension(1:10) :: T
do number = 1,10,1
length(number) = number
enddo
do number = 1,10
T(number) = 2 * pi * (length(number) / 9.81) ** .5
enddo

print 100
100 format (20x, 'Length', 20x,'period')

do number = 1,10
print 200, length(number), T(number)
200 format (20x, i5, 20x, f5.2)
enddo

end program ch6

输出:

1
2
3
4
5
6
7
8
9
10
11
Length                    period
1 2.01
2 2.84
3 3.47
4 4.01
5 4.49
6 4.91
7 5.31
8 5.67
9 6.02
10 6.34

分支结构

IF

1
2
3
4
5
6
7
8
9
10
11
12
13
IF(logical_expr1)THEN
语句1
语句2
...
ELSE IF(logical_expr2)THEN
语句1
语句2
...
ELSE
语句1
语句2
...
END IF

嵌套IF语句命名使其运行更加稳妥。

1
2
3
4
5
6
7
outer IF(test1)THEN

inner IF(test1)THEN

END IF inner

END IF outer

逻辑IF语句

IF(logical_expr) a,b,c

算数IF语句

IF(expr) a, b, c
  • <0 执行a
  • =0 执行b
  • 大于0 执行c

SELECT CASE结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[name:]SELECT CASE( case_expr)
CASE(情况选择1)[name]
语句1
语句2
...
CASE(情况选择2)[name]
语句1
语句2
...

CASE DEFAULT [name]
语句1
语句2
...
END SELECT [name]

命名case结构,使得更加清晰。

DEBUGE

在分支结构中插入WRITE是最简单的方式,输出表明运行到哪一步。

测试相等性时,使用近似测试。

循环与字符操作

do循环

不确定性执行:

1
2
3
4
5
[name:]DO
...
if(EXP)EXIT
...
END DO [name]

EXP为真,执行EXIT,退出循环。

do while循环

1
2
3
4
DO WHILE(EXP)
...
...
END DO

EXP为真,循环进行,为假时立刻退出循环。

迭代计数循环

[name:]do counter = start, end, increment
......  !语句
end do [name]
  • increment 默认为1,可以省略。
  • counter 整型变量,作为循环计数使用。
  • start end 计数循环的参数,表示开始和结束。
  • 计算规则:counter*increment≤end*increment 时进行循环。

注:为了防止永久循环,不可在循环中修改控制变量。

CYCLE语句与EXIT语句

CYCLE

使用CYCLE使得当前循环被终止,循环返回顶部。

在嵌套循环中,使用CYCLE [name]语句,可以使其CYCLE正确的循环。

IF(j=2)CYCLE outer

可以实现在内部循环中终止当前的外循环。

EXIT

使用EXIT使得循环被终止。

字符操作

子串提取

定义CHARACTER时。使用len=<len>语句,使得变量只能存储相应长度的字符。

连接操作符

//是连接操作符。

将两个或者多个字符串或者子串合并成一个大的字符串。

1
2
3
a = 'ABCDEFGHIJ'
b = '123456789'
c = a(1:3)//b(4:5)

c的值为:’ABC45‘

内置字符函数

参考3 表4-1

I/O内容

格式和格式化WRITE

1
2
WRITE(*,100)i,result
100 FORMAT('result :' ,I3, 'is' ,F7.3)
  • 100 指的是FORMAT的语句标号
  • I3 F7.3 格式符号

格式描述符

符号 含义
c 列号
d 实数输入或者输出的小数位右边的位数
m 要显示的最小位数,不够补零
n 要跳过的空格数
r 重复计数,一组或者一个描述符的使用次数,即该格式符重复r次
w 域宽,输入或者输出占用的字符数;若不能显示所有字符则使用*代替

整数输出:I描述符

rIw或者rIw.m

实数输出:F描述符

rFw.d
右对齐显示,不足补空格。

实数输出:E描述符

科学计数法:
rEw.d
±0.ddddE±ee
一般w大于等于d+7。才能容纳整个数字字符。

科学计数法:ES描述符

该格式为输出数值在1.0-10.0之间的数。
rESw.d

逻辑输出:L描述符

rLw
右对齐显示逻辑输出TF

字符输出:A描述符

rA或者rAw

水平定位:X T描述符

  • nX
    缓冲区中插入间距。n:插入的空格数。
  • Tc
    在缓冲区中跳过特定列。c:转到的列号。转到相应列号开始打印。
    注意:确保T跳到的位置不会覆盖之前的内容。

格式描述符重复使用

可以使用语句2(I6,F10.2)表示(I6,F10.2,I6,F10.2)

可以使用*(I6,F10.2)使其一直重复直至没有输出数据。

改变输出行:/描述符

在打印机中使用。

格式使用

如果FORMAT中的格式符不足,则在没有重复次数的开始括号处重新开始。

格式化READ语句

整数输入:I描述符

rIw

实数输入:F描述符

rFw.d
注:在实数描述符中输入,一定要输入带有小数点的整数!否则会产生错误!

逻辑输入:L描述符

rLw

字符输入:A描述符

rArAw

水平定位:X和T描述符

垂直定位:/描述符

文件及文件处理

OPEN

将一个文件与一个给定的i/o单元号关联起来。格式为:

OPEN(open_list)

open_list子句包含存取文件信息等。

  • UNIT=指明与文件关联的io单元号。
    UNIT=int_expr
  • FILE=指定要打开的文件名。
    FILE=char_expr
  • STATUS=指定要打开文件的状态。
    STATUS=char_expr
    其中,char_expr为下列值之一:”OLD”,”NEW”,”REPLACE”,”SCRATCH”,”UNKNOW”。
  • ACTION=指定一个文件是否以只读、只写或者读写方式打开。
    ACTION=char_expr
    其中,char_expr为下列值之一:“READ”,”WRITE”,”READWRITE”。
  • IOSTAT=指定一个整数变量名,打开操作的状态可以返回到这个变量中。
    IOSTAT=int_var
    其中,Open成功执行后,ISOTAT值为0。如果不成功,为一个包含错误信息的正整数。
  • IOMSG=指定一个字符变量名,包含错误信息。
    IOMSG=chart_var
    字符变量。OPEN语句成功执行,字符变量内容不变。否则返回错误信息。

CLOSE

CLOSE(close_list)

clost_list必须包含一个指定io单元号的子句,可以指定其它选项。(目前未阅读到那一章)。

磁盘文件读写

READ(unit,*)变量名

READ()内第一个为读取的文件的io单元号。WRITE同。

文件定位

unit:与要使用的文件关联的IO单元号

BACKSPACE

BACKSPACE(UNIT=unit)

回退一条记录。

REWIND

REWIND(UNIT=unit)

从文件头重新开始文件。

过程

外部过程:把每个子任务作为独立的程序单元进行编码。

两种外部过程:子例程和函数子程序(函数)。

子例程

CALL语句使用过程名调用,格式如下:
定义:

1
2
3
4
5
6
7
8
SUBROUTINE subroutine_name (argument_list)
...
(Delaration section)
...
(Execution section)
...
RETURN
END SUBROUTINE [subroutine_name]

INTENT属性:

定义argument_list中的变量,需要定义IN或OUT变量,以表示变量是输入还是输出。

1
2
3
REAL, INTENT(IN)::IN_VALUE1
REAL, INTENT(OUT)::OUT_VALUE1
REAL, DIMENSION(N), INTENT(INOUT)::ARRAY # 定义一个能输入和输出的数组

其中,INOUT可以写为:IN OUT
使用INTENT属性可以避免修改了IN类型的变量导致之后的该变量被改变,从而产生的错误。记住每一个形参都要设置INTENT属性!!!

定义内部变量时,定义临时变量。

1
REAL ::temp

调用:

1
CALL subroutine_name (argument_list)

调用时注意argument_list中的变量类型必须一一对应!

传递数组

数组可以显式传递或者不定大小传递。

1
2
REAL, INTENT(IN), DIMENSION(n)::data1 # n为数组大小,在输入列表中给出。
REAL, INTENT(IN), DIMENSION(*)::data1 # 不定大小,边界检测,整个或者部分数组都不能工作。

注:目前基本不使用不定数组的格式,因为会产生越界问题,且不易监测。

传递字符变量

1
CHARACTER (len=*), INTENT(IN)::STRING1

使用*表示长度即可。因为没有必要在编译时必须知道字符串长度。

小问题

不要在子例程中加入STOP语句,否则程序会“神秘终止”。
正确方法是对可能存在的错误进行检测。使用IF语句进行检测,采取相应操作。例如返回error=0

模块共享数据

模块,使用MODULE声明。

1
2
3
4
5
6
7
MODULE shared_data
! 声明在两个程序之间共享的数据
IMPLICIT NONE
SAVE ! 必须包含这个语句。
INTEGER, PARAMETER :: num_vals = 5 ! 数组中的最大数值个数
REAL, DIMENSION(num_vals)::values ! 数据值
END MODULE shared_data

使用模块中的数据:该语句必须出现在其他语句之前。

1
USE shared_data

注意:share的数据和局部变量不要重名!

模块过程

1
2
3
4
5
6
7
8
9
10
11
MODULE my_subs
implicit none
contains

SUBROUTINE sub1 (a,b,c,x,error)
implicit none

...
end subroutine sub1

END MODULE my_subs

使用:

1
USE my_subs

即可在调用程序单元中使用子例程sub1。

使用模块调用子进程,可以使程序完全掌握子进程内的所有参数等,返回必要的错误信息。而直接调用子进程,主程序无法了解子进程中的任何信息。

FORTRAN函数

函数:结果是单个数值、逻辑值、字符串或数组的过程。只能返回一个值。

用户定义函数格式:

1
2
3
4
5
6
7
8
9
10
FUNCTION name (参数列表)
...
(在声明部分必须声明name类型)
REAL, INTENT(IN):: INPUT_1
...
(执行部分)
...
name = expr
RETURN
END FUNCTION [name]

由于函数必须返回一个值,因此需要定义函数返回值类型,有两种,如下:

1
INTEGER FUNCTION my_function(i,j)
1
2
FUNCTION my_function(i,j)
INTEGER :: my_function

函数名为输出变量的名称,并且该变量已被定义,因此不需要在函数中再次定义。但是输入参数需要定义!

过程作为参数传递给其它过程

用户定义函数作为参数传递

1
2
3
4
5
# 外部定义函数,声明函数是外部调用的
REAL, EXTERNAL :: fun_1, fun_2[,.....]

# 调用fun的SUBROUTINE中
REAL,EXTERNAL :: fun

子例程作为参数传递

在SUBROUTINE中,传递名为sub的子例程。

1
2
3
4
5
6
7
8
SUBROUTINE subs(sub, ...)
...
EXTERNAL :: sub ! 定义子例程
(定义其他输入输出变量)
...
CALL sub(value_list) ! 调用子例程
...
END SUBROUTINE subs

部分内置子进程

EXP() 求指数

  • 单精度 EXP()
  • 双精度 DEXP()
  • 复数 CEXP()

复数

示例:

1
2
3
4
5
6
7
8
9
program test
COMPLEX, dimension(2):: a
real x

a(1)=CMPLX(1.0,2.0)
x=aimag(a(1))
print*, x
read *,
end program test

一些已经不再使用的内容

equivalence 等价

示例:

equivalence (nn,abuf)

nn和abuf等价,即二者完全相同,改变任何一方都会改变另一方。在定义变量时写入。