根据子例程参数

时间:2016-10-28 09:56:53

标签: fortran

我有以下代码将矢量x分成几个数组。

subroutine split(n, x, r, v, p, t)
    implicit none
    integer, intent(in) :: n
    double precision, intent(in) :: x(n)

    integer, parameter :: m = (n + 6) / 10
    ! problem here             1
    double precision, intent(out) :: r(3, m - 1)
    double precision, intent(out) :: v(3, m - 1)
    double precision, intent(out) :: p(3, m)
    double precision, intent(out) :: t(m)
    ! code
end subroutine split

此代码无法使用消息

进行编译
Error: Parameter 'n' at (1) has not been declared or is a variable, which does not reduce to a constant expression

如果我手动将所有m更改为(n + 6) / 10,则代码编译正常,但我正在寻求更优雅的方法。

作为替代方法,我已将代码重写为

subroutine splitcore(n, m, x, r, v, p, t)
    implicit none
    integer, intent(in) :: n, m
    double precision, intent(in) :: x(n)
    double precision, intent(out) :: r(3, m - 1)
    double precision, intent(out) :: v(3, m - 1)
    double precision, intent(out) :: p(3, m)
    double precision, intent(out) :: t(m)
    ! code
end subroutine splitcore

subroutine split(n, x, r, v, p, t)
    implicit none
    integer, intent(in) :: n
    double precision, intent(in) :: x(n)

    integer :: m
    double precision, intent(out) :: r(3, *)
    double precision, intent(out) :: v(3, *)
    double precision, intent(out) :: p(3, *)
    double precision, intent(out) :: t(*)

    m = (n + 6) / 10

    call splitcore(n, m, x, r, v, p, t)
end subroutine split

3 个答案:

答案 0 :(得分:1)

这是不可能的。您只需使用(n + 6) / 10而不是m。 Fortran不允许您在数组声明之前定义任何中间计算。

你甚至不需要一个常量(参数),只需一个变量即可。但这是不允许的。

答案 1 :(得分:1)

事实上,问题与数组声明没有没有。以下简单代码段

subroutine split(n, x, r, v, p, t)
    implicit none

    integer, intent(in) :: n
    double precision, intent(in) :: x(n)
    integer :: m = (n + 6) / 10
end subroutine split

也不会编译。因此,即使将m声明为变量,也无法通过(n + 6) / 10表达式对其进行初始化。问题是(n + 6) / 10不是常量表达式,因为它包含变量n

当然可以先声明变量m并稍后为其指定(n + 6) / 10表达式的值:

subroutine split(n, x, r, v, p, t)
    implicit none

    integer, intent(in) :: n
    double precision, intent(in) :: x(n)
    integer :: m

    m = (n + 6) / 10
end subroutine split

例如,这可用于分配相应的数组(当然,必须声明为可分配的数组):

subroutine split(n, x, r, v, p, t)
    implicit none

    integer, intent(in) :: n
    double precision, intent(in) :: x(n)
    integer :: m

    double precision, dimension(:, :), allocatable, intent(out) :: r
    double precision, dimension(:, :), allocatable, intent(out) :: v
    double precision, dimension(:, :), allocatable, intent(out) :: p
    double precision, dimension(:), allocatable, intent(out) :: t

    m = (n + 6) / 10
    allocate (r(3, m-1))
    allocate (v(3, m - 1))
    allocate (p(3, m))
    allocate (t(m))
    ! code
end subroutine split

答案 2 :(得分:1)

子程序中数组声明的数组规范允许是规范表达式。

规范表达式可以包含对纯函数的引用。您可以使用这样的纯函数来计算m的有效计算。

要在其使用范围内被认为是纯粹的,必须可以访问纯函数的显式接口。提供这样一个显式接口的最简单方法是将函数放在一个模块中(如果split已经在这样的模块中,它可以是同一模块)。

module m_mod
  implicit none
contains
  pure function m(n)
    integer, intent(in) :: n
    integer :: m
    m = (n + 6) / 10
  end function m
end module m_mod

subroutine split(n, x, r, v, p, t)
  use m_mod
  implicit none
  integer, intent(in) :: n
  double precision, intent(in) :: x(n)

  double precision, intent(out) :: r(3, m(n) - 1)
  double precision, intent(out) :: v(3, m(n) - 1)
  double precision, intent(out) :: p(3, m(n))
  double precision, intent(out) :: t(m(n))
  ...

就原始代码而言 - 变量,常量或类型参数的初始化程序必须是常量表达式 - 这是编译器在编译时可以评估的有效内容。常量表达式对它的限制比规范表达式更多 - 例如它不能引用变量的值 - 因为变量不是常量。

split子程序中的虚拟变量可能被假定为形状(使用(:)(:,:)声明,而不是显式形状数组,而不是等级。然后从实际参数的形状中取出数组的形状"(假设"),根本不需要在子程序split中进行形状计算。

使用具有假定形状伪参数的子例程需要一个显式接口,以便在引用过程的范围内访问该过程。