数组#

数组是 Fortran 中的核心对象。动态大小数组的创建在可分配数组部分中进行了讨论。

有四种方法可以将数组传递给过程

  1. 假定形状数组

  2. 假定秩数组

  3. 显式形状数组

  4. 假定大小数组

将数组传递给过程的首选方法是作为假定形状数组

subroutine f(r)
  real(dp), intent(out) :: r(:)
  integer :: n, i
  n = size(r)
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine f

高维数组可以通过类似的方式传递。

subroutine g(A)
  real(dp), intent(in) :: A(:, :)
  ...
end subroutine g

数组只是通过

real(dp) :: r(5)
call f(r)

在这种情况下,不会执行数组复制,这具有以下优点:形状和大小信息会自动传递并进行编译时检查,并且可以选择在运行时进行检查。类似地,数组步长可以传递,而无需复制数组,而是作为假定形状描述符

real(dp) :: r(10)
call f(r(1:10:2))
call f(r(2:10:2))

这应该始终是您在子程序中传递数组进出的默认方式。避免将数组作为整个切片传递,因为它会模糊代码的实际意图

real(dp) :: r(10)
call f(r(:))

如果需要将更通用的数组传递给过程,则可以使用 Fortran 2018 标准中引入的假定秩功能

subroutine h(r)
  real(dp), intent(in) :: r(..)
  select rank(r)
  rank(1)
  ! ...
  rank(2)
  ! ...
  end select
end subroutine h

可以使用select rank结构在运行时查询实际秩。这很容易创建必须处理不同数组秩的更通用函数。

显式形状数组对于从函数返回数据很有用。它们的大部分功能都可以由假定形状假定秩数组提供,但它们经常用于与 C 接口或在旧版 Fortran 过程中进行接口,因此这里将简要讨论它们。

要使用显式形状数组,必须像下面的示例一样显式地将维度作为虚拟参数传递

subroutine f(n, r)
  integer, intent(in) :: n
  real(dp), intent(out) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine

对于高维数组,必须传递其他索引。

subroutine g(m, n, A)
  integer, intent(in) :: m, n
  real(dp), intent(in) :: A(m, n)
  ...
end subroutine

可以使用以下方式调用例程

real(dp) :: r(5), s(3, 4)
call f(size(r), r)
call g(size(s, 1), size(s, 2), s)

请注意,不会检查形状,因此以下代码将是合法的代码,并且可能会产生不正确的结果

real(dp) :: s(3, 4)
call g(size(s), 1, s)  ! s(12, 1) in g
call g(size(s, 2), size(s, 1), s)  ! s(4, 3) in g

在这种情况下,内存布局将保留,但形状将更改。此外,显式形状数组需要连续内存,并且在传递非连续数组步长的情况下会创建临时数组。

要从具有显式形状的函数返回数组,请使用

function f(n) result(r)
  integer, intent(in) :: n
  real(dp) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end function

最后,还有假定大小数组,它提供了最少的编译时和运行时检查,并且可以在旧版代码中经常找到。应避免使用它们,而应使用假定形状假定秩数组。假定大小数组虚拟参数由最后一个维度中的星号标识,这禁用了使用此数组与许多内在函数(如sizeshape)的功能。

要检查假定形状数组的正确大小和形状,可以使用sizeshape内在函数来查询这些属性

if (size(r) /= 4) error stop "Incorrect size of 'r'"
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'"

请注意,size返回所有维度的总大小。要获取特定维度的形状,请将其作为第二个参数添加到函数中。

可以使用数组构造器初始化数组

integer :: r(5)
r = [1, 2, 3, 4, 5]

可以使用构造数组的类型来注释数组构造器

real(dp) :: r(5)
r = [real(dp) :: 1, 2, 3, 4, 5]

也可以在数组构造器中使用隐式 do 循环

integer :: i
real(dp) :: r(5)
r = [(real(i**2, dp), i = 1, size(r))]

为了使数组从 1 以外的索引开始,请执行以下操作

subroutine print_eigenvalues(kappa_min, lam)
  integer, intent(in) :: kappa_min
  real(dp), intent(in) :: lam(kappa_min:)

  integer :: kappa
  do kappa = kappa_min, ubound(lam, 1)
    print *, kappa, lam(kappa)
  end do
end subroutine print_eigenvalues