Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get priorly-unknown array as the output of a function in Fortran

In Python:

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

that works as:

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

to be translated into 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

The questions are in Fortran:

  1. how to declare y(?)?
  2. how to declare predefined values for x
  3. how to avoid dimension info n

for 1 if it is defined as y(n) the output will be:

x = (/1,0,2,0,0,3/)
print *,select(x,6)
1,2,3,0,0,0

which is not desired!
!-------------------------------
Comments:
1- All given answers are useful in this post. Specially M.S.B and eryksun's.
2- I tried to adapt the ideas for my problem and compile with F2Py however it was not successful. I had already debugged them using GFortran and all were successful. It might be a bug in F2Py or something that I don't know about using it properly. I will try to cover this issue in another post.

Update: A linked question could be found at here.

like image 544
Developer Avatar asked Nov 25 '11 02:11

Developer


People also ask

How do I pass an array to a function in Fortran?

Most FORTRAN compilers pass arrays by passing the address of the array, in this case all subprograms that use the array will then work on the same (and only) copy. The FORTRAN standard allows array passing by another method called copying in/out or copy/restore.

Can a Fortran function return an array?

Unlike C and C++, Fortran can handle returning arrays and composite types with no difficulty.


1 Answers

I hope a real Fortran programmer comes along, but in the absence of better advice, I would only specify the shape and not the size of x(:), use a temporary array temp(size(x)), and make the output y allocatable. Then after the first pass, allocate(y(j)) and copy the values from the temporary array. But I can't stress enough that I'm not a Fortran programmer, so I can't say if the language has a growable array or if a library exists for the latter.

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

Edit:

Based on M.S.B.'s answer, here's a revised version of the function that grows temp y with over-allocation. As before it copies the result to y at the end. It turns out i's not necessary to explicitly allocate a new array at the final size. Instead it can be done automatically with assignment.

    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
like image 133
Eryk Sun Avatar answered Oct 20 '22 22:10

Eryk Sun