2013-03-07 14 views
5

Ich schreibe etwas Code in Fortran 2003, der viel lineare Algebra mit dünn besetzten Matrizen tut. Ich versuche, einige der abstrakteren Merkmale des neuen Standards auszunutzen, also habe ich einfachere Programme ohne zu viel wiederholten Code.Laufzeit-Polymorphie in Fortran 2003

Ich habe eine Prozedur solver, die in einer Matrix, einige Vektoren, die Toleranz für die iterative Methode verwendet usw. Ich gebe einen Zeiger auf eine Prozedur namens matvec zu ihm; matvec ist die Subroutine, die wir für Matrix-Vektor-Multiplikationen verwenden.

Das Problem ist, manchmal matvec ist ein Verfahren, das zusätzliche Argumente colorlist, color1, color2 über die üblichen an dieses Verfahren gesendet. Ich kann mir verschiedene Möglichkeiten vorstellen, damit umzugehen.

Erste Idee: Definieren Sie zwei verschiedene abstrakte Schnittstellen matvec1, matvec2 und zwei verschiedene Löser. Das funktioniert, aber es bedeutet, einen Code zu duplizieren, was ich gerade vermeiden möchte.

Eine andere Idee: Halten Sie die gleiche abstrakte Schnittstelle matvec, und machen die zusätzlichen Argumente , color1, color2 optional. Das bedeutet, dass sie in jeder Matvec-Routine optional sind - sogar für solche, für die sie nicht wirklich optional sind, und für Routinen, in denen sie überhaupt nicht verwendet werden. Ziemlich sicher, dass ich zur Hölle fahren werde, wenn ich das tue.

Ich kann an viele andere weniger als optimale Lösungen denken. Ich hätte gerne etwas dazu - ich bin mir sicher, dass es eine elegante Möglichkeit gibt, es zu tun, ich bin mir nur nicht sicher, was es ist.

Antwort

5

Die Frage ist wirklich, ob die zusätzlichen Argumente jedes Mal übergeben werden müssen, wenn die Prozedur aufgerufen wird (weil sie zwischen zwei Aufrufen wechseln), oder sie können zu einem bestimmten Zeitpunkt initialisiert und dann nur in der Funktion verwendet werden. Im letzteren Fall könnten Sie eine Klasse mit einer abstrakten Schnittstelle erstellen, die Ihre Subroutine matvec mit den wesentlichen Argumenten definiert. Sie können diese Klasse dann um speziellere erweitern, die die zusätzlichen erforderlichen Optionen enthalten können. Sie müssen immer noch dieselbe matvec Schnittstelle als übergeordnete Klasse (mit der gleichen Argumentliste) definieren, aber sie können die zusätzlichen Werte verwenden, die in ihnen gespeichert sind, wenn ihre Prozedur matvec aufgerufen wird.

Sie finden ein ausführliches Beispiel in this answer für einen ähnlichen Fall (suchen Sie das zweite Beispiel mit module rechercheRacine).

2

Anstatt das Verfahren Zeiger als explizites Argument vorbei, könnte man die verschiedenen matvec Routinen hinter einer generischen Schnittstelle setzen:

interface matvec 
    module procedure matvec1, matvec2 
end interface 

Dann Ihre solver Routine kann nur die Gattungsnamen verwenden, um mit oder ohne die zusätzlichen Argumente . Der gleiche Ansatz kann natürlich auch getroffen werden, wenn Bálint vorgeschlagenen Ansatz der Definition ein solver als abgeleitete Typ mit typgebundene Verfahren:

type :: solver 
    real, allocatable :: matrix(:,:), v1(:), v2(:) 
contains 
    procedure, pass :: matvec1 
    procedure, pass :: matvec2 
    generic :: matvec => matvec1, matvec2 
end type 

Der wichtigste Unterschied ist, dass dies nicht Polymorphismus nicht verwenden, um die richtige Vorgehensweise zu bestimmen, aufgerufen werden, sondern eher die Eigenschaften der Dummy-Argumente.

Ich bin mir Ihrer Absichten für den Verfahrenszeiger nicht sicher; Wenn Sie das Ziel zur Laufzeit ändern möchten (oder dem Status "undefiniert" möglicherweise eine besondere Bedeutung zuweisen), sind Zeiger die einzige Möglichkeit, und alle Ziele müssen mit derselben abstrakten Schnittstelle übereinstimmen.Wenn Sie stattdessen nur eine von mehreren Prozeduren auswählen müssen, die auf ihren Argumenten basieren, können Sie das Interface (mein Beispiel) oder das Überladen (Beispiel von Bálint) ausnutzen. Jede Erweiterung eines Typs kann eine ererbte generic Bindung mit neuen Prozeduren erweitern oder eine geerbte spezifische Bindung überladen.