In order to traverse a linked list in Fortran, I use a pointer to the current element that is moved to the next one inside a loop. Trying to apply this inside a pure
function that operates on said linked list results in an error.
Example:
module list
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
contains
pure function in_list( list, val ) result(res)
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
type(n_list),pointer :: cur
res = .true.
! Traverse the list
cur => list%head
do while ( associated(cur) )
if ( cur%val == val ) return
cur => cur%next
enddo
! Not found
res = .false.
end function
end module
Results in
cur => list%head
1
Error: Bad target in pointer assignment in PURE procedure at (1)
I am aware of the rationale behind the error/warning, and that it is difficult to ensure that the arguments of the function are not changed when using pointers (Fortran 2008, ch. 12.7 "Pure procedures", esp. C1283). In this case, though, list
is never changed.
Is it possible to tell the compiler (ifort
and gfortran
) that intent(in)
is not violated?
A function is called pure function if it always returns the same result for same argument values and it has no side effects like modifying an argument (or global variable) or outputting something. The only result of calling a pure function is the return value. Examples of pure functions are strlen(), pow(), sqrt() etc.
A function must pass two tests to be considered “pure”: Same inputs always return same outputs. No side-effects.
Pure functions don't modify their input. They treat the input values as immutable. An immutable value is a value that, once created, cannot be changed.
In computer programming, a pure function is a function that has the following properties: the function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams), and.
I have found a solution using recursive
functions that is at least Standard conforming. It is neither elegant nor fast, and is limited be the stack depth, but it is working. I'll post it as an answer, although I hope some-one has a better solution...
module list
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
contains
pure function in_list( list, val ) result(res)
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
if ( associated(list%head) ) then
res = in_list_node( list%head, val )
else
res = .false.
endif
end function
recursive pure function in_list_node( node, val ) result(res)
implicit none
class(n_list),intent(in) :: node
integer,intent(in) :: val
logical :: res
if ( node%val == val ) then
res = .true.
elseif ( associated(node%next) ) then
! Recurse
res = in_list_node( node%next, val )
else
res = .false.
endif
end function
end module
program test
use list
implicit none
integer,parameter :: MAXELEM = 100000
integer :: i
type(t_list) :: lst
type(n_list),pointer :: cur
! Fill list
lst%head => NULL()
allocate( lst%head )
lst%head%val = 1
cur => lst%head
do i=2,MAXELEM
allocate( cur%next )
cur%next%val = i
cur => cur%next
enddo !i
print *,'is MAXELEM/2 in list? ', in_list( lst, MAXELEM/2 )
print *,'is MAXELEM+1 in list? ', in_list( lst, MAXELEM+1 )
end program
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With