[fortran] вопрос знатокам

mazamaza2008

Я хочу написать функцию, которая на вход получает массив и размерности, выделяет память allocate-ом, и возвращает массив. Причем функция должна уметь выделять память для массивов разного ранга. По существу мне нужна просто обертка для allocate, которая будет еще кое-что делать.
В Fortran 2003 есть возможность передавать allocatable объекты как параметры. Но проблема в том, что мне приходится указывать число размерностей массива в интерфейсе функции, поэтому она работает только для массивов определеного ранга.
Код, приведенный ниже, не компилируется, так как subroutine memory на вход принимает массив ранга 1, а я ему даю массив ранга 3.
Можно ли как-нибудь все-таки такую функцию реализовать? Писать отдельно функции для массивов ранга 1, 2, 3 и т.д. как-то не хочется...
Спасибо!
 
module memory_allocator
contains

subroutine memory(array, length)
implicit none

real(8 allocatable, intent(out dimension(:) :: array
integer, intent(in) :: length

integer :: ierr

print *, "memory: before: ", allocated(array)

allocate(array(length stat=ierr)
if (ierr /= 0) then
print *, "error allocating memory: ierr=", ierr
end if

print *, "memory: after: ", allocated(array)

end subroutine memory

subroutine freem(array)
implicit none

real(8 allocatable, dimension(:) :: array

print *, "freem: before: ", allocated(array)
deallocate(array)
print *, "freem: after: ", allocated(array)

end subroutine freem

end module memory_allocator

program alloc
use memory_allocator
implicit none

integer, parameter :: n = 3
real(8 allocatable, dimension(:,:,:) :: foo
integer :: i, j, k

print *, "main: before memory: ", allocated(foo)
call memory(foo, n*n*n)
print *, "main: after memory: ", allocated(foo)

do i = 1,n
do j = 1,n
do k = 1, n
foo(i, j, k) = real(i*j*k)
end do
end do
end do

print *, foo

print *, "main: before freem: ", allocated(foo)
call freem(foo)
print *, "main: after freem: ", allocated(foo)

end program alloc

 
gfortran -o alloc alloc.f90 -std=f2003
alloc.f90:46.14:

call memory(foo, n*n*n)
1
Error: Rank mismatch in argument 'array' at (1) (1 and 3)
alloc.f90:60.13:

call freem(foo)
1
Error: Rank mismatch in argument 'array' at (1) (1 and 3)

NataliaS

как вариант, можно хранить массив ранга 1 и по мере необходимости работать с ним как с 3-х мерным, воспользовавшись фичей 2003 фортрана 'pointer rank remapping' (правда gfortran (4.4.1) и ifort (11.1.038) ее не поддерживают).

program alloc
use memory_allocator
implicit none

integer, parameter :: n = 3
real(8 dimension( allocatable, target :: foo
real(8 dimension(:,:, pointer :: foo3d
integer :: i, j, k

print *, "main: before memory: ", allocated(foo)
call memory(foo, n*n*n)
foo3d(1:n,1:n,1:n) => foo
print *, "main: after memory: ", allocated(foo)

forall (i=1:n, j=1:n, k=1:n) foo3d(i, j, k) = real(i*j*k,kind=8)

print *, foo

print *, "main: before freem: ", allocated(foo)
call freem(foo)
print *, "main: after freem: ", allocated(foo)

end program alloc

durka82

Но проблема в том, что мне приходится указывать число размерностей массива в интерфейсе функции, поэтому она работает только для массивов определеного ранга.

А что мешает передать размерность массивом?

mazamaza2008

Ну вот а как это сделать?..
Я так понимаю, что этот массив должен быть описан как deferred shape, т.е. a(:,..., потому что он allocatable. Т.е. про него известен только ранг, а как его передать параметром?

durka82

Самый простой вариант передать всё явно: ранг, массив размерностей размера ранг (это если все размерности начинаются с 1 и того же числа - если надо использовать отрезки - немного сложнее) и сам массив.
Что-то вроде такого:
  subroutine memory(array, length, range)
implicit none

integer, intent(in) :: range, length(range)
...

mazamaza2008

Я не понимаю, array в этом случае как описать?
Вот так не получится, потому что allocatable массив array должен быть описан как deferred shape
  
subroutine memory(array, dims)
integer, intent(in) :: dims(:)
real(8 intent(inout allocatable :: array(dims)

alloc.f90:7.47:

real(8 allocatable, intent(out) :: array(dims)
1
Error: Expression at (1) must be scalar
alloc.f90:4.25:

subroutine memory(array, dims)
1
Error: Allocatable array 'array' at (1) must have a deferred shape

durka82

Действительно, с рангом массива сложнее - сконструировать не проблема, но вот описать такой параметр?..
Так сразу непонятно как.
Имхо это одна из недоделок фортрана, но мб и можно извернуться через символьные константы (описывая ими сечения, но у меня не получилось, а мб и нельзя).
Как вариант - можно действительно всё делать одномерными массивами (как тут уже и советовали).
Тогда в минимальном варианте надо будет лишь написать свой метод доступа к ячейке (но он будет конечно не такой же универсальный, как и в стандарте, но мб и хватит ну или написать фабрику.
Но конечно всё это не очень удобно, и в этом основной минус фортрана - как только хочется что-нибудь сделать красиво - язык не пускает :(
Оставить комментарий
Имя или ник:
Комментарий: