Как получить ранее неизвестный массив как вывод функции в Фортране - PullRequest
15 голосов
/ 25 ноября 2011

В 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. как объявить у (?) ?
  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- Все приведенные ответы полезны в этом посте. Специально М.С.Б. и Эриксун'с.
2- Я пытался адаптировать идеи для моей проблемы и компилировать с F2Py, но это не увенчалось успехом. Я уже отлаживал их с помощью GFortran, и все они были успешными. Это может быть ошибка в F2Py или что-то, чего я не знаю об ее правильном использовании. Я постараюсь осветить эту проблему в другом посте.

Обновление: Связанный вопрос можно найти по адресу здесь .

Ответы [ 3 ]

14 голосов
/ 25 ноября 2011

Я надеюсь, что придет настоящий программист на Фортране, но в отсутствие лучшего совета я бы только указал форму, а не размер x(:), использовал бы временный массив temp(size(x)) и сделал бы вывод y allocatable. Затем после первого прохода allocate(y(j)) и скопируйте значения из временного массива. Но я не могу не подчеркнуть, что я не программист на Фортране, поэтому я не могу сказать, имеет ли язык расширяемый массив или существует библиотека для последнего.

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

Редактировать:

Основываясь на ответе М.С.Б., вот пересмотренная версия функции, которая увеличивается 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
12 голосов
/ 25 ноября 2011

Вот пример функции 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 также заботится о (пере) распределении выходного массива (здесь «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
5 голосов
/ 25 ноября 2011

Если пример в вашем вопросе действительно является тем, что вы хотите сделать, вы можете использовать встроенный пакет '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

...