j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
178 stars 15 forks source link

foreach loop #116

Open Leonard-Reuter opened 4 years ago

Leonard-Reuter commented 4 years ago

Allow for foreach loops: eg.

do element in array
    write(*,*) element
end do

instead of

do i=1, SIZE(array)
    write(*,*) array(i)
end do
gronki commented 4 years ago

It is not even Fortran???

pon., 16 gru 2019, 13:58 użytkownik Libavius notifications@github.com napisał:

Allow for foreach loops: eg.

for element in array: write(,) element

instead of

for i in SIZE(array): write(,) array(i)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/j3-fortran/fortran_proposals/issues/116?email_source=notifications&email_token=AC4NA3LCABQCS7HGQ4E4B33QY53PLA5CNFSM4J3JQTG2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IAXI7SA, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC4NA3OFU6MV7ACITOFIL3TQY53PLANCNFSM4J3JQTGQ .

Leonard-Reuter commented 4 years ago

It is not even Fortran???

I was a bit distracted when I wrote it. However, I edited it after max. 5 mins, which is 2 hours before you commented =/.

jacobwilliams commented 4 years ago

PSA: edits are not reflected in the email notifications. If someone is only viewing these via email, they never see any edits. :)

certik commented 4 years ago

That is an interesting idea (probably the final : is not needed in the syntax?). Because Fortran's syntax for iterating over arrays is so simple, I usually like to have an index i, rather than the element itself, because typically (but not always) I want to do more with the matrix than just access the element. Also it generalizes nicely to multidimensional arrays, which the above syntax does not (I think).

Leonard-Reuter commented 4 years ago

That is an interesting idea (probably the final : is not needed in the syntax?). Because Fortran's syntax for iterating over arrays is so simple, I usually like to have an index i, rather than the element itself, because typically (but not always) I want to do more with the matrix than just access the element. Also it generalizes nicely to multidimensional arrays, which the above syntax does not (I think).

Jup, I deleted the :.

I sometimes prefer the foreach loop, if I have some kind of nested object oriented code: ie:

type :: Molecule_t
    type(Atom_t), allocatable :: atoms(:)
end type Molecule_t

And later:

subroutine Foo(molecules)
    type(Molecule_t), intent(inout) :: molecules(:)
    integer :: i, j

    do i=1, SIZE(molecules)
        do j=1, SIZE(molecules(i)%atoms)
            call molecules(i)%atoms(j)%Do_something()
        end do
    end do
end subroutine Foo

Would reduce to (probably the variables molecule and atom are best off as pointers?):

subroutine Foo(molecules)
    type(Molecule_t), intent(inout) :: molecules(:)
    type(Molecule_t), pointer :: molecule
    type(Atom_t), pointer :: atom

    do molecule in molecules
        do atom in molecule%atoms
            call atom%Do_something()
        end do
    end do
end subroutine Foo
certik commented 4 years ago

@Libavius thanks for the use case.

Leonard-Reuter commented 4 years ago

@Libavius thanks for the use case.

It could analogoulsy work for multidimensional arrays: (Note that the slicing of an array is determined by the way it is written in the memory.)

subroutine Foo(tensor)
    real(real64), intent(inout) :: tensor(:,:,:)
    real(real64), pointer :: matrix(:,:), vector(:), scalar

    do matrix in tensor
        do vector in matrix
            do scalar in vector
                call Do_something(scalar)
            end do
        end do
    end do
end subroutine Foo

However, this is not really advantageous compared to the obvious way to do it.

Leonard-Reuter commented 4 years ago

This could also enable some pythonic 'list' (here: array) comprehension:

type(Atom_t) :: atoms(n)
integer :: charges(n)

charges = [(atom%Get_charge(), atom in atoms)]

instead of:

integer :: i
type(Atom_t) :: atoms(n)
integer :: charges(n)

charges = [(atoms(i)%Get_charge(), i=1, n)]

EDIT: it is actually easier, if Get_charge ist elemental:

type(Atom_t) :: atoms(n)
integer :: charges(n)

charges = atoms%Get_charge()
jacobwilliams commented 4 years ago

Also, how about adding Python iterator like capability to a derived type? That would be very interesting.

arjenmarkus commented 4 years ago

There is the proposal on coroutines and iterators - [https://j3-fortran.org/doc/year/19/19-169.pdf] (see #60). That would definitely cover the kind of loop that is being discussed here.