根据子例程参数创建常量

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了根据子例程参数创建常量相关的知识,希望对你有一定的参考价值。

我有以下代码片段将矢量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
答案

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

规范表达式可以包括对纯函数的引用。您可以使用这样的纯函数来分解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中进行形状计算。

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

另一答案

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

你甚至不需要一个常量(参数)只是一个变量就足够了。但这是不允许的。

另一答案

事实上,这个问题与数组声明无关。以下简单的代码片段

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

以上是关于根据子例程参数创建常量的主要内容,如果未能解决你的问题,请参考以下文章

批处理脚本子例程:传递参数

fortran 模块的子例程名称冲突

以种类参数作为子例程参数的参数化派生类型

模块化程序-子例程

Perl 中带有输出参数的子例程的最佳实践命名约定

如何从 unix shell 调用带有参数的子例程