Concise notation for inheriting size from other array?

泪湿孤枕 提交于 2019-12-10 18:12:26

问题


In my code, I have a subroutine that takes a 5th-rank array as argument and uses a local variable, which is a 4-th rank array sharing the first 4 indices.

I'm trying to find a more concise way to express the size declaration in

subroutine mysub(momentum)
  complex, intent(in) :: momentum(:,:,:,:,:)
  complex :: prefactor( &
      & size(momentum,1), size(momentum,2), size(momentum,4) &
      & size(momentum,5) )
  ...
end subroutine mysub

The verbosity of the size declaration harms readability, especially when variable names are even longer than here.

If this was octave/matlab I'd pre-allocate prefactor by writing

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

Does Fortran 90 support something similarly concise? I know that it could be solved using preprocessor macros, such as

#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)

but introducing such definitions would probably harm the readability more than it helps.


回答1:


Fortran 2008 added the mold specifier to the allocate statement. If you have access to a compiler that supports this feature, you can try

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

This snippet worked with Intel Fortran 2016, Update 1.




回答2:


Although this may be more of a comment, how about defining a macro like this...?

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

It could also be defined repeatedly for different arguments, for example:

    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) )

If it is OK to use an allocatable array, I may allocate it as follows:

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) ) )

To obtain a combined macro for several different arguments, it might be useful to try 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 ) )

which translates (by cpp -P) to

   ...
   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) )



回答3:


If I cared about this conciseness then I would be tempted to go with the approach of using the mold= specifier with allocatable local variables. This syntax is reasonably well supported in all modern compilers and should easily slot in to the build process.

However, commenting on that answer you say you prefer "derived shapes" rather than allocatable local variables. Let's leave aside that there's little difference in the ultimate use (there are some) of these and explore that aspect.

By "derived shape" you mean an explicit shape automatic object. For such an object the extents of each rank must be specification expressions. You use SIZE(momentum,1) etc., as these expressions.

Where you suffer in the syntax is that each rank's extent must be given distinctly. So, there really is no prospect for anything shorter such as

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

However, we can do other things if we again ignore the Fortran 90 requirement.

Consider an automatic object

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

and the array

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

we can have a pointer object with bounds remapping

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

which may be a little neater, and even shorter if we call that extents array e instead.

Using the same idea of using an array for an automatic object's extents, we can use a block construct which allows for automatic objects after executable statements

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

The danger of all of this is making things much more obscure just to make one declaration a little neater. In summary, mold= really is the way to go if you want something tidier than your original. But I don't see your original code as being particularly unclear. None of the other suggestions here seems better to me - but feel free to take your pick.



来源:https://stackoverflow.com/questions/34342069/concise-notation-for-inheriting-size-from-other-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!