1 基本使用🎃

1.1 一维数组

所谓数组,实际上是一种使用数据类型的方法,通常用于配合循环使用用以处理大量的数据。简单来说,数组可以一次性申明出一长串的相同数据类型的变量。

数组的声明方式如下:

1
2
3
4
5
Datatype    name (size) 

! Datatype表示数组的类型,除了4种常用的类型(integer,real,complex,logical)以外,也可以用type自定义出新的类型
! name表示数组变量的名字
! size表示数组变量的大小

注意: 在声明数组时,数组的大小只能使用常数来赋值, 包括直接填入数字或者使用声明为parameter的常数变量

数组所能够保存的数据大小由size决定, 对于integer :: student(5)这个数组来说, 可以保存5个整数, 取用这5个整数只需要用数组名加上括号内的索引值即可, 如:student(1) = 89的意思是将89赋予student这个数组变量的第一个元素. 数组的索引值不一定要使用常数, 也可以使用一般的变量.

使用数组时超出范围是很危险的事情, 在程序编译过程中不会检查数组的使用是否超出了范围, 所以要充分的杜绝这种情况的发生.

下面演示如何使用type函数自定义数组类型:

1
2
3
4
5
6
7
8
9
10
11
Type :: person
real :: height,weight
End Type

Type(person) :: a(10) ! 使用新定义的person变量做数组声明
......
......

! 同样在变量后加上"%"来使用person类型的相关属性
a(2)%height = 180.0
a(2)%weight = 70.0

1.2 二维数组与多维数组

很多时候涉及到数据的维度会多于一维, 在Fortran中声明数组大小的时候使用多个数字进行声明即可得到一个多维数组变量, 使用该变量则需要给出相应数量的索引值. 以二维数组举例:

1
2
integer a(3,3) ! 创建一个3×3的数组(类似于矩阵)
a(1,1) = 3 ! 将3赋值到数组a的某一个元素

同样注意不能超出数值所声明的范围!另外, Fortran中最多可以声明7维的数组,使用维度越高的数组, 程序执行时读取数据的速度也就越慢, 这并不意味着不要在程序中使用多维数组, 而是说使用多维数组时需要格外小心.

1.3 数组声明的奇怪用法

在Fortran的数组中, 没有特别声明时, 数组的索引总是从1开始的:

1
2
integer a(5)
! 这个数组能使用的元素包括a(1),a(2),a(3),a(4),a(5)

也可以使用一种奇怪的方法改变数组起始的索引值, 即在声明数组变量的大小时指定索引的范围:

1
2
integer a(5,0:5) ! a(1~5,0~5)是可以使用的元素
integer b(2:3,-1:3) ! b(2~3,-1~3)是可以使用的元素


2 数组内容的设置🎯

2.1 赋予初始值

数组可以类似于一般变量利用DATA来设置数组的初始值:

1
2
3
4
5
integer a(5),b(5)
data a /1,2,3,4,5/
! 这样做数组的初始值设置为:a(1)=1,a(2)=2,a(3)=3,a(4)=4,a(5)=5
data b /3*5/
! "*"表示重复, 以上就是3重复五遍, 将数组的初始值全部设置为3

另外有一种带有”隐含式”循环的初始值赋予方式:

1
2
3
4
integer A(5)
integer I
DATA(A(I), I=2, 4)/2, 3, 4/
! 上面这行代码隐含着一个循环, I会从2增加到4, 并依照顺序取后面"//"中的数字, 也就是A(2)=2, A(3)=3, A(4)=4, A(5)和A(1)没有设置.

上述的隐含式循环实际上是Fortran语言的一个特色,你可以在程序中正常的使用:

1
2
write(*,*) (a(i),i=2,4)
! 显示a(2),a(3),a(4)的值

后面再多加一个数字,可以改变计数器的累加数值,默认情况下为1:
1
2
(A(I), I=2,10,2)
! 循环执行5次,分别执行到I=2,4,6,8,10

当然,隐含式的循环同样支持嵌套使用:
1
2
3
4
INTEGER A(2,2)
INTEGER I,J
DATA((A(I,J), I=1,2), J=1,2) /1,2,3,4/
! 其结果为A(1,1)=1, A(2,1)=2, A(1,2)=3, A(2,2)=4,括号内的循环会先执行

Fortran 90中,DATA可以被省略:

1
2
3
integer :: a(5) = (/ 1,2,3,4,5 /)
! 注意“/”与括号间不能留有空格
! 省略DATA时必须要给每一个元素都设置初始值,不能像有DATA时那样用隐含式循环只设置部分元素的初始值。

Fortran 90的隐含式循环十分强大,它可以做到其他语言做不到的东西:

1
2
3
integer ::I
integer :: a(5)=(/(I,I=1,5)/)
! 其运行结果为a(n) = n, n=1,2,3,4,5

2.2 对整个数组的操作

代码 操作
a=5 a中所有元素全部赋值为5
a=(/1, 2, 3/) a(1)=1, a(2)=2, a(3)=3
a=b 将a变成和b一样的数组,前提是a,b是维数、大小一样的数组
a=b+c a, b, c是三个维数、大小一样的数组,a中某个位置的元素是b,c中相应位置的元素的和
a=b-c 类似上一条,只不过变成了b,c对应元素的差
a=b*c 类似,b,c的乘积
a=b/c 类似,b,c的商
a=sin(b) a(i)=sin(b(i)),只要是内部的函数都可以这么用

上述方法都是对整个数组进行操作的,并且涉及到两个元素之间的操作都是对应元素之间进行的,可以结合相应的矩阵运算进行理解。

2.3 对部分数组的操作

代码 操作
a(3:5)=(/3,4,5/) a(3)=3, a(4)=4, a(5)=5
a(1:5:2)=3 a(1)=3, a(3)=3, a(5)=3
a(3:)=5 a(3)以及之后的所有元素赋值为5
a(1:3)=b(4:6) 类似于这种的要求左右数组元素个数相同
a(:)=b(:,2) a(1)=b(1,2), a(2)=b(2,2),以此类推

以上相当于利用索引值对数组进行切片,然后赋值,都是比较基础的用法。

2.4 WHERE

WHERE也是对部分数组的操作,但将上节中的由坐标索引变成了逻辑判断来去除我们需要使用的元素有点像if结构

1
2
3
4
5
6
7
where(a<3)
b = a
end where
! 这里的where描述会把数组中数值小于3的元素找出来,并把这些元素的值设置给数组b同样位置的元素。

! 也可以省略‘end where',写成:
where (a<3) b=a

注意:where是用来设置数组的值的,所以它的程序模块中必须出现与设置数组相关的命令,并且在他的整个程序模块中出现的数组的大小和维度必须是一样的。同时类似于if...else if...,你也可以使用ELSEWHERE来处理逻辑处理中的其他情况,同时where语句也是可以支持嵌套使用的,基本上和if语句大同小异。

2.5 FORALL

FORALL也可以看成是一种隐含式循环来使用数组的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
integer i
forall (i=1:5)
a(i)=i
end forall
! a(1)=1, a(2)=2, a(3)=3, a(4)=4, a(5)=5
! 其详细语法为:
forall (triplet1[, triplet2[, triplet3[, ...]]], mask)
......
end forall
! tripletn指代数组的坐标索引范围,FORALL中可以赋值好几个triplet,这主要取决于数组的维度。
! mask指代条件判断语句,类似于if语句
! 下面举个例子:
integer :: a(10,5)
forall (I=2:10:2,J=1:5,a(I,J)<10) ! 二维数组,只处理数组中范围内值小于10的元素
a(I,J)=I+J
end forall
! 和if语句与where语句的情况一样,'end forall'也是可以省略的:
forall(I=1:5,J=1:5,a(I,J)/=0) a(I,J)=1/a(I,J)

3 数组的保存规则💾

在Fortran中,数组数据的保存是按照一定规则的,我们可以根据这个规则优化代码,编写出效率较高的程序。

拿一个二维数组举例:

A(1,1)=>A(2,1)=>A(3,1)=>A(1,2)=>A(2,2)=>A(3,2)=>A(1,3)=>A(2,3)=>A(3,3)

简单来说,最左边的元素会被最先填进内存。在写代码时可以充分利用这一点来提升代码的运行速度,尽量连续的读取数据,避免CPU跳跃式地在内存中读取数据。

4 可变大小的数组🦐

在Fortran 90中,提供了一种可以在程序执行后根据实际需求改变数组大小的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
integer :: students
integer allocatable :: a(:) ! 声明一个可变大小的数组
integer :: i
integer :: error

write(*,*) "How many students?"
read(*,*)students
allocate(a(students),stat = error) ! 配置内存空间,stat用来查看配置内存是否成功:0->成功,其他->失败。
deallocatable(a) ! 释放a的内存,结合上述代码可以重新定义数组的大小
if (.not. allocated(a)) then
allocate(a(5))
end if
! 检查数组a是否有配置内存,没有就请求5个元素的内存