Reputation: 43
Let's have an array A(:,:)
Real, Allocatable:: A(:,:), B(:)
Integer, Allocatable:: rowin(:)
Integer:: m,j,k, nl
Allocate(A(max1,max2))
defined by several loops over m,j,k
nl = 0
A(rowin(m)+j,k) = ...
and now I want to select subset of A(:,:) such that their values are negative and store them into array B(:) unknown length yet, i.e.
if(A(rowin(m)+j,k).lt.0.d0) then
nl = nl + 1
B(nl) = A(rowin(m)+j,k)
end if
Since max1 and max2 are very large numbers, I don't want to allocate B(:) of the length max1*max2, but exactly of the length that A(:,:) contains negative values. Also, I don't want to go through complicated loops over m,j and k again. Thank you.
Upvotes: 3
Views: 1884
Reputation: 7395
If pre-allocation of B
to some fixed size is not desirable, we can emulate a dynamic array by using automatic reallocation of an allocatable array. In the simplest case, we can expand B
element-by-element by adding a new value. For example, suppose that we have double loops over m
and j
, select values that match a given condition, and add tho values to B
. Here, the size of B
grows from 0, 1, 2, ... automatically.
integer, allocatable :: B(:)
integer :: m, j
allocate( B( 0 ) ) !! start from an empty array
do m = 1, 10000
do j = 1, 10000
!! Some condition to add an element, e.g.
if ( m == j .and. mod( m, 1000 ) == 0 ) then
!! Add a new element to B.
B = [ B, m ]
endif
enddo
enddo
print *, "size( B ) = ", size( B )
print *, B(:)
The result becomes
size( B ) = 10
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
In your case you can probably add desired values of A
in a similar way, for example
allocate( B( 0 ) )
do m = 1, ...
do j = 1, ...
do k = 1, ...
if ( A( rowin(m)+j, k ) < 0.d0 ) then !! or any condition
B = [ B, A(rowin(m)+j,k) ]
end if
enddo
enddo
enddo
A drawback of this approach is that it is very inefficient because B
is re-allocated every time a new element is added. Although this is no problem for small B
, it may become a serious bottleneck for large B
. In this case one can increase the size of B
geometrically rather than increasing by 1. For example, the following code doubles the size of B
as needed so as to decrease the number of reallocation.
integer, allocatable :: B(:)
integer :: m, j, nl, p
allocate( B( 0 ) ) !! start from an empty array
nl = 0 !! number of elements found
do m = 1, 10000
do j = 1, 10000
if ( m == j .and. mod( m, 1000 ) == 0 ) then
nl = nl + 1
!! Expand B if the size becomes insufficient.
if ( size(B) < nl ) B = [ B, ( 0, p=1,nl ) ]
!! Add a new value.
B( nl ) = m
endif
enddo
enddo
B = B( 1 : nl ) !! adjust B to the optimal size
Some more notes:
resize()
that takes an allocatable array as an argument and changes its size. If the efficiency truly matters, it may be better to use allocate()
and move_alloc()
explicitly in resize()
to eliminate one temporary array.-assume realloc_lhs
option. If the size of B
can become rather large, you will also need -heap-arrays
option. For gfortran or Oracle fortran, no special options are necessary.B(:) = [ B, m ]
is NGUpvotes: 2
Reputation: 78316
This is fairly straightforward:
b = pack(a,a<0.0)
will pack the negative elements of a
into the rank-1 array b
. With a recent compiler b
will be automatically allocated to the correct size during the assignment.
(The latest Fortran standard gives a very similar operation as an example in the documentation of the pack
function.)
Upvotes: 3