从其他数组继承大小的简明表示法?

时间:2015-12-17 18:49:02

标签: arrays fortran parameter-passing fortran90

在我的代码中,我有一个子程序,它采用第5级数组作为参数,并使用局部变量,这是一个共享前4个索引的第4级数组。

我试图找到一种更简洁的方式来表达

中的尺寸声明
subroutine mysub(momentum)
  complex, intent(in) :: momentum(:,:,:,:,:)
  complex :: prefactor( &
      & size(momentum,1), size(momentum,2), size(momentum,4) &
      & size(momentum,5) )
  ...
end subroutine mysub

大小声明的详细程度会损害可读性,尤其是当变量名称比此处更长时。

如果是octave / matlab我会通过编写预先分配prefactor

prefactor = zeros(size(momentum)([1 2 4 5]))

Fortran 90是否支持同样简洁的内容?我知道它可以使用预处理器宏来解决,例如

#define XSIZE2(array,a,b) SIZE(array,a), SIZE(array,b)
#define XSIZE3(array,a,b,c) SIZE(array,a), SIZE(array,b), SIZE(array,c)
#define XSIZE4(array,a,b,c,d) SIZE(array,a), SIZE(array,b), SIZE(array,c), SIZE(array,d)

但是引入这样的定义可能会损害可读性,而不是它的帮助。

3 个答案:

答案 0 :(得分:3)

Fortran 2008将mold说明符添加到allocate语句中。如果您可以访问支持此功能的编译器,则可以尝试

program main

  implicit none

  integer :: a(2,3,4,5,6)
  integer, allocatable :: b(:,:,:,:)

  print *, shape(a)

  allocate(b, mold=a(:,:,:,:,1))
  print *, shape(b)

end program main

此代码段与英特尔Fortran 2016更新1一起使用。

答案 1 :(得分:1)

虽然这可能更像是评论,但如何定义这样的宏......?

    subroutine mysub(momentum)
    complex, intent(in) :: momentum(:,:,:,:,:)
#define _(i) size( momentum, i )
    complex :: prefactor( _(1), _(2), _(4), _(5) )

也可以针对不同的参数重复定义,例如:

    subroutine mysub( momentum, coeff )
    complex, intent(in) :: momentum(:,:,:,:,:), coeff(:,:,:)
#define _(i) size( momentum, i )
    complex :: prefactor_momentum( _(1), _(2), _(4), _(5) )
#define _(i) size( coeff, i )
    complex :: prefactor_coeff( _(1), _(3) )

如果可以使用可分配的数组,我可以按如下方式分配它:

subroutine sub( momentum )
    complex, intent(in) :: momentum(:,:,:,:,:)
    complex, allocatable :: prefactor(:,:,:,:)
    integer :: d( 5 )

    d = shape( momentum )
    allocate( prefactor( d(1), d(2), d(4), d(5) ) )

要获得几个不同参数的组合宏,尝试this approach

可能很有用
#define dim2(A,i1,i2)       size(A,i1), size(A,i2)
#define dim3(A,i1,i2,i3)    size(A,i1), size(A,i2), size(A,i3)
#define dim4(A,i1,i2,i3,i4) size(A,i1), size(A,i2), size(A,i3), size(A,i4)

#define _dims(A,_1,_2,_3,_4,NAME,...) NAME
#define getdims(A,...) _dims(A, __VA_ARGS__, dim4, dim3, dim2)(A,__VA_ARGS__)

subroutine mysub( momentum )
    complex, intent(in) :: momentum(:,:,:,:,:)
    complex :: prefactor2( getdims( momentum, 1, 5 ) )
    complex :: prefactor3( getdims( momentum, 1, 3, 5 ) )
    complex :: prefactor4( getdims( momentum, 1, 2, 4, 5 ) )

将(cpp -P)翻译为

   ...
   complex :: prefactor2( size(momentum,1), size(momentum,5) )
   complex :: prefactor3( size(momentum,1), size(momentum,3), size(momentum,5) )
   complex :: prefactor4( size(momentum,1), size(momentum,2), size(momentum,4), size(momentum,5) )

答案 2 :(得分:1)

如果我关心这种简洁性,那么我很想和approach of using the mold= specifier一起使用可分配的局部变量。所有现代编译器都很好地支持这种语法,并且应该很容易地插入到构建过程中。

然而,评论这个答案你说你更喜欢“派生形状”而不是可分配的局部变量。让我们撇开它们的最终用途(有一些)并没有什么区别,并探讨这方面。

“派生形状”是指显式形状自动对象。对于这样的对象,每个等级的范围必须是规范表达式。您使用SIZE(momentum,1)等作为这些表达式。

您在语法中遇到的情况是必须明确给出每个等级的范围。因此,确实没有任何缩短的可能性,例如

complex prefactor(array_extents_spec)  ! As an array, say.

但是,如果我们再次忽略Fortran 90的要求,我们可以做其他事情。

考虑一个自动对象

complex, target :: prefactor_t(SIZE(momentum)/SIZE(momentum,3))  ! rank-1, of desired size

和数组

integer extents(4)
extents = [SIZE(momentum,1), SIZE(momentum,2), SIZE(momentum,4), SIZE(momentum,5)]

我们可以有一个带有边界重映射的指针对象

complex, pointer :: prefactor(:,:,:,:)
prefactor(1:extents(1), 1:extents(2), 1:extents(3), 1:extents(4)) => prefactor_t

可能有点整洁,如果我们调用范围数组e,则更短。

使用相同的想法将数组用于自动对象的范围,我们可以使用block构造,该构造允许在可执行语句之后使用自动对象

complex, intent(in) :: momentum(:,:,:,;,:)
integer e(5)
e = SHAPE(momentum)
block
  complex prefactor(e(1), e(2), e(4), e(5))  ! Automatic, of desired shape
end block

所有这一切的危险在于让事情变得更加模糊,只是为了让一个宣言更整洁。总之,如果你想要比原版更整洁的东西,mold=真的是要走的路。但我不认为你原来的代码特别不清楚。这里没有其他建议对我来说似乎更好 - 但随意选择。