我在Fortran 2003中编写了一些代码,它使用稀疏矩阵执行大量线性代数。我正在尝试利用新标准的一些更抽象的功能,所以我有更简单的程序,没有太多的重复代码。
我有一个过程solver
,它接受一个矩阵,一些向量,所使用的迭代方法的容差等等。我正在向它传递一个指向matvec
的过程的指针; matvec
是我们用于矩阵向量乘法的子程序。
问题是,有时候matvec
是一个过程,它会在发送到此过程的常规参数之上引入额外的参数colorlist, color1, color2
。我可以想到几种解决这个问题的方法。
第一个想法:定义两个不同的抽象接口matvec1
,matvec2
和两个不同的求解器。这有效,但这意味着要复制一些代码,这正是我想要避免的。
另一个想法:保持相同的抽象接口matvec
,并使额外的参数colorlist
,color1
,color2
可选。这意味着在每个matvec例程中都可以选择它们 - 甚至是那些它们不是真正可选的例程,以及它们甚至根本不用的例程。如果我这样做,我肯定会下地狱。
我可以想到许多其他不太理想的解决方案。我想对此有所了解 - 我确信有一些优雅的方法可以做到,我只是不确定它是什么。
答案 0 :(得分:5)
问题是,每次调用过程时是否必须传递其他参数(因为它们在两次调用之间发生变化),或者它们可以在某个时刻初始化,然后只在函数中使用。在后一种情况下,您可以使用抽象接口创建一个类,该接口使用基本参数定义子例程matvec
。然后,您可以使用更专业的类扩展该类,这可以包含所需的其他选项。他们仍然必须定义与父类相同的matvec
接口(具有相同的参数列表),但是当他们调用matvec
过程时,他们可以使用存储在其中的附加值。
您可以找到类似案例的详细示例in this answer(查找显示module rechercheRacine
的第二个示例)。
答案 1 :(得分:2)
您可以将各种matvec
例程放在通用接口后面,而不是将过程指针作为显式参数传递:
interface matvec
module procedure matvec1, matvec2
end interface
然后你的solver
例程可以使用带有或不带额外参数的通用名称。当使用Bálint建议的方法将solver
定义为具有类型绑定过程的派生类型时,当然也可以采用相同的方法:
type :: solver
real, allocatable :: matrix(:,:), v1(:), v2(:)
contains
procedure, pass :: matvec1
procedure, pass :: matvec2
generic :: matvec => matvec1, matvec2
end type
主要区别在于,这不使用多态来确定要调用的正确过程,而是使用伪参数的特征。
我不确定你对程序指针的意图;如果您希望在运行时更改其目标(或者可能为其“未定义”状态指定一些特殊含义),则指针是唯一的方式,并且所有目标都需要匹配相同的抽象接口。如果您只需要根据其参数选择几个过程中的一个,那么您可以利用接口(我的示例)或重载(Bálint的示例)。类型的每个扩展都可以使用新过程扩展继承的generic
绑定,或者重载继承的特定绑定。