问题
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