Cómo obtener una matriz desconocida anteriormente como la salida de una función en Fortran

En Python :

def select(x): y = [] for e in x: if e!=0: y.append(e) return y 

que funciona como:

 x = [1,0,2,0,0,3]
 seleccionar (x)
 [1,2,3]

Para ser traducido a 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 

Las preguntas están en Fortran:

  1. ¿Cómo declarar y (?) ?
  2. Cómo declarar valores predefinidos para x
  3. cómo evitar información de dimensión n

para 1 si se define como y (n) la salida será:

 x = (/ 1,0,2,0,0,3 /)
 imprimir *, seleccionar (x, 6)
 1,2,3,0,0,0

Que no se desea!
! ——————————-
Comentarios:
1- Todas las respuestas dadas son útiles en esta publicación. Especialmente MSB y eryksun’s.
2- Traté de adaptar las ideas para mi problema y comstackr con F2Py sin embargo, no tuvo éxito. Ya los había depurado usando GFortran y todos fueron exitosos. Puede que sea un error en F2Py o algo que no conozco sobre cómo usarlo correctamente. Intentaré cubrir este tema en otro post.

Actualización: una pregunta enlazada se puede encontrar aquí .

Espero que venga un verdadero progtwigdor de Fortran, pero en ausencia de un mejor consejo, solo especificaría la forma y no el tamaño de x(:) , usaría una matriz temp(size(x)) , y haría la salida y allocatable Luego, después de la primera pasada, allocate(y(j)) y copie los valores de la matriz temporal. Pero no puedo enfatizar lo suficiente como para no ser un progtwigdor de Fortran, así que no puedo decir si el lenguaje tiene una matriz que se puede cultivar o si existe una biblioteca para este último.

 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 

Editar:

Basado en la respuesta de MSB, aquí hay una versión revisada de la función que crece temporalmente con la asignación excesiva. Como antes, copia el resultado a y al final. Resulta que no es necesario asignar explícitamente una nueva matriz en el tamaño final. En su lugar, se puede hacer automáticamente con la asignación.

  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 

Este es un ejemplo de una función de Fortran que devuelve una matriz de longitud variable. Esta es una característica de Fortran 2003. También se usa en el controlador de prueba la asignación automática en la asignación, otra característica de 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 

Aquí hay una solución alternativa que usa una matriz temporal para “hacer crecer” la matriz de salida (función de retorno) según sea necesario. Mientras se evitan dos pasadas a través de la matriz de entrada, se requieren copias de la matriz. Otra característica de Fortran 2003, move_alloc, reduce el número de copias necesarias. move_alloc también se encarga de la (re) asignación de la matriz de salida (aquí “y”) y la desasignación de la matriz de entrada (aquí “temp”). Quizás esto sea más elegante, pero probablemente sea menos eficiente, ya que se utilizan varias copias. Esta versión es probablemente más educativa que útil. La versión de @ eryksun usa una pasada y una copia, a costa de hacer que la matriz temporal sea de tamaño completo.

 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 

Si el ejemplo de su pregunta es realmente lo que quiere hacer, puede usar el `paquete ‘intrínseco de 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 

La salida del progtwig de ejemplo es: 1 2 3