《FORTRAN SYNTAX》第3章 数组

一维数组

DATA_TYPE :: arr_name(arr_size)
DATA_TYPE,DIMENSION(arr_size) :: arr_name

出于本人习惯,今后对数组的声明统一用第二种方式。arr_size只能是常数常量,不能是变量。Fortran的数组索引值默认从1开始,但是可以通过特别声明来改变这个默认规则:

DATA_TYPE,DIMENSION(istart:iend) :: arr1 !索引从istart~iend

一维数组初始化需要用到数组构造器,数组构造器的起始分隔符是(/[,结束分隔符是/)]

INTEGER(KIND = 4),DIMENSION(5) :: arr1 = (/ 1,2,3,4,5 /)
INTEGER(KIND = 4),DIMENSION(5) :: arr1 = [1,2,3,4,5]

两种数组构造器的作用一致,前者属于老版本形式,后者属于新版本形式,本文之后的内容统一用后者:

INTEGER(KIND = 4),DIMENSION(5) :: arr1 = [(i,i = 1,5)] !隐式DO循环
INTEGER(KIND = 4),DIMENSION(5) :: arr1 = [1,(i,i = 2,4),5]

下标三元组可以对一维数组的部分元素操作:

arr1(istart:iend:incr)

下标三元组指定了所有数组下标的有序子集,这个子集的起点是istart,结束点是iend,数值之间按incr增量前进。如果下标三元组中缺省istart,它默认取值为数组中第一个元素的下标;如果三元组中缺省iend,它默认取值为数组中最后一个元素的下标;如果三元组中缺省incr,它默认取值为1。
Fortran也支持对整个一维数组元素的操作:

arr1 = 5 !arr1(i) = 5
arr1 = arr1 * 10 !arr1(i) = arr1(i)*10
arr2 = arr1 !arr2(i) = arr1(i)
arr3 = arr1 + arr2 !arr3(i) = arr1(i) + arr2(i)
arr3 = arr1 * arr2 !arr3(i) = arr1(i) * arr2(i)
arr3 = arr1 / arr2 !arr3(i) = arr1(i) / arr2(i)
arr2 = SIN(arr1) !arr2(i) = SIN(arr1(i))
arr3 = arr1 > arr2 !arr3是一个逻辑数组
arr2(:) = arr1(:,2) !把二维数组arr1的第二列内容赋值给一维数组arr2

二维数组

Fortran最高可以支持十五维数组,这里仅介绍到二维数组,高维数组可以类推。

DATA_TYPE :: arr_name(arr_size1,arr_size2)
DATA_TYPE,DIMENSION(arr_size1,arr_size2) :: arr_name

同样地,可以通过特别声明来改变二维数组的索引范围:

DATA_TYPE,DIMENSION(istart1:iend1,istart2:iend2) :: arr2 !第一维索引从istart1~iend1,第二维索引从istart2~iend2

Fortran中二维数组以为主顺序为数组元素分配空间,也就是说,Fortran在内存中首先为第一列分配空间,接着是第二列,直到所有列被分配完,这种分配二维数组内存空间的方式被称为列主(Column Major)原则。对于C等编程语言来说,它们分配二维数组内存空间的方式被称为是行主(Row Major)原则。在遍历数组的时候就很有必要根据数组的保存规则进行高效访问。对于高维数组的存储,记住“第一个下标总是改变的最快,最后一个小标总是改变的最慢”。

!遍历一个三维数组的正确高效方式
DO i = 1,5
    DO j = 1,5
        DO k = 1,5
            WRITE(*,*) arr3(k,j,i)
        END DO
    END DO
END DO

二维数组的初始化不能用数组构造器,因为数组构造器总是产生一个一维数组,为了克服数组构造器的这种限制,Fortran提供了RESHAPE()函数,它可以在不改变数组中元素个数的情况下,改变一个数组的结构:

PROGRAM main
    IMPLICIT NONE
    INTEGER(KIND = 4) :: i,j
    INTEGER(KIND = 4),DIMENSION(4,3) :: arr2
    arr2 = RESHAPE([1,1,1,1,2,2,2,2,3,3,3,3],[4,3])
    WRITE(*,"(4(I2.2,2X,I2.2,2X,I2.2,/))") ((arr2(i,j),j = 1,3),i = 1,4) !隐式DO循环
    !01□□02□□03
    !01□□02□□03
    !01□□02□□03
    !01□□02□□03
END PROGRAM main

与一维数组类似,用下标三元组也可以对二维数组的部分元素操作:

arr2(istart1:iend1:incr1,istart2:iend2:incr2)

Fortran也支持对整个二维数组元素的操作:

arr2 = 5 !arr2(i,j) = 5
arr2 = arr2 * 10 !arr2(i,j) = arr2(i,j)*10
arr2 = arr1 !arr2(i,j) = arr1(i,j)
arr3 = arr1 + arr2 !arr3(i,j) = arr1(i,j) + arr2(i,j)
arr3 = arr1 * arr2 !arr3(i,j) = arr1(i,j) * arr2(i,j)
arr3 = arr1 / arr2 !arr3(i,j) = arr1(i,j) / arr2(i,j)
arr2 = SIN(arr1) !arr2(i,j) = SIN(arr1(i,j))
arr3 = arr1 > arr2 !arr3是一个逻辑数组
arr2(:,:) = arr3(:,:,1) !把三维数组arr3的第一页赋值给二维数组arr2

WHERE结构

WHERE(mask_expr) Array_Assignment_Statements !mask_expr是一个逻辑数组
[name:] WHERE(mask_expr_1)
    Array_Assignment_Statements_1
ELSE WHERE(mask_expr_2) [name] !命名的WHERE结构在ELSE WHERE后的名称不可省
    Array_Assignment_Statements_2
    ...
ELSE WHERE(mask_expr_n-1) [name]
    Array_Assignment_Statements_n-1
ELSE WHERE [name]
    Array_Assignment_Statements_n
END WHERE [name] !命名的WHERE结构在END WHERE后的名称不可省

例:假设年所得30000以下税率为10%,30000~50000之间为12%,50000以上为15%。记录5个人的所得税金额。

PROGRAM main
    IMPLICIT NONE
    REAL(KIND = 4),DIMENSION(5) :: income = [25000,30000,50000,40000,35000]
    REAL(KIND = 4),DIMENSION(5) :: tax
    WHERE(income < 30000.0)
        tax = income * 0.10
    ELSE WHERE(income < 50000.0)
        tax = income * 0.12
    ELSE WHERE
        tax = income * 0.15
    END WHERE
    WRITE(*,'(5(F8.1))') tax
END PROGRAM main

FORALL结构

FORALL结构的本质作用是通过循环对数组中的元素进行修改或者调用。可以在FORALL中使用WHERE,但不可以在WHERE中使用FORALL,写几段程序感受一下:

FORALL(i = 1:5) arr1(i) = 10 !arr1(i) = 10

[name:] FORALL(i = 1:5,j = 1:5,arr2(i,j) < 10)
    arr2(i,j) = 0 !将arr2中小于10的元素置0
END FORALL [name]

例:把矩阵的上半部设置为1,对角线设置为0,下半部设置为-1。

PROGRAM main
    IMPLICIT NONE
    INTEGER(KIND = 4) :: i,j
    INTEGER(KIND = 4) :: arr2(4,4)
    FORALL(i = 1:4,j = 1:4,i > j) arr2(i,j) = -1
    FORALL(i = 1:4,j = 1:4,i == j) arr2(i,j) = 0
    FORALL(i =1:4,j = 1:4,i < j) arr2(i,j) = 1
    WRITE(*,"(4(4(I2,2X),/))") ((arr2(i,j),j = 1,4),i = 1,4)
END PROGRAM main
!□0□□□1□□□1□□□1
!-1□□□0□□□1□□□1
!-1□□-1□□□0□□□1
!-1□□-1□□-1□□□0

可分配数组

到现在为止,所看到的所有数组的大小都是在程序开始的时候在类型声明语句中声明好的,这种数组声明的类型称为静态内存分配(Static Memory Allocation)。在更多的情况下,需要等到程序运行之后才会知道所需要使用的数组的大小。这时候就要用到动态分配内存(Dynamic Memory Allocation)。
Fortran在类型声明语句中使用ALLOCATABLE属性来声明动态分配内存的数据,使用ALLOCATE语句分配实际内存,用ALLOCATED查询分配状态,最后用DEALLOCATE语句释放内存。以二维数组为例:

!声明数组的ALLOCATABLE属性
DATA_TYPE,ALLOCATABLE,DIMENSION(:,:) :: arr2
!给数组分配实际内存,分配成功allocate_status取值为0,反之取值为非0
!allocate_err_str包含描述信息,用来告诉用户问题所在
!第二种分配方式可以将源数组source_arr复制给arr2
ALLOCATE(arr2(arr_size1,arr_size2),STAT = allocate_status,ERRMSG = allocate_err_str)
ALLOCATE(arr2,SOURCE = source_arr,STAT = allocate_status,ERRMSG = allocate_err_str)
!查询分配状态,返回逻辑值
ALLOCATED(arr2)
!释放分配内存,释放成功deallocate_status取值为0,反之取值为非0
DEALLOCATE(arr2,STAT = deallocate_status)

在配置空间时也可以设定数组索引的初始值:

DATA_TYPE,ALLOCATABLE,DIMENSION(:,:) :: arr2
ALLOCATE(arr2(istart1:iend1,istart2:iend2))

Fortran2003和更高版本允许通过简单地赋值来自动分配和释放可分配数组:

PROGRAM main
    IMPLICIT NONE
    INTEGER(KIND = 4) :: i
    INTEGER(KIND = 4),ALLOCATABLE,DIMENSION(:) :: arr1
    INTEGER(KIND = 4),DIMENSION(3) :: arr2 = [1,2,3]
    INTEGER(KIND = 4),DIMENSION(5) :: arr3 = [1,2,3,4,5]
    arr1 = arr2 !把数组arr1按3个元素值的规模来分配
    WRITE(*,"(L2)") ALLOCATED(arr1) !□T
    WRITE(*,"(3(I3))") (arr1(i),i = 1,SIZE(arr1)) !□□1□□2□□3
    arr1 = arr3 !把数组arr1按5个元素值的规模来分配
    WRITE(*,"(L2)") ALLOCATED(arr1) !□T
    WRITE(*,"(5(I3))") (arr1(i),i = 1,SIZE(arr1)) !□□1□□2□□3□□4□□5
    DEALLOCATE(arr1)
END PROGRAM main

在子例程或者函数中没有声明SAVE属性的可分配数组,当子例程或函数退出时会自动释放,不再需要DEALLOCATE语句。


《 F O R T R A N   S Y N T A X 》系列博客创作参考资料来源 《FORTRAN\ SYNTAX》系列博客创作参考资料来源 FORTRAN SYNTAX》系列博客创作参考资料来源

  1. 《Fortran95程序设计》.彭国伦.中国电力出版社.
  2. 《工程分析程序设计》.陈斌、周屈兰.西安交通大学出版社.
  3. 《Fortran程序设计(第四版)》.Stephen J.Chapman.中国电力出版社.

博客创作: A i d e n   L e e 博客创作:Aiden\ Lee 博客创作:Aiden Lee
特别声明:文章仅供学习参考,转载请注明出处,严禁盗用!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值