универсальная функция карты в Фортране - PullRequest
0 голосов
/ 24 декабря 2018

Моя конечная цель состоит в том, чтобы иметь универсальную функцию отображения на Фортране, т.е. функцию, которая принимает массив произвольного типа A и функцию типа A-> B, применяет эту функцию ко всем элементам данного массива и возвращаетмассив типа B. Я не смог реализовать это с массивом, поэтому я решил начать с одного элемента, но даже это не работает.

Вот моя попытка:

program main
  integer :: elem_int
  elem_int = 1
  elem_int = to_int(apply_func(elem_int, add_one_int))
  print *, elem_int

contains
  ! don't know any other way to cast class(*) to int
  function to_int(unbound) result(res) 
    class(*), intent(in) :: unbound
    integer :: res
    select type (unbound)
      type is (integer)
        res = unbound
    end select
  end function

  function apply_func(elem, func) result(new_elem)
    class(*) :: elem
    class(*) :: func
    class(*), allocatable :: new_elem
    ! not sure if this allocation is needed
    allocate(new_elem, source = elem) 
    new_elem = func(elem)
  end function

  function add_one_int(num) result(res)
    integer :: num
    integer :: res
    res = num + 1
  end function
end program

Этот код компилируется, но завершается с ошибкой сегментации в строке

new_elem = func(elem)

Я подумал, может быть, он думает, что func является массивом, и пытается проиндексировать его, поэтому я попытался определить абстрактный интерфейс следующим образом:

  abstract interface
    function any_func(x)
      class(*) :: x
      class(*), allocatable :: any_func
    end function
  end interface

И изменил объявление func на procedure(any_func), но затем мой компилятор (ifort 18.0.1) выдает следующую ошибку:

error #7069: The characteristics of the associated actual function result differ from the characteristics of the dummy function result. [ADD_ONE_INT]

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

Ответы [ 2 ]

0 голосов
/ 25 декабря 2018

Предстоящий стандарт Fortran 2018 включает новую конструкцию select_rank, которая обеспечит большую гибкость для кодирования процедур, не зависящих от ранга, без ограничений elemental процедур.До сегодняшнего дня (конец 2018 года) этот стандарт был официально опубликован ISO, поэтому может потребоваться некоторое время, прежде чем он будет реализован поставщиками.


Просто ради пустяковэто ошибка в gfortran 8.2.0, которая позволяет записать своего рода подпрограмму map в Fortran с использованием assumed-rank фиктивного аргумента и без select_rank (или определенных подпрограмм для каждого ранга).

Важное примечание: Я НЕ РЕКОМЕНДУЮ использовать этот фрагмент для производственного кода.Я решил опубликовать его только для того, чтобы оценить фортранов, которые могут достичь этого поста, в надежде, что это поможет лучше понять язык и, возможно, вдохновит на некоторые идеи. самоответ @ DartLenin - правильный способ сделать это в Фортране.

module maps
  implicit none

  abstract interface
    integer function unary_int(x)
      integer :: x
    end function
    ! different interfaces would be needed for other arities an types
  end interface

  interface map
    procedure :: map_unary_int  ! + overloads for other implementations 
  end interface

contains
  subroutine map_unary_int(f, x)
    procedure(unary_int) :: f
    integer :: x(..)
    call apply_flatten(x)  ! <- there is the bug: assumed-rank variable shouldn't 
                           ! be allowed as actual argument to assumed-size

  contains
    subroutine apply_flatten(x_)
      integer :: x_(size(x)), i
      x_(1:size(x)) = [(f(x_(i)), i=1, size(x))]
    end
  end
end

program main
  use :: maps
  implicit none

  integer :: int_scl = 0, int_1(1) = 1, int_2x2(2, 2) = 2

  call map(add1, int_scl)
  print *, int_scl, "sh:", shape(int_scl)  ! prints: 1 sh:

  call map(add1, int_1)
  print *, int_1, "sh:", shape(int_1)      ! prints: 2 sh: 1

  call map(add1, int_2x2)
  print *, int_2x2, "sh:", shape(int_2x2)  ! prints: 3 3 3 3 sh: 2 2

contains
  integer function add1(num)
    integer :: num
    add1 = num + 1
  end function
end program

Примечания:

  • Он изменяет (обновляет) переданный аргумент.Это не может быть функция, потому что не было бы никакого способа указать форму возвращаемого значения.
  • Требуются определенные реализации для каждой функции функции, а также для каждого типа и комбинации типов ее аргументов.
  • Он преобразует любую процедуру в элементарную версию, без ограничений intent и большого количества переписывания.
  • Я сообщу об этом как об ошибке, так что она, вероятно, будет исправлена ​​в ближайшее время и выиграетбольше не работает.
0 голосов
/ 24 декабря 2018

После некоторого копания я узнал, что когда функция помечена как elemental, она может быть применена к массиву, по сути, обеспечивая ту же функциональность, которую я искал.Вот пример для функции int->real type

program main
  integer :: int_arr(3)
  real :: real_arr(3)
  int_arr = [1, 2, 3]
  real_arr = my_sqrt(int_arr)
  print *, real_arr
contains
  elemental function my_sqrt(arg) result(res)
    integer, intent(in) :: arg
    real :: res
    res = sqrt(real(arg))
  end function
end program
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...