与一维数组声明接近,二维数组声明如:
REAL , DIMENSION(3,6)::array ! 3行6列实数数组,有效下标分别为1~3和1~6
INTEGER , DIMENSION(0:100,0:20) ! 101行21列整数数组,有效下标分别为0~100和0~20
CHARACTER(len=6) , DIMENSION(-2:2,10) ! 5行10列字符数组,有效下标分别为-2~2和0~10
数组是以列为主顺序进行存储的。
对于M
行N
列的二维数组,共M×N
个数据,存储的顺序是N1
列、N2
列…,按照列来进行内存分配。数据初始化和I/O语句的使用都是基于该存储特性。
二维数据的初始化原理主要有两种:DO
循环存储和列顺序内存存储,前者包含后者,相应的具体方法有以下三种:
赋值语句初始化
嵌套DO
循环
对于M×N
二维数组,如果数据有规律,示意如下:
INTEGER,DIMENSION(M , N) :: array
DO i = 1,M ! 这是以行为主顺序存储
DO j = 1,N
array(i,j) = j
END DO
END DO
INTEGER,DIMENSION(M , N) :: array
DO i = 1,N ! 这是以列为主顺序存储
DO j = 1,M
array(j,i) = j
END DO
ENDDO
RESHAPE
函数
可将一维数组old_array
转化成M
行N
列的二维数组,使用方式为:RESHAPE(old_array , [M,N])
,如:
new_array = RESHAPE([ 1 ,1 ,1 ,1 ,2 ,2 ,2 ,2 ,3 ,3 ,3 ,3],[4,3])
需注意的是,按照列顺序进行存储,因此new_array
中第一列元素均为1,第二列均为2。
类型声明初始化
使用方式与上述相同,不同的是在声明部分初始化,如:
INTEGER , DIMENSION(4,3)::new_array(4,3) = RESHAPE([ 1 ,1 ,1 ,1 ,2 ,2 ,2 ,2 ,3 ,3 ,3 ,3],[4,3])
READ
语句初始化
DO
循环来按照行顺序存储(参考data1.txt
数据排序):INTEGER::i,j
INTEGER,DIMENSION(4,3)::array
OPEN(10,FILE='data1.txt',STATUS='OLD',ACTION='READ')
READ(10,*) ((array(i,j),j=1,3),i=1,4)
!data1.txt中的数据
1 2 3 1 2 3 1 2 3 1 2 3
INTEGER,DIMENSION(4,3)::array
OPEN(10,FILE='data2.txt',STATUS='OLD',ACTION='READ')
READ(10,*) array
!data2.txt中的数据
1 1 1 1 2 2 2 2 3 3 3 3
综上所述,在初始化或操作二维数组时,尽量使用(显/隐式)DO
循环,有助于提高程序的可读性,而不使用内存存储的初始化方法,该方法仅用于理解数组存储的过程。
与一维数组操作相似,两个二维数组操作的前提条件是结构一致。同样地,可使用下标进行操作,如:
array(a,:) ! 表示数组的第a行
array(:,b) ! 表示数组的第b列
array(a1:a2,:) ! 表示数组的第a1行至第a2行。列同理
array(a1:a2:a3,:) ! 表示数组的第a1行至第a2行,但增量为a3行。列同理
Fortran支持下标多达15个的复杂数组。多维数组的声明、初始化和使用方式与二维数组相同。同样是以列顺序作为存储方法。对于多维数组array(a,b,...,n)
,在使用过程中,a
下标总是变化最多(频繁)的,n
下标总是变化最少的。
Fortran中有三大类内置函数:基本函数、查询函数和变换函数。
基本函数
大部分接受标量参数的Fortran内置函数都是基本函数。通用的基本函数有
ABS
、SIN
、COS
、TAN
、EXP
、LOG
、LOG10
、MOD
、SQRT
等。这些基本函数同样也适用于数组参数。
当对数组使用基本函数,则是对数组中每个元素使用基本函数,例子如:
声明部分:
REAL , DIMENSION(4)::array_x = [1.,2.,3.,4]
REAL , DIMENSION(4)::array_y
INTEGER::i
执行部分:
array_y = SIN(array_x) ! 对整个数组进行基本函数操作
等价于
DO i = 1,4
array_y(i) = SIN(x(i)) ! 对整个数组中的每个元素进行函数操作
END DO
查询函数
查询研究对象的属性。对于数组来说,则是查询数组的大小、结构、元素下标等基本属性。
对于二维数组,常用的查询函数如下表:
函数 | 作用 |
---|---|
SHAPE(array) |
返回数组array 的形状、结构 |
SIZE(array,dimension) |
dimension 为1表示行,为2表示列,指定则返回数组array 的行或列的个数,不指定则返回整个数组的元素总个数 |
LBOUND(array,dimension) |
dimension 不指定则返回数组array 的下标下边界,指定1或2则返回行或列的下标下边界 |
UBOUND(array,dimension) |
dimension 不指定则返回数组array 的下标上边界,指定1或2则返回行或列的下标上边界 |
表中查询函数的使用方法如下例:
PROGRAM array_test ! 主程序
IMPLICIT NONE
REAL,DIMENSION(-6:4,0:4)::a=1.0 ! 数组的元素均为1.0
WRITE(*,100) SHAPE(a) ! 几行几列
100 FORMAT('数组a的形状为:',2I3)
WRITE(*,200) SIZE(a) ! 数组元素的个数
200 FORMAT('数组a的大小为:',I3)
WRITE(*,300) LBOUND(a) ! 下边界
300 FORMAT('数组a的下标最小值分别为:',2I3)
WRITE(*,400) UBOUND(a) ! 上边界
400 FORMAT('数组a的下标最大值分别为:',2I3)
END PROGRAM array_test
相应的结果为:
数组a的结构为: 11 5
数组a的大小为: 55
数组a的下标最小值分别为: -6 0
数组a的下标最大值分别为: 4 4
RESHAPE
函数就属于变换函数,在二维数组初始化示例中将一个一维数组转换成二维数组。由上述可知,对数组中所有元素进行操作的方法有两种:DO
循环遍历操作和对数组使用函数(这仅适用于基本函数等部分情况)。
对数组中的部分元素进行操作的一种常规方法是使用DO
循环和IF
判断结构组合。需要对数组中的所有元素进行逐次判断。
针对Fortran中的数组,存在一种WHERE
结构,能够一次完成操作,被称为掩码数组赋值(掩码可理解成为开关,符合要求则开,不符合则关)。
WHERE
结构的常用格式为:
[name:] WHERE (mask_expression1)
块1
ELSEWHERE (mask_expression2) [name]
块2
ELSEWHERE [name]
块3
END WHERE [name]
注意事项:
mask_expression
是一个逻辑数组,与被处理的数组具有同样的结构,如name>0.
,表示大于0.的元素;mask_expression1
中为真时的数组元素执行块1,mask_expression2
为真时的数组元素执行块2,mask_expression1
和mask_expression2
均为假时的数组元素执行块3;WHERE
结构中可以有多个ELSEWHERE
,对于数组中任何给定元素,至多只能执行语句中的一个块,块其实是一系列赋值语句;WHERE
结构是同时运算,更加优于对逐个元素完成运算(如DO
循环),尤其对于多维数组,。类似于IF
语句,Fortran提供了一条单行的WHERE
语句:WHERE (mask_expression) 块
WHERE
结构是通过逻辑条件来对数组中的元素进行操作,FORALL
结构是通过下标所以和逻辑条件来对数组中的元素进行操作。
FORALL
结构的常用格式为:
[name:] FORALL (索引1 [,索引2,...,逻辑条件])
赋值1
赋值2
...
赋值n
END FORALL [name]
注意事项:
索引
是指下标的三元组操作(a:b:c)
,a
为起始值,b
为结束值,c
为增量值;索引
必须至少有一个,逻辑条件
是可选条件;FORALL
结构可以拆成含有IF
结构的嵌套DO
结构,前者是任意次序地处理数组中的元素,后者是按照DO
循环的严格顺序来处理;赋值
语句是有先后执行顺序的,先执行赋值1
,再执行赋值2
,直至赋值n
,换言之,赋值1
的结果可能会对赋值2
的计算有影响。例子:
REAL.DIMENSION(10,10)::array = 1.
FORALL(i=1:10)
array(i:i) = 100. ! 矩阵对角的元素由1.替换成100.
END FORALL
另一个例子:
REAL.DIMENSION(m,n)::array
FORALL(i=1:m,j=1:n,array(i,j)/=0.)
array(i:j) = 1./ array(i:j) ! 将矩阵中所有非0的元素转换成倒数
END FORALL
类似于IF
语句,Fortran提供了一条单行的FORALL
语句:FORALL (索引1 [,索引2,...,逻辑条件]) 赋值
静态数组的大小在编译时声明,后续只能靠重新编译程序进行修改。如4行3列数组:
INTEGER,DIMENSION(4,3)::array
动态数组(可分配数组)的大小在执行时声明,为了适应实际数组的大小,可允许程序调整内存需求量。
可分配数组的定义过程是:ALLOCATABLE
属性声明可分配数组、ALLOCATE
语句分配内存、DEALLOCATE
语句释放内存。(一般版本、旧版本)
1)ALLOCATABLE
属性的使用结构为:
type,ALLOCATABLE,DIMENSION(:[,:,...])::array
注意事项:
ALLOCATABLE
属性用于声明数组的大小是动态的,同时需要声明具体的维度;:
代表维度,如REAL,ALLOCATABLE,DIMENSION(:,:)::a
代表的是二维动态实数数组a
;2)ALLOCATE
语句的使用结构有两种形式,分别为:
形式①:ALLOCATE(array([i1:]i2,[j1:]j2,...),...,STAT=status,ERRMSG=err_msg)
形式②:ALLOCATE(array,SOURCE=source_array,STAT=status,ERRMSG=err_msg)
注意事项:
ALLOCATE
语句为已声明的动态数组分配内存空间,并指定大小;source_array
的结构赋值到array
中(单个);STAT
和ERRMSG
子句是可选的,但应尽量有,一方面可以检查分配状态,另一方面在出现分配错误时可友好中止程序,不至于莫名其妙的中断。当内存分配成功时,STAT
的值为0,ERRMSG
的值不会变化,当内存分配失败时,STAT
的值为非0,ERRMSG
的值包含错误的信息;ALLOCATE(array(-5:5),STAT=status,ERRMSG=err_msg)
,形式②如ALLOCATE(array1,SOURCE=array2,STAT=status,ERRMSG=err_msg)
ALLOCATED()
检测数组的分配状态,分配成功会返回.TRUE.
;SAVE
(语句或属性),则在该过程的第一次调用中利用ALLOCATE
语句分配一次内存,随后在连续调用中的动态数组内容会被保持不变(除非进行重新计算赋值);如果没有使用SAVE
,则过程返回至调用程序中时,数组内容被自动释放掉。3)DEALLOCATE
语句的使用结构为:
DEALLOCATE(array1,...,STAT=status,ERRMSG=err_msg)
注意事项:
STAT
和ERRMSG
的意义和ALLOCATE
语句的定义相一致。在2003及更高版本中,允许通过简单地赋值数据来自动分配和释放动态数组。
相应的语句为:
type,DIMENSION(:[,:,...]),ALLOCATABLE::array
注意事项:
ALLOCATE
和DEALLOCATE
语句;SAVE
属性的动态数组,内存会在子例程或函数退出时自动释放,不需要执行DEALLOCATE
语句进行释放。Fortran提供了在过程执行时自动创建临时数组,在过程执行结束返回至调用程序后自动释放掉该数组。
自动数组定义:①局部数组变量(非形参);②显式结构(有下标取值,但不固定,由形参传递或由模块的数据指定)。
示例:
SUBROUTINE sub1(....,m,n) ! 形参控制局部数组变量的下标
...
REAL,DIMENSION(m,n)::a ! 局部变量a数组
a = 1. ! 在执行部分初始化
...
注意事项:
SAVE
属性,因为只是临时性的;A
中调用另一个过程B
,过程A
中的自动数组可以作为调用参数传递到过程B
中;RETURN
或END
语句时,自动数组会释放掉。总的来说,自动数据用于在过程中建立局部临时数组(具有过渡性);可分配数组可在主程序和过程中创建并销毁,同时能够在同一次执行过程中改变结构大小。