Fortran 纯函数中的指针

Fortran 纯函数中的指针,fortran,gfortran,intel-fortran,Fortran,Gfortran,Intel Fortran,为了在Fortran中遍历链表,我使用了一个指向当前元素的指针,该元素被移动到循环中的下一个元素。试图在对所述链表进行操作的纯函数中应用此函数会导致错误 例如: module list implicit none ! Node type n_list integer :: val type(n_list),pointer :: next => NULL() end type ! Linked list type t_

为了在Fortran中遍历链表,我使用了一个指向当前元素的指针,该元素被移动到循环中的下一个元素。试图在对所述链表进行操作的
函数中应用此函数会导致错误

例如:

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
导致

    cur => list%head
         1
Error: Bad target in pointer assignment in PURE procedure at (1)
我知道错误/警告背后的基本原理,并且在使用指针时很难确保函数的参数不改变(Fortran 2008,第12.7章“纯过程”,特别是C1283)。但是,在这种情况下,
list
永远不会更改


有没有可能告诉编译器(
ifort
gfortran
)没有违反
intent(in)
的要求?

我找到了一个使用
递归
函数的解决方案,该函数至少符合标准。它既不优雅也不快速,并且受到堆栈深度的限制,但它正在工作。我会把它作为一个答案,虽然我希望有人有更好的解决办法

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

您遇到的约束(C1283)的相关部分是

在纯子程序中,具有基本对象的任何指示符,即。。具有INTENT(IN)属性的伪参数。。不得使用

  • 作为指针赋值stmt中的数据目标

下面的注释说明了约束描述的动机

上述约束旨在确保纯程序没有副作用

你想说的是“我保证没有副作用,我们不需要限制”。对于这种保证,约束是足够的,但不是必需的,您可以很好地分析代码

然而,一个合格的处理器/编译器必须能够检测到违反约束的情况,这不仅仅是约束的总体目标,因此您不需要说“这是纯的”,而且“我不需要被告知违反C1283”。对于编译器供应商来说,这似乎是一个很大的努力,但收效甚微

那么,我猜答案是“不”:没有编译代码的方法。这并不是决定性的,因为我们实际上已经进入了特定于实现的领域。您特别询问了gfortran和ifort,因此“使用
-nocheck c1283
”反驳了我的“答案”

现在,如果有一个选项,你可以选择“相信我”(和非标准Fortran)。那么,我们还是去那里吧。只是我们要撒谎。像往常一样,接口块将是我们的手段

module list_mod
  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

  interface
    pure logical function in_list(list, val)
      import t_list
      class(t_list), intent(in) :: list
      integer, intent(in) :: val
    end function
  end interface

end module

! Interface mismatch in the external function
function in_list(list, val) result(res)
  use list_mod, only : t_list, n_list
  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

  use list_mod
  type(t_list) testlist
  type(n_list), pointer :: ptr
  integer i
  logical :: res(5) = .FALSE.

  allocate(testlist%head)
  ptr => testlist%head

  do i=1,5
    allocate(ptr%next)
    ptr => ptr%next
    ptr%val = i
  end do

  ! in_list is pure, isn't it?
  forall(i=1:5:2) res(i)=in_list(testlist,i)
  print*, res
end
这是纯粹的肮脏和限制:你不再有一个模块过程;你不符合标准;编译器可能很聪明,可以检查接口(即使它不需要)。如果编译器因此恨你,你只能怪你自己


最后,要获得过程的纯
pure

需要付出相当多的努力。好的,我找到了一个使用
传输
内在函数的解决方案。其主要思想是克隆列表结构(没有数据,我选中了),并使用指向第一个节点(未更改)的指针作为起始值。是的,这是一个循环孔,但是
ifort
gfortran
都接受这个,没有任何警告

module list_mod
  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 getHead(list) result(res)
    implicit none
    class(t_list),intent(in)  :: list
    type(n_list),pointer      :: res
    type(t_list),pointer      :: listPtr

    ! Create a copy of pointer to the list struct
    allocate( listPtr )
    listPtr = transfer( list, listPtr )

    ! Set the pointer
    res => listPtr%head

    ! Free memory
    deallocate( listPtr )
  end function

  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 => getHead(list)
    do while ( associated(cur) )
      if ( cur%val == val ) return
      cur => cur%next
    enddo

    ! Not found
    res = .false.
  end function

end module

program test
  use list_mod
  implicit none
  integer,parameter     :: MAXELEM = 10000000
  integer               :: i
  type(t_list)          :: list
  type(n_list),pointer  :: cur

  ! Fill list
  list%head => NULL()
  allocate( list%head )
  list%head%val = 1

  cur => list%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( list, MAXELEM/2 )
  print *,'is MAXELEM+1 in list? ', in_list( list, MAXELEM+1 )
end program

是的,当然。。。我抄错了行;-)谢谢我不知道答案,但如果我猜“不”,你能用接口块撒谎吗?似乎可以在不匹配的外部函数周围使用接口块来获得理想的效果。这很难看,也有局限性,但有一个优势,那就是你显然在做一些你知道是错误的事情(比一个神秘的编译器标志要求忽略约束检查更糟糕)。@francescalus你能把它作为一个解决方案吗?它比使用递归函数(factor~5)工作得更好,并且对于大型列表没有显示故障!我想您可以删除
pure
属性。如果您有一个最新的编译器,您甚至可以用父类型的可分配项替换类型的指针组件。可能值得尝试说服编译器进行尾部调用消除。也许将_list_节点更改为子例程?是的,它不执行尾部调用,因为某些
class.0={v}{CLOBBER}。不知道这是什么,但它与多态性假人有关。最后一行是一个一般性警告:我确信asker有足够的经验来确定
pure
属性的值,但我不能低估这种方法的邪恶程度。它可能是邪恶的,但它起到了作用;-)事实上,我可以从一个模块过程中调用外部函数,所以我仍然有这个好处。顺便说一句:
gfortran
发出警告(“警告:全局过程中的接口不匹配…”),
ifort
没有注意到任何事情…很抱歉取消接受您的回答,但我发现了一个更好的解决方案,使用
传输
内部…@高性能标记很好,更适合我的任务。。。在符合标准方面不是更好。这确实是对编译器检查的规避,因为也不允许仅复制节点。我会毫不犹豫地称它为标准一致的解决方案,我会认为它与弗朗西斯卡尔斯的解决方案处于同一水平。@ VLADIMILF是真的,但是我不需要一个具有错误匹配接口的外部函数,并且我把所有的东西都包含在一个模块中。由于代码段用于模板(任意列表),这是一个巨大的改进。我看到,在我放弃的列表中,我不仅没有使用纯过程,而且在其中一些过程中也没有指定意图。