springy
|
Energizer
|
|
|
|
Рег.: 10.01.2004
|
Сообщений: 366
|
|
Рейтинг: 29
|
|
[fortran] вопрос знатокам
13.02.2010 13:43
|
|
|
Я хочу написать функцию, которая на вход получает массив и размерности, выделяет память allocate-ом, и возвращает массив. Причем функция должна уметь выделять память для массивов разного ранга. По существу мне нужна просто обертка для allocate, которая будет еще кое-что делать.
В Fortran 2003 есть возможность передавать allocatable объекты как параметры. Но проблема в том, что мне приходится указывать число размерностей массива в интерфейсе функции, поэтому она работает только для массивов определеного ранга.
Код, приведенный ниже, не компилируется, так как subroutine memory на вход принимает массив ранга 1, а я ему даю массив ранга 3.
Можно ли как-нибудь все-таки такую функцию реализовать? Писать отдельно функции для массивов ранга 1, 2, 3 и т.д. как-то не хочется...
Спасибо!
code: 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
code: 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)
|
|
epsilon
|
enthusiast
|
|
|
|
Рег.: 01.04.2007
|
Сообщений: 376
|
|
Рейтинг: 411
|
|
Re: [fortran] вопрос знатокам
[re: springy]
13.02.2010 17:23
|
|
|
как вариант, можно хранить массив ранга 1 и по мере необходимости работать с ним как с 3-х мерным, воспользовавшись фичей 2003 фортрана 'pointer rank remapping' (правда gfortran (4.4.1) и ifort (11.1.038) ее не поддерживают).
code:
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
|
|
Forest
|
Carpal Tunnel
|
|
|
|
Рег.: 29.08.2002
|
Сообщений: 11597
|
|
Рейтинг: 795
|
|
Re: [fortran] вопрос знатокам
[re: springy]
13.02.2010 22:41
|
|
|
В ответ на:
Но проблема в том, что мне приходится указывать число размерностей массива в интерфейсе функции, поэтому она работает только для массивов определеного ранга.
А что мешает передать размерность массивом?
|
|
springy
|
Energizer
|
|
|
|
Рег.: 10.01.2004
|
Сообщений: 366
|
|
Рейтинг: 29
|
|
Re: [fortran] вопрос знатокам
[re: Forest]
14.02.2010 00:42
|
|
|
Ну вот а как это сделать?.. Я так понимаю, что этот массив должен быть описан как deferred shape, т.е. a(:,...,:), потому что он allocatable. Т.е. про него известен только ранг, а как его передать параметром?
|
|
Forest
|
Carpal Tunnel
|
|
|
|
Рег.: 29.08.2002
|
Сообщений: 11597
|
|
Рейтинг: 795
|
|
Re: [fortran] вопрос знатокам
[re: springy]
17.02.2010 18:26
|
|
|
Самый простой вариант передать все явно: ранг, массив размерностей размера ранг (это если все размерности начинаются с 1 и того же числа - если надо использовать отрезки - немного сложнее) и сам массив. Что-то вроде такого: code: subroutine memory(array, length, range)
implicit none
integer, intent(in) :: range, length(range)
...
|
|
springy
|
Energizer
|
|
|
|
Рег.: 10.01.2004
|
Сообщений: 366
|
|
Рейтинг: 29
|
|
Re: [fortran] вопрос знатокам
[re: Forest]
17.02.2010 22:28
|
|
|
Я не понимаю, array в этом случае как описать?
Вот так не получится, потому что allocatable массив array должен быть описан как deferred shape code:
subroutine memory(array, dims)
integer, intent(in) :: dims(:)
real(8), intent(inout), allocatable :: array(dims)
code: 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
|
|
Forest
|
Carpal Tunnel
|
|
|
|
Рег.: 29.08.2002
|
Сообщений: 11597
|
|
Рейтинг: 795
|
|
Re: [fortran] вопрос знатокам
[re: springy]
18.02.2010 20:56
|
|
|
Действительно, с рангом массива сложнее - сконструировать не проблема, но вот описать такой параметр?.. Так сразу непонятно как. Имхо это одна из недоделок фортрана, но мб и можно извернуться через символьные константы (описывая ими сечения, но у меня не получилось, а мб и нельзя).
Как вариант - можно действительно все делать одномерными массивами (как тут уже и советовали). Тогда в минимальном варианте надо будет лишь написать свой метод доступа к ячейке (но он будет конечно не такой же универсальный, как и в стандарте, но мб и хватит), ну или написать фабрику.
Но конечно все это не очень удобно, и в этом основной минус фортрана - как только хочется что-нибудь сделать красиво - язык не пускает 
|
|
|
|