Fortran中的通用地图函数

时间:2018-12-24 02:18:39

标签: fortran polymorphism

我的最终目标是在Fortran中拥有一个通用的映射函数,即,该函数接受任意类型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-arg函数都符合该接口,但是,显然,这不是声明它的正确方法。有什么想法在工作时如何做到这一点吗?预先感谢。

2 个答案:

答案 0 :(得分:2)

经过一番挖掘,我了解到将一个函数标记为elemental时,它可以应用于数组,本质上提供了我一直在寻找的相同功能。这是int->real类型的函数的示例

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

答案 1 :(得分:2)

即将到来的标准Fortran 2018包括新的构造select_rank,它将为编码与等级无关的过程带来更大的灵活性,而不受elemental过程的约束。直到今天(2018年底),该标准才刚刚由ISO正式发布,因此可能需要一段时间才能被供应商实施。


仅出于琐事考虑,gfortran 8.2.0中存在一个错误,该错误允许使用map伪参数在Fortran中编写一种assumed-rank子例程,而没有{ {1}}(或每个等级的特定例程)。

重要提示::我不建议将此代码段用于生产代码。我决定发布它只是为了感谢可能会担任此职位的Fortran同事,希望它可以提供对语言的更好理解并启发一些想法。 @DartLenin的self answer是在Fortran中执行此操作的正确方法。

select_rank

注意:

  • 它修改(更新)传递的参数。它不能用作函数,因为无法指定返回值的形状。
  • 它需要针对每个函数Arity及其参数的每种类型和类型组合的特定实现。
  • 它将任何过程转换为该过程的基本版本,而没有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 约束和大量重写。
  • 我将向错误报告此错误,因此可能会尽快修复,并且不再起作用。