Как получить ранее не объявленный массив как выход функции в Fortran
В Python:
def select(x):
y = []
for e in x:
if e!=0:
y.append(e)
return y
который работает как:
x = [1,0,2,0,0,3]
select(x)
[1,2,3]
для перевода в Fortran:
function select(x,n) result(y)
implicit none
integer:: x(n),n,i,j,y(?)
j = 0
do i=1,n
if (x(i)/=0) then
j = j+1
y(j) = x(i)
endif
enddo
end function
Вопросы находятся в Фортране:
1- как объявить y (?)?
2- как объявить предопределенные значения для x
3- как избежать информации о размере n
для 1, если он определен как y (n), выход будет:
x = (/1,0,2,0,0,3/)
print *,select(x,6)
1,2,3,0,0,0
что нежелательно!
! -------------------------------
Комментарии:
1 -. Все приведенные ответы полезны в этом сообщении. Специально M.S.B и eryksun's.
2 - Я попытался адаптировать идеи для своей проблемы и скомпилировать с помощью F2Py
, однако это не удалось. Я уже отлаживал их с помощью GFortran, и все они были успешными. Это может быть ошибка в F2Py
или что-то, что я не знаю об использовании его должным образом. Я попытаюсь рассмотреть этот вопрос в другом сообщении.
Update:
Связанный вопрос можно найти на здесь.
Ответы
Ответ 1
Надеюсь, что появился настоящий программист из Fortran, но в отсутствие лучшего совета я бы указал только размер, а не размер x(:)
, использовать временный массив temp(size(x))
и сделать вывод y allocatable
. Затем после первого прохода allocate(y(j))
и скопируйте значения из временного массива. Но я не могу подчеркнуть, что я не программист из Fortran, поэтому не могу сказать, имеет ли язык растущий массив или существует библиотека для последнего.
program test
implicit none
integer:: x(10) = (/1,0,2,0,3,0,4,0,5,0/)
print "(10I2.1)", select(x)
contains
function select(x) result(y)
implicit none
integer, intent(in):: x(:)
integer:: i, j, temp(size(x))
integer, allocatable:: y(:)
j = 0
do i = 1, size(x)
if (x(i) /= 0) then
j = j + 1
temp(j) = x(i)
endif
enddo
allocate(y(j))
y = temp(:j)
end function select
end program test
Изменить:
Основано на M.S.B. ответ, здесь пересмотренная версия функции, которая увеличивает temp y
с перераспределением. Как и раньше, он копирует результат в y в конце. Оказывается, мне не нужно явно выделять новый массив в конечном размере. Вместо этого это можно сделать автоматически с назначением.
function select(x) result(y)
implicit none
integer, intent(in):: x(:)
integer:: i, j, dsize
integer, allocatable:: temp(:), y(:)
dsize = 0; allocate(y(0))
j = 0
do i = 1, size(x)
if (x(i) /= 0) then
j = j + 1
if (j >= dsize) then !grow y using temp
dsize = j + j / 8 + 8
allocate(temp(dsize))
temp(:size(y)) = y
call move_alloc(temp, y) !temp gets deallocated
endif
y(j) = x(i)
endif
enddo
y = y(:j)
end function select
Ответ 2
Вот пример функции Fortran, возвращающей массив переменной длины. Это особенность Fortran 2003. Также в тестовом драйвере используется автоматическое распределение при назначении, другая функция Fortran 2003.
module my_subs
contains
function select(x) result(y)
implicit none
integer, dimension (:), intent (in) :: x
integer, dimension (:), allocatable :: y
integer :: i, j
j = 0
do i=1, size (x)
if (x(i)/=0) j = j+1
enddo
allocate ( y (1:j) )
j = 0
do i=1, size (x)
if (x(i)/=0) then
j = j+1
y(j) = x(i)
endif
enddo
return
end function select
end module my_subs
program test
use my_subs
implicit none
integer, dimension (6) :: array = [ 5, 0, 3, 0, 6, 1 ]
integer, dimension (:), allocatable :: answer
answer = select (array)
write (*, *) size (array), size (answer)
write (*, *) array
write (*, *) answer
stop
end program test
Вот альтернативное решение, которое использует временный массив для "увеличения" выходного массива (возврат функции) по мере необходимости. В то время как два прохода через входной массив избегаются, требуются копии массива. Другая функция Fortran 2003, move_alloc, уменьшает количество необходимых копий. move_alloc также заботится о (re) распределении выходного массива (здесь "y" ) и освобождении входного массива (здесь "temp" ). Возможно, это более элегантно, но, вероятно, менее эффективно, поскольку используются несколько копий. Эта версия, вероятно, более образовательная, а затем полезная. Версия @eryksun использует один проход и одну копию за счет создания временного массива в полном размере.
function select(x) result(y)
implicit none
integer, dimension (:), intent (in) :: x
integer, dimension (:), allocatable :: y, temp
integer :: i, j
j = 0
do i=1, size (x)
if (x(i)/=0) then
j = j+1
allocate (temp (1:j))
if ( allocated (y) ) temp (1:j-1) = y
call move_alloc (temp, y)
y(j) = x(i)
endif
enddo
return
end function select
Ответ 3
Если пример вашего вопроса действительно является тем, что вы хотите сделать, вы можете использовать встроенный "пакет" Fortran90:
program pack_example
implicit none
integer, dimension(6) :: x
x = (/ 1,0,2,0,0,3 /)
! you can also use other masks than 'x/=0'
write(*,*) pack(x, x/=0)
end program pack_example
Вывод примерной программы: 1 2 3