next up previous contents
Nächste Seite: FORTRAN-Knigge Aufwärts: Programmiersprachen, FORTRAN Vorherige Seite: Weitere Sprachmittel   Inhalt


Tips zum (nicht nur FORTRAN-) Programmieren

Hier sollen die in Abschnitt ,,Programmierstil -- wie schreibt man ein gutes Programm` gebrachten sprachunabhängigen Hinweise nochmals in Kurzfassung angegeben und um weitere, mehr FORTRAN-spezifische Tips ergänzt werden. Im anschließenden Abschnitt wird ergänzend ein kurzgefaßter ,,FORTRAN-Knigge`` gebracht.

Ziel ist ein klar strukturiertes und gut lesbares Programm. Struktur erreicht man durch Gliederung und Zerlegung in nicht zu große Module mit definierten Schnittstellen. Die einzelnen Module sollen ebenfalls im formalen Aufbau und im Ablauf strukturiert sein. Dabei unterstützt uns die Verwendung von Konstrukten zur strukturierten Programmierung. Lesbarkeit und Selbst-Dokumentation erreicht man durch Gliederung in Programmkopf (Kommentarteil und Vereinbarungsteil) und Prozedurrumpf. Der Kommentarteil umfaßt die Zweckangabe und die Parameterbeschreibung und enthält weitere Hinweise sowie eine Modifikationsgeschichte. Der Vereinbarungsteil umfaßt Definitionen, Vereinbarungen und Initialisierungen. Für die Programmanweisungen im Prozedurrumpf sollen aussagekräftige Namen verwendet werden. Übersichtlichkeit erreicht man durch optische Trennung mit Leerzeichen und -Zeilen. Zusammengehörige Blöcke im Wiederholungsbereich von Schleifen und in IF-Blöcken werden eingerückt. Kleinere Anweisungsblöcke werden mit beschreibenden Kommentaren versehen, ebenso wie die Eingabeanforderungen und Ausgaben des Programms.

Die folgenden Tips sind zum Teil ebenfalls schon an anderer Stelle im Kapitel 4.2 näher besprochen worden.

Geschachtelte Schleifen:

Beispiel:

      DO i = 1, 64
         DO j = 1, 4
            A(i) = B(i,j)*C(i,j)
         ENDDO 
      ENDDO

läßt sich abrollen zu

      DO i = 1, 64
         A(i) = B(i,1)*C(i,1) + B(i,2)*C(i,2) + B(i,3)*C(i,3) + B(i,4)*C(i,4)
      ENDDO

Die untere Codeversion bewirkt auch auf nicht-vektorisierenden Rechnern eine schnellere Ausführung: der Overhead für die innere Schleife entfällt, und der Rechner kann Zwischenresultate statt im Arbeitsspeicher in den schnelleren Registern halten. Überdies kann jetzt die größere Schleife vektorisiert werden.

Vergleiche mit REAL-Größen sollten wegen der endlichen internen Darstellungsgenauigkeit nicht auf ,,gleich`` durchgeführt werden, sondern stets auf einen Toleranzbereich (ein Epsilon $\varepsilon $);

also nicht

IF (A-B .EQ. 0.00) THEN

sondern

IF (ABS(A-B) .LT. 1.E-6) THEN

Bei Iterationsdifferenzen (Unterschied zweier aufeinanderfolgender iterierter Werte) sollte eine Abfrage auf relative Genauigkeit statt auf absolute gemacht werden, da nur dann gewährleistet ist, daß die Rechnung auf eine bestimmte Anzahl von signifikanten Stellen genau ist.

Also nicht absolut:

IF (ABS(X(i) - X(i-1)) .LE. Eps) THEN

sondern relativ:

IF (ABS(X(i) - X(i-1)) .LE. Eps*ABS(X(i)) THEN

oder äquivalent dazu und gleichzeitig effizienter:

IF (ABS(X(i-1)/X(i)-1.) .LE. Eps) THEN

Für den Fall Eps=0.01 ergibt sich dann für $\Delta $X=X(i)-X(i-1) und jeweils absolute und relative Genauigkeitsabfrage bei

 X(i) = 100.2 X(i-1) = 100.1 : $\Delta $X = 1.E-1, absolut nicht erfüllt  
         relativ erfüllt  
 X(i) = 0.1002 X(i-1) = 0.1001 : $\Delta $X = 1.E-4, absolut erfüllt  
         relativ erfüllt  
 X(i) = 0.009 X(i-1) = 0.001 : $\Delta $X = 8.E-3, absolut erfüllt  
         relativ nicht erfüllt  

Obwohl in den ersten beiden Fällen beide Werte in drei signifikanten Stellen übereinstimmen, ist die absolute Genauigkeitsabfrage einmal erfüllt, einmal nicht. Im letzten Fall ist die absolute Abfrage erfüllt, obwohl beide Werte in keiner signifikanten Stelle übereinstimmen.

Die Verwendung von Klammern (auch redundanten) verbessert die Lesbarkeit von Anweisungen erheblich und schließt Zweifel bei der Interpretation und Auswertungsreihenfolge von komplizierten Ausdrücken aus.

Vorschubsteuerzeichen für den Drucker sollten zur Vermeidung unerwünschter Effekte nicht in anderen FORMAT-Spezifikationen versteckt werden, sondern explizit angegeben werden. Also nicht:

100 FORMAT (I5, F6.2)

sondern

100 FORMAT (' ', I4, F6.2)

Alle Größen im Programm sollten grundsätzlich initialisiert, d.h. mit Anfangswerten versehen werden. Man überlasse diese Arbeit nicht dem rechner- und installationsabhängigen Betriebssystem/Compiler. Schon oft haben Programme mit vom Betriebssystem zu Null initialisierten Größen gerechnet und dabei vernünftig aussehende, jedoch falsche Ergebnisse produziert.

Alle Größen im Programm sollten in expliziten Typ-Anweisungen definiert werden, d.h. man sollte auf Typzuweisung nach Konvention durch den Anfangsbuchstaben der Variablen und auf die DIMENSION-Anweisung (ohne eine damit verbundene IMPLICIT-Anweisung) verzichten.

Parameterübergabe an Unterprogramme sollte im Hinblick auf klare Schnittstellen durch Parameterlisten erfolgen. Die Verwendung von COMMON-Blöcken läßt zwar effizientere Programme zu, birgt aber auch einige Risiken in sich (siehe 4.2.20 ,,Speicherplatz-Zuordnung [...]``).

Generell läßt sich der Widerspruch zwischen effizientem und stilvollem Programmieren nicht auflösen. Effizienz und Stil schließen sich meist gegenseitig aus. Aus Gründen des Selbstschutzes sollte man dem Motto ,,Stil immer da, wo möglich -- Effizienz dort, wo unbedingt nötig`` folgen.

Zum Testen von Programmen, die im Entstehen oder in der Veränderung begriffen sind, noch einige Hinweise.

Zunächst sollen die wichtigsten Fehlerarten erläutert werden, die während der Programmentwicklung auftreten können: Syntax-, Compilations-, Link-, logische und Laufzeitfehler. Die Schwere der Fehler wird oftmals noch unterschieden nach: Hinweis (Comment), Vorsicht (Caution), Warnung (Warning) und Fatal. Fatale Fehler müssen auf jeden Fall beseitigt werden, da sie so schwerwiegend sind, daß sie ein lauffähiges Programm verhindern. Warnungen deuten meist darauf hin, daß trotz formal korrektem Programm ein Fehler wahrscheinlich ist. Generell sollten alle Meldungen beachtet werden, auch wenn es sich dabei ,,nur`` um Hinweise handelt.

Syntaxfehler sind Verletzungen der FORTRAN-Syntax auf der Ebene einzelner Wörter (lexikalisch) und einzelner Anweisungen (syntaktische Struktur). Beispiele hierfür sind

Diese Fehler werden im ersten Übersetzungsdurchgang vom Compiler erkannt und als Syntaxfehler ausgeworfen. Von Programmen mit Syntaxfehlern werden keine Objektdateien erzeugt, es kann somit kein lauffähiges Programm erhalten werden.

Compilationsfehler treten auf, wenn das Programm zwar syntaktisch richtig ist, aber dennoch kein lauffähiger Objektcode erzeugt werden kann. Meist werden compilerinterne Grenzen (heap, stack, table sizes) oder Systemgrenzen (segment size) überschritten. Neben diesen sind Beispiele für Compilationsfehler

Linkfehler treten in der letzten Phase der Erzeugung eines lauffähigen Programms auf. Neben den Fehlern aufgrund der Überschreitung linkerinterner Grenzen machen sich sich beispielsweise folgende Fehler bemerkbar:

Laufzeitfehler ergeben sich erst nach dem Start eines ausführbaren Programms, das fehlerfrei übersetzt und gelinkt werden konnte. Häufig vorkommende Fehler und Ursachen sind:

Logische Fehler entstehen bei ungenügend durchdachter Umsetzung einer Aufgabenstellung in einen Programmentwurf. Strikte Verwendung von Struktogrammen vor der Codierung sollte solche Fehler weitestgehend vermeiden helfen.

Ein Programm sollte nach jeder Änderung getestet werden; niemals mehrere Veränderungen gleichzeitig durchführen. Tests mit vorher festgelegten Testdaten durchführen und neben den eigentlichen Ergebnissen auch Zwischenergebnisse ausdrucken lassen; so lassen sich oft logische Fehler frühzeitig erkennen. Bei der Fehlersuche (Laufzeitfehler, logische Fehler) läßt man sich ,,verdächtige`` Größen durch ins Programm gestreute WRITE- oder PRINT-Anweisungen ausdrucken. Verdächtige Programmbereiche lassen sich so sukzessive eingrenzen. Für spätere Fehlersuchzwecke kann man diese Anweisungen statt sie zu entfernen einfach Kommentarisieren, d.h. ein ! davor setzen. Die Technik des Kommentarisierens empfiehlt sich auch bei Änderungen im Programmcode, wenn Anweisungsblöcke durch andere ersetzt werden. Dies kann allerdings ungewollte logische Fehler zur Folge haben: nicht alle unnötigen Anweisungen sind kommentarisiert oder notwendige Anweisungen sind aus Versehen noch kommentarisiert.

Bei der Fehlersuche macht sich auch modularisierte Programmierung bezahlt. Compilations- und Laufzeitfehler lassen sich besser lokalisieren und damit schneller beseitigen. Korrekte Programmeinheiten brauchen nicht jedesmal neu übersetzt werden.

Laufzeitfehler sind oft auch auf falsche Variablennamen aufgrund von Tippfehlern zurückzuführen. Solche lassen sich leicht lokalisieren, wenn man im Programm mit

IMPLICIT NONE

die implizite Typvereinbarung nach der Namenskonvention unwirksam macht. Damit müssen alle Variablen explizit deklariert werden, was ohnehin dringend angeraten wird.


next up previous contents
Nächste Seite: FORTRAN-Knigge Aufwärts: Programmiersprachen, FORTRAN Vorherige Seite: Weitere Sprachmittel   Inhalt
Lars Tornow 2003-03-31