对局部数组的参数派生边界使用变量名

Use variable name for argument-derived bounds of local arrays

对于 fortran,我 运行 在我有多个局部变量的情况下,这些局部变量的大小以有点冗长的方式从输入参数中导出,例如

program pbounds 
contains 
  subroutine sbounds(x) 
    integer,intent(in) :: x(:,:)
    integer y1(size(x,1)/3,size(x,2)/2)
    integer y2(size(x,1)/3,size(x,2)/2)
    integer y3(size(x,1)/3,size(x,2)/2)
    ! ... do some stuff 
  end subroutine sbounds
end program pbounds

这似乎过于冗长,因为我不断重复大小表达式。此外,当需要更改时——例如当事实证明我需要 y4 并将 size(x,1)/3 更改为 size(x,1)/4 – 对于看起来不太整洁的真实代码,很容易错过之前的代码之一变量。

在我的真实代码中,其他示例包括来自冗长、重复的复合类型字段的大小声明,例如

type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(obj%subfield%nmax, obj%subfield%xmax)
...

是否可以在不求助于预处理器宏或可分配数组的情况下为大小表达式定义名称?

我尝试过的东西

对于可分配变量,我们可以使用局部变量 作为大小表达式的名称,但我们拆分了声明 每个本地数组超过两行。

program pboundsx
contains
  subroutine sboundsx(x)
    integer,intent(in) :: x(:,:)
    integer,allocatable :: y1(:,:),y2(:,:),y3(:,:)
    integer s(2)
    s = [ size(x,1)/3, size(x,2)/2 ]
    allocate(y1(s(1),s(2)))
    allocate(y2(s(1),s(2)))
    allocate(y3(s(1),s(2)))
    ! ... do some stuff 
  end subroutine sboundsx
end program pboundsx

使用预处理器宏,我们可以为大小表达式命名, 但是以添加多个预处理器线为代价, 扰乱缩进模式等。

program pboundsm
contains
  subroutine sboundsm(x)
    integer,intent(in) :: x(:,:)
#define s1 (size(x,1)/3)
#define s2 (size(x,2)/2)
    integer y1(s1,s2)
    integer y2(s1,s2)
    integer y3(s1,s2)
#undef s1
#undef s2
    ! ... do some stuff     
  end subroutine sboundsm
end program pboundsm

通过第二个子例程,我们可以明确显示大小 参数,但这可能是最冗长和晦涩的 解决方案;考虑到在现实世界的代码中更是如此 'x' 不是唯一的参数。

program pboundss
contains
  subroutine sboundss(x)
    integer, intent(in) :: x(:,:)
    call sboundss2(x,size(x,1)/3,size(x,2)/2)
  end subroutine sboundss
  subroutine sboundss2(x,s1,s2)
    integer, intent(in) :: x(:,:), s1, s2
    integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
  end subroutine sboundss2
  ! ... do stuff
end program pboundss

如果允许混合声明和初始化,解决方案会很简单 – 但事实并非如此:

program pboundsv
contains
  subroutine sboundsv(x)
    integer,intent(in) :: x(:,:)
    integer s1 = size(x,1)/3, s2 = size(x,2)/3  ! INVALID DECLARATION
    integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
    ! ... do stuff
  end subroutine sboundsv
end program pboundsv

如果编译器允许 (*),则可以选择将子程序主体完全包含到一个块中(= 一个新范围)并混合声明和赋值:

program pboundsv
contains
    subroutine sboundsv(x)
        integer,intent(in) :: x(:,:)
        integer s1, s2
        s1 = size(x,1)/3 ; s2 = size(x,2)/3
        block
        integer y1(s1,s2), y2(s1,s2), y3(s1,s2)
        ! ... do stuff
        endblock
    endsubroutine
end program

(*) 但是,这是 Fortran >95,Oracle studio fortran 12.5 仍然无法编译它(非常遗憾)...(gfortran 和 ifort 似乎还可以)。

部分解决方案 - 虽然规范语句不能依赖于局部变量的值 (**),但它们可以依赖于其他局部变量的先前规范。例如:

subroutine sbounds(x) 
  integer,intent(in) :: x(:,:)
  integer y1(size(x,1)/3,size(x,2)/2)
  integer y2(size(y1,1),size(y1,2))
  integer y3(size(y1,1),size(y1,2))
  ! ... do some stuff 
end subroutine sbounds

...

type(sometype), intent(in) :: obj
real :: arr1(obj%subfield%nmax, obj%subfield%nmax, obj%subfield%xmax, 3, 3)
real :: arr2(size(arr1,1), size(arr1,3))

在某些情况下,这可以使声明的逻辑结构更加清晰 - "the extent of this dimension of this variable is the same as the extent of this dimension of that variable",与计算范围的特定表达式相比,这可能是与代码 reader 更相关的消息.

** 请注意,对规范和常量表达式的各种限制是您最后一段代码的真正问题。您可以非常愉快地将声明与初始化和 Fortran 中的其他声明混合(它们只是规范语句),您不能做的是将规范语句与可执行语句混合(块构造等除外)。规范表达式不能依赖于局部变量的值,部分原因是难以确保确定性排序,而常量表达式不能依赖于任何变量的值,因为常量表达式应该是常量(并且能够在编译时评估)。