Fortran用户定义的运算符中未分配的假定形状数组的问题

时间:2019-07-12 13:51:04

标签: arrays fortran operator-overloading

我正在尝试实现一个python类型的df = df.groupby(['name','objective']).min() 运算符,该运算符检查一维数组是否包含某个元素。原则上,我可以使用它,但是我无法覆盖我要使用的两种类型的数组,即固定大小的数组和可分配的数组。下面的代码几乎可以满足我的要求:

in

按原样,代码将产生以下输出:

MODULE operator_in
  IMPLICIT NONE

  INTERFACE OPERATOR(.IN.)
     MODULE PROCEDURE in_integer_list
!     MODULE PROCEDURE in_integer_list_alloc
  END INTERFACE OPERATOR(.IN.)

CONTAINS
  FUNCTION in_integer_list(key, list) RESULT(res)
    IMPLICIT NONE
    INTEGER, INTENT(IN) :: key
    INTEGER, INTENT(IN) :: list(:)
    LOGICAL             :: res

    INTEGER :: ii

    res = .FALSE.
    DO ii = 1,SIZE(list)
       IF (key == list(ii)) THEN
          res = .TRUE.
          RETURN
       END IF
    END DO
  END FUNCTION in_integer_list

  FUNCTION in_integer_list_alloc(key, list) RESULT(res)
    IMPLICIT NONE
    INTEGER,              INTENT(IN) :: key
    INTEGER, ALLOCATABLE, INTENT(IN) :: list(:)
    LOGICAL                          :: res

    IF (ALLOCATED(list)) THEN
       res = in_integer_list(key, list)
    ELSE
       res = .FALSE.
    END IF
  END FUNCTION in_integer_list_alloc

END MODULE operator_in


PROGRAM test

  USE operator_in

  INTEGER              :: list1(5) = (/1, 4, 6, 3, 8/)
  INTEGER, ALLOCATABLE :: list2(:), list3(:)
  INTEGER              :: ii


  ALLOCATE(list2(7))
  list2(:) = (/8,7,6,5,4,2,1/)

  DO ii = 1,5
     IF (ii .IN. list1) THEN
        WRITE (*,'(I3,A,5I3)') ii, ' is in ', list1
     END IF
     IF (ii .IN. list2) THEN
        WRITE (*,'(I0.3,A,7I3)') ii, ' is in ', list2
     END IF
!     IF (ii .IN. list3) THEN
!        WRITE (*,'(I3,A,7I3)') ii, ' is in ', list3
!     END IF
  END DO

END PROGRAM test

但是,如果我取消对最后三行的注释,

  1 is in   1  4  6  3  8
  1 is in   8  7  6  5  4  2  1
  2 is in   8  7  6  5  4  2  1
  3 is in   1  4  6  3  8
  4 is in   1  4  6  3  8
  4 is in   8  7  6  5  4  2  1
  5 is in   8  7  6  5  4  2  1

由于未分配 IF (ii .IN. list3) THEN WRITE (*,'(I0.3,A,7I3)') ii, ' is in ', list3 END IF ,代码因分段错误而崩溃:

list3

我试图通过编写第二个函数(Program received signal SIGSEGV: Segmentation fault - invalid memory reference. Backtrace for this error: #0 0x10925ebe4 #1 0x10925e306 #2 0x7fff5e878b5c #3 0x1092547da #4 0x109254bc5 #5 0x109254cce Segmentation fault: 11 )来解决此问题,该函数允许可分配的数组,但是在我的界面中声明了两个函数:

in_integer_list_alloc

给我一​​个歧义错误:

  INTERFACE OPERATOR(.IN.)
     MODULE PROCEDURE in_integer_list
     MODULE PROCEDURE in_integer_list_alloc
  END INTERFACE OPERATOR(.IN.)

如果我在界面中注释掉了第一个过程:

   FUNCTION in_integer_list(key, list) RESULT(res)
  1
user-defined_operator.f90:27:2:

   FUNCTION in_integer_list_alloc(key, list) RESULT(res)
  2
Error: Ambiguous interfaces in operator interface 'in' for 'in_integer_list' at (1) and 'in_integer_list_alloc' at (2)

我当然遇到了固定大小的数组 INTERFACE OPERATOR(.IN.) ! MODULE PROCEDURE in_integer_list MODULE PROCEDURE in_integer_list_alloc END INTERFACE OPERATOR(.IN.) 的问题:

list1

所以:在代码由于未分配传递的数组而崩溃时,是否有一种聪明的方法来实现我想要的功能,或者至少获得正确的错误消息?

2 个答案:

答案 0 :(得分:3)

理想情况下,重新设计代码,这样就无需处理未分配的数组。如果要表示一个空列表,请使用分配的零大小数组。

(未分配的对象在概念上更适合“没有列表”,而不是“列表为空”。从概念上讲,您不应该查询不存在的对象。)

如果必须的话,可以按照以下内容编写单个参数适配器函数:

FUNCTION foo(arg)
  INTEGER, INTENT(IN), OPTIONAL:: arg(:)
  INTEGER, ALLOCATABLE :: foo(:)
  IF (PRESENT(arg)) THEN
    foo = arg
  ELSE
    foo = [INTEGER ::]
  END IF
END FUNCTION foo

然后可以使用适配器:

 IF (item .in. foo(list)) THEN
   ...

适配器功能的适当命名留给阅读器。

(为适配器编写的虚拟参数为OPTIONAL,以容纳不存在的实际参数,未分配的实际参数和分离的实际参数。这是Fortran 2008的功能。)

答案 1 :(得分:1)

作为Python用户,我也很感激.in.运算符可以提供的语法糖。也就是说,标准的Fortran也非常简洁:

if (any(items==value)) then
    ...
endif

但是,这是一种实现.in.的方法,该方法应处理固定大小和可分配数组:

module operator_in
    implicit none
    interface operator(.in.)
        module procedure operatorin
    end interface operator(.in.)

    contains
    logical function operatorin(v,lst) result(found)
        implicit none
        integer, intent(in) :: v
        integer, intent(in) :: lst(:)
        integer, allocatable :: temp(:)

        found = .false.
        allocate(temp,source=lst)
        if (size(temp)>0) then
            if (any(temp == v)) found=.true.
        endif
    end function operatorin
end module operator_in

注意:我使用可分配的temp数组,因此,如果使用check:all(或类似版本)编译,则代码将运行而不会出现错误消息。另外,我使用any来避免手动遍历项目列表。试试吧,很好。