首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Fortran设计相互交互的对象时的建议

使用Fortran设计相互交互的对象时的建议
EN

Stack Overflow用户
提问于 2018-02-22 17:21:45
回答 2查看 313关注 0票数 7

我整个下午都在努力解决这个问题,所以我希望有人能帮我。

我有一个名为base_model (例如)的抽象基类,在Fortran2003中如下所示:

代码语言:javascript
复制
type, abstract :: base_model
contains
  procedure(initMe), pass(this), deferred :: init ! constructor
  procedure(delMe), pass(this), deferred :: delete ! destructor
  procedure(solveMe), pass(this), deferred :: solve
end type base_model

显然,抽象过程initMedelMesolveMe是使用抽象接口块定义的。然后我有三个派生类,名为model1model2model3 (例如):

代码语言:javascript
复制
type, extends(base_model) :: model1
  double precision :: x,y
contains
  procedure :: init => init_model1
  procedure :: delete => delete_model1
  procedure :: solve => solve_model1
end type model1

type, extends(base_model) :: model2
contains
  procedure :: init => init_model2
  procedure :: delete => delete_model2
  procedure :: solve => solve_model2
end type model2

type, extends(base_model) :: model3
contains
  procedure :: init => init_model3
  procedure :: delete => delete_model3
  procedure :: solve => solve_model3
end type model3

然后我有一个名为control (例如)的“控制”对象,它扩展了一个抽象的base_control

代码语言:javascript
复制
type, abstract :: base_control
  class(base_model), allocatable :: m1
  class(base_model), allocatable :: m2
  class(base_model), allocatable :: m3
contains
  procedure(initMe), pass(self), deferred :: init
  procedure(delMe), pass(self), deferred :: delete
  procedure(runMe), pass(self), deferred :: run
end type base_control

type, extends(base_control) :: control
contains
  procedure :: init => init_control
  procedure :: delete => delete_control
  procedure :: run => run_control
end type control

对象m1m2m3可以分配到任何模型:model1model2model3,并根据用户请求的“控件”的不同顺序“解决”。

三个可分配对象(m1m2m3)需要在它们之间传递数据。考虑到它们是“控制”对象的成员,我可以为每个模型定义一个"getter“,然后将所需的数据传递给每个模型。但是,在编译时不知道具体的模型,因此,“控制”对象不知道要获取什么数据,实际上,模型不知道要接收什么数据!

例如,如果I allocate(model1::m1) (即将m1分配为model1类型),那么它将包含两位数据double precision :: x,y。然后,如果m2被分配为model2 (allocate(model2::m2))类型,那么它可能需要x,但是如果它被分配为model3 (allocate(model3::m2))类型,那么它可能需要来自m1y。因此,既然“控制”对象无法知道分配了什么类型的m2,那么它如何从m1获得必要的数据以传递给m2

另一个复杂的问题是,模型之间的相互作用通常是圆形的。也就是说,m1需要来自m2的数据,m2需要来自m1的数据等等。此外,所需的数据一般不仅是特定于模型,而且在类型和数量上都是可变的。

不幸的是,数据xy不是base_model的成员,因此,将m1作为参数传递给m2也是行不通的。

因此,我有以下问题:

  1. 是否有更好的方法来设计这些对象,以便我可以轻松地在它们之间传递数据?环顾四周,有一些建议认为,最好的做法是重新设计对象,使它们之间的交互不是循环的。然而,这在这里是有必要的!
  2. 我是否必须为对象之间可能共享的每一段数据编写一个"getter“?这似乎是大量的编码(我有很多数据可以共享)。然而,这似乎也相当复杂,因为"getter“(特定于一段数据)还必须满足抽象接口。

在Python这样的高级语言中,这很容易,因为我们可以简单地创建一个新的数据类型作为模型的组合,但据我所知,这在Fortran中是不可能的。

提前谢谢。任何帮助都是非常感谢的。

编辑:在下面与弗朗西夫特讨论之后,select type是一种选择。实际上,在上面给出的简单示例中,select type将是一个不错的选择。然而,在我的实际代码中,这将导致大量嵌套的select type,因此如果有一种不使用select type的方法,我会更喜欢它。感谢弗朗西夫斯指出我在select type上的错误。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-03-05 07:37:18

来回答你的两个问题:

有更好的设计方法吗?

我不太明白为什么你的设计中有这么多违禁品,但简而言之,是的。您可以为模型使用上下文管理器。我建议您查看以下答案:Context class pattern

你必须在每个模型上写一个getter方法吗?

不完全是,如果您在这个特定的问题上使用上下文策略,那么您需要在每个模型上实现的唯一一件事就是在模型之间共享数据的setter方法。

我在Python中为这个场景实现了一个可行的解决方案。代码比文字更能说明问题。我避免使用Python的任何特殊特性来为您提供如何在本例中使用上下文的清晰理解。

代码语言:javascript
复制
from abc import ABC, abstractmethod
import random

class BaseModel(ABC):
    def __init__(self, ctx):
        super().__init__()
        self.ctx = ctx
        print("BaseModel initializing with context id:", ctx.getId())

    @abstractmethod
    def solveMe():
        pass

class BaseControl(object):
    # m1 - m3 could be replaced here with *args
    def __init__(self, m1, m2, m3):
        super().__init__()
        self.models = [m1, m2, m3]


class Control(BaseControl):
    def __init__(self, m1, m2, m3):
        super().__init__(m1, m2, m3)

    def run(self):
        print("Now Solving..")
        for m in self.models:
            print("Class: {} reports value: {}".format(type(m).__name__, m.solveMe()))


class Model1(BaseModel):
    def __init__(self, x, y, ctx):
        super().__init__(ctx)
        self.x = x
        self.y = y
        ctx.setVal("x", x)
        ctx.setVal("y", y)

    def solveMe(self):
        return self.x * self.y

class Model2(BaseModel):
    def __init__(self, z, ctx):
        super().__init__(ctx)
        self.z = z
        ctx.setVal("z", z)

    def solveMe(self):
        return self.z * self.ctx.getVal("x")

class Model3(BaseModel):
    def __init__(self, z, ctx):
        super().__init__(ctx)
        self.z = z
        ctx.setVal("z", z)

    def solveMe(self):
        return self.z * self.ctx.getVal("y")

class Context(object):
    def __init__(self):
        self.modelData = {}
        self.ctxId = random.getrandbits(32)

    def getVal(self, key):
        return self.modelData[key]

    def setVal(self, key, val):
        self.modelData[key] = val

    def getId(self):
        return self.ctxId


ctx = Context()

m1 = Model1(1,2, ctx)
m2 = Model2(4, ctx)
m3 = Model3(6, ctx)

# note that the order in the arguments to control defines behavior
control = Control(m1, m2, m3)
control.run()

输出

代码语言:javascript
复制
python context.py
BaseModel initializing with context id: 1236512420
BaseModel initializing with context id: 1236512420
BaseModel initializing with context id: 1236512420
Now Solving..
Class: Model1 reports value: 2
Class: Model2 reports value: 4
Class: Model3 reports value: 12

解释

简而言之,我们创建了一个上下文类,它有一个可以在不同模型之间共享的字典。此实现非常特定于您提供的原始数据类型(即x、y、z)。如果您需要在数据跨模型共享之前计算数据,则仍然可以使用此模式,将solveMe()的返回替换为延迟承诺。

票数 3
EN

Stack Overflow用户

发布于 2018-03-05 09:26:41

下面是基于键/值(*)访问另一个对象的字段的类似尝试。为了简单起见,主程序从child1_t对象获取一个整数,并将一个复杂的值设置为child2_t对象(这两个值都是parent_t的扩展类型)。

parent.f90:

代码语言:javascript
复制
module parent_m
    implicit none

    type, abstract :: parent_t   !(abstract is optional)
    contains
        procedure :: set
        procedure :: get
        procedure :: show
    endtype

    type composite_t
        class(parent_t), allocatable :: pa, pb
    endtype

contains
    subroutine set( this, key, val )  ! key-based setter
        class(parent_t), intent(inout) :: this
        character(*),   intent(in)     :: key
        integer,        intent(in)     :: val
    endsubroutine

    subroutine get( this, key, val )  ! key-based getter
        class(parent_t), intent(in)  :: this
        character(*),    intent(in)  :: key
        integer,         intent(out) :: val
    endsubroutine

    subroutine show( this )   ! print contents
        class(parent_t), intent(in) :: this
    endsubroutine
end module

child.f90:

代码语言:javascript
复制
module child_m
    use parent_m, only: parent_t
    implicit none

    type, extends(parent_t) :: child1_t
        integer :: n1 = 777   ! some property
    contains
        procedure :: get  => child1_get
        procedure :: show => child1_show
    endtype

    type, extends(parent_t) :: child2_t
        complex :: c2 = ( 0.0, 0.0 )   ! another property
    contains
        procedure :: set  => child2_set
        procedure :: show => child2_show
    endtype

contains

    subroutine child1_get( this, key, val )
        class(child1_t), intent(in)  :: this
        character(*),    intent(in)  :: key
        integer,         intent(out) :: val

        select case( key )
            case ( "num", "n1" ) ; val = this % n1  ! get n1
            case default ; stop "invalid key"
        end select
    end subroutine

    subroutine child1_show( this )
        class(child1_t), intent(in) :: this
        print *, "[child1] ", this % n1
    endsubroutine

    subroutine child2_set( this, key, val )
        class(child2_t), intent(inout) :: this
        character(*),    intent(in)    :: key
        integer,         intent(in)    :: val

        select case( key )
            case ( "coeff", "c2" ) ; this % c2 = cmplx( real(val), 0.0 )  ! set c2
            case default ; stop "invalid key"
        end select
    end subroutine

    subroutine child2_show( this )
        class(child2_t), intent(in) :: this
        print *, "[child2] ", this % c2
    endsubroutine

end module

main.f90:

代码语言:javascript
复制
program main
    use parent_m, only: composite_t
    use child_m,  only: child1_t, child2_t
    implicit none
    type(composite_t) :: c
    integer itmp

    allocate( child1_t :: c % pa )
    allocate( child2_t :: c % pb )

    print *, "initial state:"
    call c % pa % show()
    call c % pb % show()

    call c % pa % get( "num",  itmp )   ! get an integer value from pa
    call c % pb % set( "coeff", itmp )  ! set a complex value to pb

    print *, "modified state:"
    call c % pa % show()
    call c % pb % show()
end

汇编和结果:

代码语言:javascript
复制
 $ gfortran parent.f90 child.f90 main.f90

 initial state:
 [child1]          777
 [child2]              (0.00000000,0.00000000)
 modified state:
 [child1]          777
 [child2]              (777.000000,0.00000000)

虽然上面的代码只处理整数和复杂的“数据传输”,但其他类型的数据可以类似地添加到select case结构中(而无需为每个构造添加新的getter/setter方法)。此外,如果有必要,我们可以通过get()关键字(在parent_t类型中)重载不同类型的value (例如set_int()set_real()),并在扩展类型中重写它们。数组类型的value也是如此,也许.

如果扩展类型之间的数据传输(副本)很昂贵(例如,大数组),我猜getter可以返回指向它们的指针,以便childX_t的数据可以以一种紧密耦合的方式进行通信(而不知道它们的实现)。

(但我想可能有一种比做上述事情更简单的方法,包括问题中的第1点(例如,程序本身的重新设计)。此外,如果我们手头有一些字典类型,字典的使用(如在the other answer中)似乎对我更有吸引力。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48933614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档