Fortran im Wandel der Zeit

Fortran 90

Next Generation Fortran

Mit der Version 90 beginnt gewissermaßen die zweite Epoche der Sprache Fortran. Mit ihr wurden viele der genannten Schwächen und Mängel beseitigt. Die meisten Compiler identifizieren Fortran-90-Programme anhand der Endung .f90. Um den Code besser abgrenzen zu können, sind entsprechende Programme häufig durchgängig kleingeschrieben, obwohl die Sprache weiterhin keinen Unterschied zwischen Groß- und Kleinschreibung macht. Oft lässt sich zudem auf den Vorschlag treffen, die Rümpfe von Subroutinen und Funktionen nicht einzurücken. Apples Entwicklungsumgebung Xcode, die ein Fortran-Syntax-Highlighting hat, formatiert Fortran-Code zum Beispiel automatisch so, was allerdings nicht unbedingt zur Lesbarkeit beiträgt. Eine strenge Regel gibt es nicht, sodass sich Entwickler bei Legacy-Code auf allerhand mehr oder weniger wilde Formatierungen einstellen sollten.

Die meisten Compiler ermöglichten mit Einführung der neuen Version ebenfalls einen vorgeschalteten Durchlauf des C-Präprozessors, meist bei Verwendung der Dateiendung .F. Damit wurden die alten Fortran-include-Statements oft durch #include-Präprozessor-Direktiven ersetzt. Präprozessoranweisungen werden allerdings oft mit ihren #if-Direktiven zum bedingten Kompilieren verwendet, was für extrem schwer lesbaren Code sorgen kann. In manchen Programmcodes finden sich Subroutinen, die direkt mit einem #if beginnen und dem zugehörigen #endif enden. Der Compiler übersetzt den gesamten Code folglich nur bei entsprechendem Wert in der in der Direktiven genannten Variable. Ein sinnvoller Einsatz von Optionsdateien, die Parameter setzen mit denen sich zur Laufzeit der Aufruf entsprechender Funktionen steuern lässt, ist leider nicht immer gegeben.

Wesentlich in Fortran 90 ist zunächst die Einführung des sogenannten Free Format, bei dem die alte Regelung der Spaltenbedeutungen aufgehoben wurde. Die Continuation Mark ist nun ein <I>&<I> am Ende der Zeile, die mit der nachfolgenden zu verbinden ist. Eine modernere Strukturierung von Schleifen ermöglichen die besser lesbaren do-Anweisungen mit einem enddo statt der Kombination aus Label und continue am Ende sowie die do-while-Variante. Hinzu kommen eine break-Anweisung zum vorzeitigen Verlassen von Schleifen und ein case/switch-Konstrukt. Sie bieten Fortran 90 die Optionen, die in anderen Sprachen wie C vorhanden sind.

Listing 8 zeigt die Fortran-90-Version des Programms aus Listing 1 im Free Format:

1  program fortran90
2 implicit none
3 real x, y, sum
4 integer n, k
5 ! berechne Maschinengenauigkeit:
6 x = 1.0
7 do while (1.0+x > 1.0)
8 y = x
9 x = x/2.0
10 enddo
11 print*, 'Maschinengenauigkeit = ', y
12
13 ! berechne Summe:
14 write(*,*) 'Bitte n eingeben:'
15 read(*,*) n
16 sum = 0.0
17 do k=1, n
18 sum = sum + 1.0/k
19 enddo
20 write(*,'(a,i3,2x,a,f10.2)'), ↲
'Summe der Kehrwerte der ersten ', ↲
n, ' natuerlichen Zahlen = ', sum
21 end program

Listing 8: Fortran-90-Programm

Es gibt nun eine do-while-Schleife (Zeilen 7-10), das continue ist durch ein enddo ersetzt und die Labels werden überflüssig. Ein ! leitet Kommentare ein: Alles, was in der Zeile folgt, ignoriert der Compiler. Die alten Vergleichsoperatoren bleiben gültig, werden aber durch die gebräuchlichere Form ergänzt (> alternativ zu .gt. für "greater than" im Beispiel, vgl. das .eq. für equal in Listing 1). Zudem kommt die implicit none-Anweisung zum Einsatz, sie erzwingt die Deklaration aller Variablen und muss direkt zu Beginn jedes Programms und jeder Funktion beziehungsweise Subroutine stehen, für die sie gilt.

Die Einführung dynamischer Felder (Arrays) ist eine weitere wesentliche Neuerung in Fortran 90. Ein Beispiel ist in Listing 9 zu sehen:

1  program arrays
2 implicit none
3 integer :: n = 3
4 real(8), allocatable :: x(:)
5 ! real(8), allocatable, dimension(:) :: x
6 real(8), allocatable :: A(:,:)
7 real(8), allocatable, target :: y(:)
8 real(8), pointer :: p(:)
9 real(8) d
10 allocate(x(n))
11 allocate(y(n))
12 allocate(A(n, n))
13 x = 1.0d0
14 y = 2.0d0*x - 3.0d0
15 p => y
16 A = 3.0d0
17 d = dot_product(x, y)
18 A = matmul(A, A)
19 write(*,*) d
20 write(*,*) x
21 write(*,*) y
22 write(*,*) p
23 write(*,*) A
24 deallocate(x, y, A)
25 end program

Listing 9: Dynamische Felder

Das Programm erzeugt in Zeile 4 ein dynamisches Feld, gibt ihm in Zeile 9 Speicherplatz und gibt letzteren in Zeile 23 wieder frei. Die doppelten Doppelpunkte in der Deklaration sind jetzt notwendig, weil das Schlüsselwort allocatable anzugeben ist. Im Listing findet die erwähnte moderne Schreibweise real(8) für Gleitkommazahlen doppelter Genauigkeit Verwendung. Die auskommentierte Zeile 5 zeigt eine äquivalente Schreibweise. In Zeile 6 ist das Anlegen eines zweidimensionalen Felds zu sehen und in Zeile 8 ein Zeiger, der auf ein eindimensionales Feld zeigen soll, was er in Zeile 15 tut. Variablen, auf die ein Pointer zeigen soll, müssen das target-Attribut erhalten. Die Fortran-90-Pointer sind nicht mit denen aus C vergleichbar oder kompatibel. Die Umsetzung dynamischer Arrays ist insgesamt weniger fehleranfällig als in C, weil dafür selbst keine Pointer nötig sind. Darüber hinaus wurden Operationen auf ganzen Feldern ermöglicht (Zeilen 13, 14 und 16) und diverse Funktionen auf Feldern eingeführt (im Listing in Zeilen 17, 18 für das Skalar- und Matrixprodukt), was für wesentlich kürzeren und besser lesbaren Code sorgt und der Matlab-Syntax ähnelt. Die Endung d0 bei den Konstanten in Zeilen 13, 14 und 16 ist die korrekte Schreibweise im wissenschaftlichen Format für doppelt genaue Konstanten (daher d), mit Mantisse (vor dem d) und Exponent (hinter dem d). Das wäre an der Stelle nicht nötig, da sonst wie auch in C eine automatische Konvertierung von einfacher Genauigkeit in den Typ der Variablen (also doppelte Genauigkeit) stattfände.

Derived Types und Module

Eine wichtige Ergänzung in Fortran 90 sind selbstdefinierte, strukturierte Datentypen, sogenannte "Derived Types", die im Verlauf noch beschrieben werden. Außerdem hat das Standardisierungsgremium das Konzept der Module eingeführt, die unter anderem ein Ersatz für Common-Blöcke sind. Wie in Letzteren lassen sich dort globale Variablen zusammenfassen. Danach ist es möglich, auf sie von jeder Funktion oder Subroutine mit der Anweisung use <Modulname> zuzugreifen. Mit use <Modulname>, only: <Variablenname> ist die Nutzung ebenfalls eingeschränkt möglich. Listings 10 und 11 zeigen die Alternative zu den Common-Blöcken aus Listing 5 beim Verwenden der Module: Das Modul in Listing 10 enthält die Deklaration der beiden Variablen, in Listing 11 wird das Modul eingebunden und es lässt sich auf globale Variablen zugreifen.

1  module xy_module
2 real x, y
3 end module

Listing 10: Definition eines Moduls

1  program modules
2 use xy_module
3 x = 1.0
4 y = 2.0
5 write(*,*) 'im Hauptgrogramm: ', x, y
6 call sub()
7 end program
8
9 subroutine sub()
10 use xy_module
11 write(*,*) 'in der Subroutine: ', x, y
12 end subroutine

Listing 11: Verwendung des Moduls

Zu beachten ist, dass das Modul zuerst zu kompilieren ist. Die entstehende .mod-Datei ist im Anschluss zum Kompilieren der das Modul benutzenden Codeteile nötig.

Dabei entspricht der Name der .mod-Datei dem des Moduls, nicht dem der Quelldatei des Moduls. Das Generieren von Makefiles mit korrekten Abhängigkeiten ist damit nicht ganz trivial. Die .mod-Dateien sind außerdem zwischen den Compilern nicht unbedingt kompatibel, was bei plattformübergreifendem Übersetzen ein Problem darstellen kann.

Das simple Nutzen von Modulen als Ersatz von Common-Blöcken und damit als Ort der Deklaration globaler Variablen findet man oft in Legacy-Code. Module werden unübersichtlich, wenn sie wieder use-Anweisungen enthalten und andere Module einbinden, was eine Verschachtelung bewirkt. Oft ist Detektivarbeit nötig, um in komplexem Code wirklich die Abhängigkeiten von in Modulen definierten und mit ihnen eingebundenen globalen Variablen zu finden. Nicht ohne Grund warnt jede gute Programmierausbildung vor dem Einsatz Letzterer. Doch Module können mehr, denn mit ihnen beginnt gewissermaßen das Zeitalter der Objektorientierung in Fortran. Erst mit ihr werden sie wirklich sinnvoll.