数组#

Arrays are a central object in Fortran. The creation of dynamic sized arrays is discussed in the allocatable arrays section.

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

  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 内在函数来查询这些属性

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