2

我非常热衷于实现 FORTRAN 2003 (F2003) 的面向对象编程 (OOP) 功能。我的问题更多是关于程序的设计。假设我有一个求解器,例如函数f(x)=0的根查找器;以 FORTRAN 中最简单的格式,它会得到

function solver(f,a,b) result(root)
    [some definition of variables]
    [some iterative procedures]
    root = ...
end function

在 F95 等 FORTRAN 的早期版本中,为了获得可移植性,代码被单独编译,并将外部函数传递给求解器。现在从 F2003 OOP 的角度来看,一般情况下我们有一个求解器类

type solver_t
   real :: a,b,root
contains
   procedure :: solve => solve_solver_t          ! root=this%solve()
   [some initialization and post-calculation procedures]
end type

和我们曲线的另一类

type curve_t
    [some variable definition]
contains
    [some initialization procedures]
    procedure :: func => function_curve_t    ! y=this%func(x)
    procedure :: plot => plot_curve
end type

并且会有更多不同的曲线类别(类型)。现在,我如何以编译求解器类(不知道曲线类/类型)的方式连接这两个概念,并且每当我编写新的不同曲线类(如 2nd_order_polynomial_curve , 3rd_order_polynomial_curve, log_curve, exp_curve,...)。我的意思是,最后,不知何故,我得到了曲线的根源。

4

1 回答 1

3

这是一个如何使用 F2003 OOP 来实现这一想法的示例。我将从一个要构建到共享库中的模块开始:

module solver
  implicit none

  type, abstract :: curve_t
   contains
     procedure(func_f), pass(this), deferred :: f
  end type curve_t

  type :: solver_t
     class(curve_t), pointer :: curve
   contains
     procedure, pass :: solve => solve_root_bisect_method
  end type solver_t

  abstract interface
     function func_f(this, x)
       import curve_t
       class(curve_t) :: this
       real, intent(in) :: x
       real :: func_f
     end function func_f
  end interface

contains

  function solve_root_bisect_method(this, a_start, b_start) result(root)
    implicit none
    class(solver_t) :: this
    real, intent(in) :: a_start, b_start
    real :: root, c, eps, a, b
    integer :: i, imax
    imax = 100
    eps = 1e-5
    a = a_start
    b = b_start

    do i=1, imax
       c = (a+b)/2.
       if ( (this%curve%f(c) == 0) .or. ((b-a)/2. < eps)) then
          root = c
          return
       end if
       if (sign(1.,this%curve%f(c)) == sign(1.,this%curve%f(a))) then
          a = c
       else
          b = c
       end if
    end do
    ! solution did not converge, produce error
    root = -999
  end function solve_root_bisect_method
end module solver

这定义了一个抽象类来表示曲线和求解器的一个类。求解器也可以抽象化,但出于演示的目的,我选择不这样做并提供一个求解器。您仍然可以扩展此类型并为求解接口提供不同的过程。您可以将其编译到共享库中,例如

gfortran -shared -fPIC -o solver.so solver.f90

这将产生solver.sosolver.mod。我做了这个额外的步骤来演示可移植性和在不了解任何曲线的情况下进行编译。

现在我们可以假装是想要使用这个方便的库来查找任意曲线的根的第三方。首先我们可以定义自己的模块来扩展curve并提供一些功能。

module curves
  use solver
  implicit none

  type, extends(curve_t) :: linear_curve
     real :: m, b
   contains
     procedure, pass(this) :: f => f_linear
  end type linear_curve

  type, extends(curve_t) :: polynomial_curve
     real :: a, b, c
   contains
     procedure, pass(this) :: f => f_polynomial
  end type polynomial_curve

contains

  real function f_linear(this, x)
    use solver
    implicit none
    class(linear_curve) :: this
    real, intent(in) :: x
    f_linear = this%m * x + this%b
  end function f_linear

  real function f_polynomial(this, x)
    use solver
    implicit none
    class(polynomial_curve) :: this
    real, intent(in) :: x
    f_polynomial = this%a*x*x + this%b*x + this%c
  end function f_polynomial
end module curves

这定义了线性曲线和多项式曲线的类型,其中包含它们的参数和用于计算y作为x给定这些参数的函数的函数。因为我们派生自curve_t并遵循 的接口f,所以我们可以轻松地将这些类与solver_t类一起使用。

这是一个小程序来演示这一点

program test
  use solver
  use curves
  implicit none

  type(linear_curve), target :: linear
  type(polynomial_curve), target :: parabola
  type(solver_t) :: root_solver
  real :: root

  linear%m = 1.
  linear%b = 0.     ! y=x
  parabola%a = 1.
  parabola%b = 0.
  parabola%c = -1.  ! y=x^2-1

  root_solver%curve => linear
  root = root_solver%solve(-1., 1.)
  print *, "root  = ", root

  root_solver%curve => parabola
  root = root_solver%solve(-4., 0.5)
  print *, "root1 = ", root
  root = root_solver%solve(-0.5, 4.)
  print *, "root2 = ", root
end program test

这里我声明了一些曲线,设置它们的参数,然后调用求解器找到一个根。如果您编译我们的曲线模块、测试程序并链接到我们之前创建的共享库,我们可以运行输出:

% ./roots                               
 root  =    0.00000000    
 root1 =   -1.00000286    
 root2 =    1.00000286

(根的质量受到我转储到第一个模块中的示例求解器的质量的限制,你可以做得更好)。这不是纯 OO 的最佳演示,因为 solver_t 类可以做得更好,但我专注于演示如何处理多个用户定义的曲线,而无需在编译 solve_t 时了解它们的任何信息。

于 2015-08-07T21:13:16.110 回答