oop - caracteristicas - polimorfismo en tiempo de ejecución en Fortran 2003
fortran definicion (2)
Estoy escribiendo un código en Fortran 2003 que hace una gran cantidad de álgebra lineal con matrices dispersas. Estoy intentando explotar algunas de las características más abstractas del nuevo estándar, así que tengo programas más simples sin demasiados códigos repetidos.
Tengo un solver
procedimientos que toma en una matriz, algunos vectores, la tolerancia para el método iterativo utilizado, etc. Le paso un puntero a un procedimiento llamado matvec
; matvec
es la subrutina que usamos para las multiplicaciones de matrices-vectores.
El problema es que, a veces, matvec
es un procedimiento que colorlist, color1, color2
argumentos adicionales colorlist, color1, color2
encima de los habituales enviados a este procedimiento. Puedo pensar en varias formas de lidiar con esto.
Primera idea: definir dos interfaces abstractas diferentes matvec1
, matvec2
y dos solucionadores diferentes. Esto funciona, pero significa duplicar código, que es exactamente lo que trato de evitar.
Otra idea: mantener la misma interfaz abstracta matvec
, y hacer que los argumentos adicionales colorlist
, color1
, color2
opcionales. Eso significa hacerlos opcionales en cada rutina matvec, incluso aquellos para los que no son realmente opcionales, y para rutinas donde ni siquiera se usan. Estoy bastante seguro de que iré al infierno si hago esto.
Puedo pensar en muchas otras soluciones menos que óptimas. Me gustaría obtener alguna opinión sobre esto, estoy seguro de que hay una forma elegante de hacerlo, solo que no estoy seguro de qué se trata.
En lugar de pasar el puntero de procedimiento como un argumento explícito, podría poner las diversas rutinas matvec
detrás de una interfaz genérica:
interface matvec
module procedure matvec1, matvec2
end interface
Entonces su rutina de solver
puede simplemente usar el nombre genérico con o sin los argumentos adicionales. Por supuesto, también se puede tomar el mismo enfoque cuando se usa el enfoque sugerido de Bálint para definir un solver
como un tipo derivado con procedimientos de tipo ligado:
type :: solver
real, allocatable :: matrix(:,:), v1(:), v2(:)
contains
procedure, pass :: matvec1
procedure, pass :: matvec2
generic :: matvec => matvec1, matvec2
end type
La diferencia principal es que esto no usa polimorfismo para determinar el procedimiento correcto para invocar, sino más bien las características de los argumentos ficticios.
No estoy seguro de sus intenciones para el puntero del procedimiento; si desea cambiar su objetivo en el tiempo de ejecución (o tal vez asignar algún significado especial a su estado "indefinido"), los punteros son la única forma y todos los objetivos deben coincidir con la misma interfaz abstracta. Si, en cambio, solo necesitas seleccionar uno de varios procedimientos basados en sus argumentos, entonces puedes explotar la interfaz (mi ejemplo) o la sobrecarga (ejemplo de Bálint). Cada extensión de un tipo puede extender un enlace generic
heredado con nuevos procedimientos o sobrecargar un enlace específico heredado.
La pregunta es, realmente, si los argumentos adicionales se deben pasar cada vez que se invoca el procedimiento (porque cambian entre dos invocaciones), o pueden inicializarse en algún momento y luego usarse en la función. En el caso posterior, podría crear una clase con una interfaz abstracta, que defina su subrutina matvec
con los argumentos esenciales. Luego puede extender esa clase con otras más especializadas, que pueden contener las opciones adicionales necesarias. Aún tendrán que definir la misma interfaz matvec
que la clase padre (con la misma lista de argumentos), pero pueden usar los valores adicionales almacenados en ellos cuando se matvec
su procedimiento matvec
.
Encontrará un ejemplo detallado en esta respuesta para un caso similar (busque el segundo ejemplo que muestra el module rechercheRacine
).