我有以下代码将矢量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
答案 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
中进行形状计算。
使用具有假定形状伪参数的子例程需要一个显式接口,以便在引用过程的范围内访问该过程。