Fortran:将任意“结构”传递给模块子例程

时间:2014-11-22 22:04:08

标签: fortran gfortran

我正在尝试编写一个通用的子程序来实现最小化。由于我想要一个通用的子程序,目标函数可以有不同的参数,不仅在名称中,而且在尺寸上也是如此。所以我需要一种方法来传递参数结构(我使用的是单词结构,因为我的想法是在Matlab中使用类似结构类型变量的东西)。 我设法使用派生数据类型,它工作得很好,但是当我在同一个程序中有两个不同的目标函数时会出现问题。这是一个示例代码:

在主程序main.f90中:

MODULE MYPAR
  IMPLICIT NONE
  TYPE PARPASS1    !! Parameter passing structure 1
     INTEGER       :: a
     REAL          :: X(2,2)
  END TYPE PARPASS1

  TYPE PARPASS2    !! Parameter passing structure 2
     REAL          :: b
     REAL          :: Y(3)
  END TYPE PARPASS2
END MODULE MYPAR

PROGRAM MAIN

  USE MYPAR
  USE MYLIB
  IMPLICIT NONE

  INTEGER        :: am
  REAL           :: bm,Xm(2,2),Ym(3),sol1,sol2
  TYPE(PARPASS1) :: PARAM1
  TYPE(PARPASS2) :: PARAM2

  am = 1
  bm = 3.5
  Xm(1,:) = [1.0, 2.0]
  Xm(2,:) = [0.5, 2.5]
  Ym(1:3) = [0.25,0.50,0.75]

  PARAM1%a = am
  PARAM1%X = Xm
  PARAM2%b = bm
  PARAM2%Y = Ym

  CALL MYSUB(sol1,OBJ1,PARAM1)
  CALL MYSUB(sol2,OBJ2,PARAM2)
  PRINT *,sol1
  PRINT *,sol2

CONTAINS

  SUBROUTINE OBJ1(sumval,PARAM)
    REAL,INTENT(OUT)          :: sumval
    TYPE(PARPASS1),INTENT(IN) :: PARAM
    INTEGER                   :: a
    REAL,ALLOCATABLE          :: X(:,:)
    a = PARAM%a
    X = PARAM%X
    sumval = a+X(1,1)+X(2,2)
  END SUBROUTINE OBJ1

  SUBROUTINE OBJ2(divval,PARAM)
    REAL,INTENT(OUT)          :: divval
    TYPE(PARPASS2),INTENT(IN) :: PARAM
    REAL                      :: b
    REAL,ALLOCATABLE          :: Y(:)
    b = PARAM%b
    Y = PARAM%Y
    divval = b / (Y(1)+Y(2))
  END SUBROUTINE OBJ2

END PROGRAM MAIN

一个名为mylib.90的模块

MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

CONTAINS

  SUBROUTINE MYSUB(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS1), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB

END MODULE MYLIB

显然,如果我使用CALL MYSUB(sol2,OBJ2,PARAM2)PRINT *,sol2对这些行进行评论,我的代码会顺利运行。这是我之前有两个“目标函数”,但是现在当它有它们时它不起作用,因为MYSUB中的派生类型变量PARPASS1不能是任意的。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

你可以使用一个接口并重载子程序MYSUB:

MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

  interface MYSUB
    module procedure MYSUB_PARPASS1, MYSUB_PARPASS2
  end interface

CONTAINS

  SUBROUTINE MYSUB_PARPASS1(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS1), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB_PARPASS1

  SUBROUTINE MYSUB_PARPASS2(sol,FN,PARAM)
    REAL,INTENT(OUT)           :: sol
    TYPE(PARPASS2), INTENT(IN) :: PARAM
    CALL FN(sol,PARAM)
    sol = 2*sol
  END SUBROUTINE MYSUB_PARPASS2   

END MODULE MYLIB

然后你可以使用MYSUB来调用它,它会根据PARAM的类型来区分函数

编辑:好的,这个怎么样:

MODULE MYPAR
  IMPLICIT NONE

  type, abstract :: PARPASS
   contains
     procedure(func), deferred :: OBJ
  end type PARPASS

  TYPE, extends(PARPASS) :: PARPASS1    !! Parameter passing structure 1
     INTEGER       :: a
     REAL          :: X(2,2)
   contains
     procedure :: OBJ => OBJ1
  END TYPE PARPASS1

  TYPE, extends(PARPASS) :: PARPASS2    !! Parameter passing structure 2
     REAL          :: b
     REAL          :: Y(3)
   contains
     procedure :: OBJ => OBJ2
  END TYPE PARPASS2

  abstract interface
     subroutine func(this, val) !Interface for the subroutine you want to implement
       import
       class(PARPASS), intent(in) :: this
       real, intent(out) :: val
     end subroutine func
  end interface

contains

   subroutine OBJ1(this, val)
    class(PARPASS1),INTENT(IN) :: this
    real, intent(out)          :: val
    INTEGER                    :: a
    REAL,ALLOCATABLE           :: X(:,:)
    a = this%a
    X = this%X
    val = a+X(1,1)+X(2,2)
  END subroutine OBJ1

  subroutine OBJ2(this, val)
    class(PARPASS2),INTENT(IN) :: this
    real, intent(out)          :: val
    REAL                       :: b
    REAL,ALLOCATABLE           :: Y(:)
    b = this%b
    Y = this%Y
    val = b / (Y(1)+Y(2))

  END subroutine OBJ2

END MODULE MYPAR


MODULE MYLIB

  USE MYPAR
  IMPLICIT NONE

CONTAINS

  SUBROUTINE MYSUB(sol, param)
    REAL,INTENT(OUT)           :: sol
    class(PARPASS), INTENT(IN) :: PARAM
    call param%obj(sol)
    sol = 2*sol
  END SUBROUTINE MYSUB

END MODULE MYLIB

PROGRAM MAIN

  USE MYPAR
  USE MYLIB
  IMPLICIT NONE

  INTEGER        :: am
  REAL           :: bm,Xm(2,2),Ym(3),sol1,sol2
  TYPE(PARPASS1) :: PARAM1
  TYPE(PARPASS2) :: PARAM2

  am = 1
  bm = 3.5
  Xm(1,:) = [1.0, 2.0]
  Xm(2,:) = [0.5, 2.5]
  Ym(1:3) = [0.25,0.50,0.75]

  PARAM1%a = am
  PARAM1%X = Xm
  PARAM2%b = bm
  PARAM2%Y = Ym

  CALL MYSUB(sol1, PARAM1)
  CALL MYSUB(sol2, PARAM2)
  PRINT *,sol1
  PRINT *,sol2

END PROGRAM MAIN

它使用包含过程OBJ的抽象类型,然后派生类型可以扩展它并实现实际过程。然后,您可以传递任何扩展PARPASS的类型,并将类型绑定过程OBJ实现为'MYSUB',并从内部调用它,而无需为所有不同的可能性提供单独的接口。