Range Over Iterators in Fortran
Starting with version 1.23, Go has added support for iterators, which lets us range over pretty much anything!
Let’s look at the List
type from the previous example again. In that example we had an AllElements
method that returned a slice of all elements in the list. With Go iterators, we can do it better - as shown below.
module ListModule
implicit none
type :: element
integer :: val
type(element), pointer :: next => null()
end type element
type :: List
type(element), pointer :: head => null(), tail => null()
contains
procedure, pass(this) :: Push => PushElement
procedure, pass(this) :: All => AllElements
end type List
contains
subroutine PushElement(this, v)
class(List), intent(inout) :: this
integer, intent(in) :: v
type(element), pointer :: newElement
allocate(newElement)
newElement%val = v
newElement%next => null()
if (.not. associated(this%tail)) then
this%head => newElement
this%tail => newElement
else
this%tail%next => newElement
this%tail => newElement
end if
end subroutine PushElement
subroutine AllElements(this, yield)
class(List), intent(inout) :: this
logical, external :: yield
type(element), pointer :: e
e => this%head
do while (associated(e))
if(.not. yield(e%val)) return
e => e%next
end do
end subroutine AllElements
end module ListModule
All returns an iterator, which in Fortran can be represented with a multitasking technique, such as a subroutine that takes another function as a parameter.
The iterator function takes another function as a parameter, called yield
by convention. It will call yield
for every element we want to iterate over, and note yield
’s return value for a potential early termination.
Iteration doesn’t require an underlying data structure, and doesn’t even have to be finite! Here’s a function returning an iterator over Fibonacci numbers: it keeps running as long as yield
keeps returning true
.
subroutine genFib(yield)
logical, external :: yield
integer :: a, b, t
a = 1
b = 1
do
if(.not. yield(a)) return
t = a + b
a = b
b = t
end do
end subroutine genFib
program main
use :: ListModule
implicit none
type(List) :: lst
logical :: yieldIterator
lst%Push(10)
lst%Push(13)
lst%Push(23)
call lst%All(yieldIterator)
end program
logical function yieldIterator(val)
integer, intent(in) :: val
print *, val
yieldIterator = .true.
end function
program main
implicit none
logical :: yieldFib
call genFib(yieldFib)
end program
logical function yieldFib(val)
integer, intent(in) :: val
if(val >= 10) then
yieldFib = .false.
else
print *, val
yieldFib = .true.
end if
end function
The output would be:
10
13
23
1
1
2
3
5
8
Now that we can iterate over list elements and generate Fibonacci numbers iteratively in Fortran, let’s learn more about the language.