2015-04-16 2 views
4

Чтобы переправить связанный список в Fortran, я использую указатель на текущий элемент, который перемещается в следующий внутри цикла. Попытка применить это внутри функции pure, которая работает с указанным связанным списком, приводит к ошибке.Указатели в чистых функциях

Пример:

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) не нарушен?

+0

Вы имеете в виду C1283? – francescalus

+0

Да, конечно ... Я скопировал неправильную строку ;-) Спасибо! –

+0

Я понятия не имею о ответе, но если я думаю «нет», не могли бы вы лежать с блоком интерфейса? – francescalus

ответ

1

ОК, я нашел решение, используя встроенный transfer. Основная идея - клонировать структуру списка (без данных, я проверил) и использовать указатель на первый узел (неизменный) в качестве начального значения. Да, это петля, но оба 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 
+0

Это действительно обход проверки компилятора, потому что просто копирование узла также запрещено. Я бы не стал называть это стандартным подходящим решением, я бы счел, что он находится на том же уровне, что и решение francescalus. –

+0

@ VladimirF Правда, но мне не нужна внешняя функция с неправильным совпадением интерфейса, и я сохраняю все, что содержится в одном модуле. Поскольку фрагмент используется для шаблонов (произвольные списки), это огромное улучшение. –

+0

Я вижу, что в моих списках я сдался, и не только не использовал чистые процедуры, но и некоторые из них не указывали намерения. –

5

Я нашел решение с использованием recursive функций, которые по меньшей мере соответствуют стандарту. Он не является ни элегантным, ни быстрым, и он ограничен глубиной стека, но он работает. Я отправлю его в качестве ответа, хотя я надеюсь, что некоторые-один имеет лучшее решение ...

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 
+0

Возможно, стоит попытаться убедить компилятор выполнить удаление хвостового вызова. Возможно, измените in_list_node на подпрограмму? –

+0

@ VladimirF Я пробовал это, но никакой разницы ... –

+0

Да, он не выполняет хвостовой вызов из-за некоторого 'class.0 = {v} {CLOBBER};'. Не знаю, что это такое, но оно связано с полиморфным манекеном. –

4

Соответствующая часть ограничения вы натыкаться (C1283) является

В чистая подпрограмма любой целеуказатель с базовым объектом, который является .. фиктивным аргумент с НАМЕРЕНИЯМИ (ВО) атрибут .. не должен использоваться

  • ..

  • в качестве данных-мишени в указатель присваивания-зЬтЬ

В записке ниже этого описания ограничений побуждает его

Вышеуказанные ограничения разработаны, чтобы гарантировать, что чистый процедура от свободного побочные эффекты

Что вы хотите сказать: «Я гарантирую, что побочных эффектов нет, нам не нужны ограничения для этого». Ограничений достаточно, но не обязательно для этой гарантии, и вы можете хорошо проанализировать свой код.

Однако соответствующий процессор/компилятор должен быть состоянии обнаружить нарушения ограничений не только общая цель ограничений, так что вам не нужно просто сказать «это pure действительно», но также «и я не нужно рассказывать о нарушениях 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.

+1

Последняя строка - это общее предупреждение: я уверен, что у меня достаточно опыта, чтобы определить значение атрибута 'pure', но я не могу понять, насколько злой этот подход. – francescalus

+0

Это может быть зло, но он выполняет эту работу ;-) На самом деле, я мог бы использовать внешнюю функцию из модульной процедуры, поэтому я все еще получаю эту выгоду. Кстати: 'gfortran' выдает предупреждение (*« Предупреждение: несоответствие интерфейса в глобальной процедуре ... »*),' ifort' не замечает ничего ... –

+0

Извините, что не принимаю ваш ответ, но я нашел лучшее решение, используя встроенный 'transfer' ... –

Смежные вопросы