Fortran语言学习——数组
1 基本使用🎃
1.1 一维数组
所谓数组,实际上是一种使用数据类型的方法,通常用于配合循环使用用以处理大量的数据。简单来说,数组可以一次性申明出一长串的相同数据类型的变量。
数组的声明方式如下:
1 | Datatype name (size) |
注意: 在声明数组时,数组的大小只能使用常数来赋值, 包括直接填入数字或者使用声明为parameter的常数变量
数组所能够保存的数据大小由size
决定, 对于integer :: student(5)
这个数组来说, 可以保存5个整数, 取用这5个整数只需要用数组名加上括号内的索引值即可, 如:student(1) = 89
的意思是将89赋予student
这个数组变量的第一个元素. 数组的索引值不一定要使用常数, 也可以使用一般的变量.
使用数组时超出范围是很危险的事情, 在程序编译过程中不会检查数组的使用是否超出了范围, 所以要充分的杜绝这种情况的发生.
下面演示如何使用type函数自定义数组类型:
1 | Type :: person |
1.2 二维数组与多维数组
很多时候涉及到数据的维度会多于一维, 在Fortran中声明数组大小的时候使用多个数字进行声明即可得到一个多维数组变量, 使用该变量则需要给出相应数量的索引值. 以二维数组举例:
1 | integer a(3,3) ! 创建一个3×3的数组(类似于矩阵) |
同样注意不能超出数值所声明的范围!另外, Fortran中最多可以声明7维的数组,使用维度越高的数组, 程序执行时读取数据的速度也就越慢, 这并不意味着不要在程序中使用多维数组, 而是说使用多维数组时需要格外小心.
1.3 数组声明的奇怪用法
在Fortran的数组中, 没有特别声明时, 数组的索引总是从1开始的:1
2integer a(5)
! 这个数组能使用的元素包括a(1),a(2),a(3),a(4),a(5)
也可以使用一种奇怪的方法改变数组起始的索引值, 即在声明数组变量的大小时指定索引的范围:1
2integer 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
5integer 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
4integer 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
2write(*,*) (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
4INTEGER 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 | integer :: a(5) = (/ 1,2,3,4,5 /) |
Fortran 90的隐含式循环十分强大,它可以做到其他语言做不到的东西:
1 | integer ::I |
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
7where(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
18integer 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 | integer :: students |