Neben der sequentiellen Programmausführung bietet Fortran 90/95 andere grundlegende Kontrollstrukturen zur Steuerung des Programmablaufs.
Verzweigungen
|
Schleifen
|
Terminierung
|
if
select case
|
do
|
stop
exit
cycle
|
Zwecks komfortabler Manipulation von Feldinhalten stehen zusätzliche Spezialkonstrukte zur Verfügung:
All diese Programmelemente werden in diesem Kapitel detailliert dargestellt. Neben den genannten Steuerkonstrukten sind in Fortran 90/95 auch noch das berüchtigte goto
und einige als veraltet gekennzeichnete Sprunganweisungen möglich. Fortran 90 und Fortran 95 unterscheiden sich in Details bei Schleifen und Verzweigungen. Da Fortran 90 durch Fortran 95 bzw. beide durch den Fortran 2003-Standard abgelöst wurden, wird auf die Unterschiede nicht näher eingegangen.
Form
|
Strukturbild
|
if( logischer ausdruck ) anweisung
|
|
|
Der if-Einzeiler oder das "if statement" ist die einfachste Form, um in Fortran-Programmen eine Verzweigung zu realisieren. Ist der angegebene logische Ausdruck wahr, dann wird die Anweisung ausgeführt, sonst nach dem if-Einzeiler mit der Programmausführung fortgesetzt. Dieses Konstrukt kann nur eine einzelne Anweisung verdauen!
Die gesamte if-Struktur inklusive Anweisung muss in einer Zeile angegeben werden. Eine konventionelle Zeilenfortsetzung mit dem &-Zeichen ist aber möglich. Einige Anweisungen sind nicht erlaubt. So kann die Anweisung nicht wieder ein "if statement" sein (geschachtelte if-Einzeiler). Ebenso darf der Anweisungsteil nicht aus end program
, end function
oder end subroutine
bestehen.
Beispiel:
program bsp
implicit none
integer :: i
i = 2
! Der logische Ausdruck ist .true. , "Hallo" wird ausgegeben
if (i == 2) print *, 'Hallo'
! Das funktioniert nicht und ist deshalb auskommentiert
! if (i == 2)
! print *, 'Hallo'
! So geht's aber
if (i == 2) &
print *, 'Hallo'
! Folgendes wiederum wird nicht das ev. erhoffte Resultat zeigen (= keine
! Ausgabe, da der logische Ausdruck .false.) Die durch Strichpunkt
! abgetrennte write-Anweisung gehört nicht mehr zum "if statement",
! sondern ist schon eine eigenständige Anweisung ausserhalb des
! if-statements
if (i == 3) print *, 'Hallo 1'; print *, 'Hallo 2'
! Ausgabe:
! Hallo
! Hallo
! Hallo 2
end program
Kurzform
|
Langform
|
Strukturbild
|
if( logischer ausdruck ) then
anweisungsblock
end if
|
|
name: if( logischer ausdruck ) then
anweisungsblock
end if name
|
|
|
Das if-then-Konstrukt erlaubt eine konventionelle einseitige Verzweigung. Im Gegensatz zum if-Einzeiler ist hier ein Anweisungsblock mit beliebig vielen Anweisungen erlaubt. Einem if-then-Konstrukt, wie auch den meisten nachfolgenden Verzweigungs- und Schleifentypen, kann eine Bezeichnung mitgegeben werden.
Beispiel:
program bsp
implicit none
integer :: i
i = 2
if (i == 2) then
print *, 'Hallo1'
end if
CHECKIT: if (i /= 3) then
print *, 'Hallo2'
exit CHECKIT
print *, 'Hallo3' ! <- wird nicht mehr ausgefuehrt
end if CHECKIT
! Ausgabe:
! Hallo1
! Hallo2
end program
Kurzform
|
Langform
|
Strukturbild
|
if( logischer ausdruck ) then
if-anweisungsblock
else
else-anweisungsblock
end if
|
|
name: if( logischer ausdruck ) then
if-anweisungsblock
else name
else-anweisungsblock
end if name
|
|
|
Dies ist eine typische zweiseitige Verzweigung. Je nachdem, ob die Bedingung zu wahr oder falsch ausgewertet wird, wird der if- oder der else-Anweisungsblock ausgeführt, um nach dem Verzweigungsende wieder den gleichen Programmcode abzuarbeiten. Bei der Langform (benanntes if-then-else-Konstrukt) ist übrigens auch noch eine andere Variante möglich. So könnte der Bezeichner name
nach else
auch weggelassen werden. Nach dem end if
ist er, sofern die benannte Form gewählt wurde, aber auf jeden Fall anzugeben.
Beispiel:
program bsp
implicit none
real :: t, tc, tr
character :: conv
print *, "Gib Temperatur in Kelvin ein:"
read (*,*) t
CHECKRANGE: if (t >= 0.0) then
print *, "Umrechnung (c -> in Celsius, anderes Zeichen -> in Rankine):"
read (*,*) conv
if (conv == "c") then
tc = t - 273.15
print *, tc
else
tr = 1.8 * t
print *, tr
end if
else CHECKRANGE
print *, "Fehler: t < 0.0 K"
end if CHECKRANGE
end program
Kurzform
|
Langform
|
if (logischer ausdruck 1) then
if-anweisungsblock 1
else if (logischerAusdruck 2) then
if-anweisungsblock 2
else if (logischerAusdruck n) then
if-anweisungsblock n
else
else-anweisungsblock
end if
|
|
name: if (logischer ausdruck 1) then
if-anweisungsblock 1
else if (logischerAusdruck 2) then name
if-anweisungsblock 2
else if (logischerAusdruck n) then name
if-anweisungsblock n
else name
else-anweisungsblock
end if name
|
|
Strukturbild
|
Das ist die allgemeinste Form der if-Verzweigung und wird im Fortran-Standard als "if construct" bezeichnet. Die vorher beschriebenen Formen sind nur vereinfachte Spezialfälle dieses Verzweigungstyps.
Beispiel:
program bsp
implicit none
integer :: i
print *, "Gib eine natuerliche Zahl ein:"
read (*,*) i
! *** Langform ***
I999: if (i == 1) then
print *, "A"
else if ((i > 1) .and. (i <= 5)) then I999
print *, "BCDE"
else if ((i > 5) .and. (i <= 11)) then I999
print *, "FGHIJK"
else I999
print *, "L-Z"
end if I999
! *** Das Gleiche in Kurzform ***
if (i == 1) then
print *, "A"
else if ((i > 1) .and. (i <= 5)) then
print *, "BCDE"
else if ((i > 5) .and. (i <= 11)) then
print *, "FGHIJK"
else
print *, "L-Z"
end if
end program
Kurzform
|
Langform
|
Strukturbild
|
select case( variable )
case( fall1 )
anweisungsblock1
! ...
case( falln )
anweisungsblockn
[case default
anweisungsblockdefault]
end select
|
|
name: select case( variable )
case( fall1 ) name
anweisungsblock1
! ...
case( falln ) name
anweisungsblockn
[case default name
anweisungsblockdefault]
end select name
|
|
|
Eine andere Möglichkeit zur Erstellung von Verzweigungen stellt die "select case"-Steueranweisung (das "case construct") bereit. Die variable
kann vom Typ integer
, logical
oder character
sein. Die Fälle werden durch Konstanten repräsentiert. Es muss nicht jeder Fall einzeln angeschrieben werden. Fälle, die zwar die gleichen Aktionen ausführen sollen, aber durch verschiedene Konstanten dargestellt werden, können zu einem Fall zusammengezogen werden. Optional kann auch noch ein "Default"-Fall angegeben werden.
Möglichkeiten um Fälle festzulegen:
n |
Einzelne Konstante |
gleich n
|
n1, n2, n3 |
Fallliste |
n1 oder n2 oder n3
|
n: |
Bereich |
von n bis ...
|
:m |
Bereich |
von ... bis m
|
n:m |
Bereich |
von n bis m
|
Die in der Tabelle gelisteten Alternativen können natürlich auch kombiniert werden, z.B. eine Liste aus Einzelkonstanten und Bereichen. Bei logischem Datentyp ist die Angabe von Bereichen natürlich unsinnig.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i
read( *, * ) i
select case( i )
case( :111 )
write( *, * ) 'Fall 1'
case( 112:332, 334 )
write( *, * ) 'Fall 2'
case( 333 )
write( *, * ) 'Fall 3'
case default
write( *, * ) 'unspezifiziertes Ereignis'
end select
! Ausgabe (Eingabe: 222):
! Fall 2
! Ausgabe (Eingabe: -5):
! Fall 1
! Ausgabe (Eingabe: 333):
! Fall 3
! Ausgabe (Eingabe: 5000):
! unspezifiziertes Ereignis
end program bsp
|
Fortran kennt nur einen allgemeinen Schleifentyp – die do-Schleife. Diese do-Schleife gibt es aber in verschiedenen Ausprägungen, so dass damit ziemlich alle denkbaren Einsatzfälle abgedeckt sind.
Im Prinzip haben alle do-Schleifen die Form
Kurzform
|
Langform
|
Strukturbild
|
do
anweisungsblock1
if( logischer ausdruck ) exit
anweisungsblock2
end do
|
|
name: do
anweisungsblock1
if( logischer ausdruck ) exit
anweisungsblock2
end do name
|
|
|
Lässt man den anweisungsblock1
weg, dann erhält man eine kopfgesteuerte Schleife. Entfällt der anweisungsblock2
, so ist die Schleife fußgesteuert. Ohne Abbruchbedingung ist dies eine Endlosschleife.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i = 1
do
write( *, * ) i
i = i + 1
if( i > 10 ) exit
end do
write( * , * ) "Die do-Schleife wurde beendet"
! Ausgabe
! 1
! 2
! ...
! 10
! Die do-Schleife wurde beendet
end program bsp
|
Kurzform
|
Langform
|
do zählvariable = startwert, endwert [, schrittweite]
anweisungsblock
end do
|
|
name: do zählvariable = startwert, endwert [, schrittweite]
anweisungsblock
end do name
|
|
Strukturbild
|
Zählvariable, Startwert, Endwert und Schrittweite müssen vom Typ integer
sein. Die Zählvariable darf in der Schleife nicht manipuliert werden. Wird die Schrittweite nicht explizit vorgegeben, so hat sie den Wert 1.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i
do i = 1, 10
write( *, * ) i
end do
! Zeilenweise Ausgabe der Zahlen 1 bis 10
end program bsp
|
Kurzform
|
Langform
|
Strukturbild
|
do while( logischer ausdruck )
anweisungsblock
end do
|
|
name: do while( logischer ausdruck )
anweisungsblock
end do name
|
|
|
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i
i = 0
do while( i < 5 )
write( *, * ) i
i = i + 1
end do
! Die Zahlen 0 bis 4 werden ausgegeben
end program bsp
|
Bei Eingabe oder Ausgabe ist die Angabe einer impliziten do-Liste möglich.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i
write( *, * ) ( 'Hallo', i = 1, 10 )
! Ausgabe: HalloHalloHalloHalloHalloHalloHalloHalloHalloHallo
end program bsp
|
Sollen in Feldern Elemente manipuliert werden, so können die where
-Anweisungen (das "masked array assignment") hilfreich sein. Dabei lassen sich Feldelementen in Abhängigkeit von vorgegebenen Bedingungen (Maske) neue Werte zuweisen.
Form
|
where( bedingung ) variable = ausdruck
|
|
Das "where statement" muss komplett in eine Zeile geschrieben werden, wobei Zeilenumbrüche mit dem &-Zeilenumbruchsmarker jedoch erlaubt sind. Für komplexere Probleme ist das im nächsten Abschnitt vorgestellte "where construct" (where-elsewhere) vorgesehen.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer, dimension(5) :: arr = (/ 5, 1, -1, 1, 7 /)
write( *, * ) arr
! Ausgabe: 5 1 -1 1 7
where( arr >= 3 ) arr = 99999
write(*,*) arr
! Ausgabe: 99999 1 -1 1 99999
end program bsp
|
Kurzform
|
Langform
|
where( bedingung1 )
anweisungsblock
[elsewhere( bedingung2 )
anweisungsblock2]
[elsewhere( bedingungn )
anweisungsblockn]
[elsewhere
anweisungsblockm]
end where
|
|
name: where( bedingung1 )
anweisungsblock
[elsewhere( bedingung2 ) name
anweisungsblock2]
[elsewhere( bedingungn ) name
anweisungsblockn]
[elsewhere name
anweisungsblockm]
end where name
|
|
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer, dimension(10) :: arr = (/ 5, 1, -1, 1, 7, -3, 7, 6, -9, 8 /)
where( arr <= 0 )
arr = 0
elsewhere( arr > 0 .and. arr <= 3 )
arr = 1
elsewhere
arr = 2
end where
write(*,*) arr
! Ausgabe: 2 1 0 1 2 0 2 2 0 2
end program bsp
|
Auch die forall
-Schleife ist für den Einsatz bei Feldern gedacht. Im Gegensatz zum "where construct" werden hier die ausgewählten Feldelemente in erster Linie über die Indizes und erst in zweiter Linie über den Wert bestimmt.
Formen
|
forall( index = subscript:subscript[:stride] ) anweisung
|
|
forall( ind1 = subs1a:subs1b[:str1], ind2 = subs2a:subs2b[:str2] [, indn = subsna:subsnb[:strn]] ) anweisung
|
|
Die erste angegebene Form ist für eindimensionale Felder gedacht, die zweite für mehrdimensionale Felder. Über subscript
kann der Indexbereich festgelegt werden. Das optionale stride
gibt die Schrittweite vor. Wird dieser Wert nicht angegeben, so ist die Schrittweite = 1.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /)
integer, dimension(2, 2) :: b = reshape( (/ 0, 2, -1, 5 /), (/ 2, 2 /) )
integer :: i, j
forall( i = 1:3 ) a(i) = 0
write( *, * ) a
! Ausgabe: 0 0 0 4 5
forall( i = 1:5:2 ) a(i) = -1
write( *, * ) a
! Ausgabe: -1 0 -1 4 -1
forall( i = 1:5 ) a(i) = max ( a(i), 3 )
write( *, * ) a
! Ausgabe: 3 3 3 4 3
forall( i = 1:2, j = 2:2 ) b(i, j) = -9
write( *, * ) b
! Ausgabe: 0 2 -9 -9
end program bsp
|
Aber das ist nicht alles, was der forall-Einzeiler zustande bringt. Zusätzlich zur Feldelementbestimmung über Indexbereiche kann auch eine Maske vorgegeben werden. Die forall-Schleife ist also auch eine erweiterte where-Anweisung.
Form
|
forall( ind1 = s1a:s1b[:s1] [, indn = sna:snb[:sn] [, maske] ) anweisung
|
|
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /)
integer :: i, j
forall( i = 1:4, a(i) > 2 ) a(i) = 0
write( *, * ) a
! Ausgabe: 1 2 0 0 5
end program bsp
|
Bei der Verwendung von forall-Anweisungen sind einige Prämissen zu beachten. So müssen nebst anderem in Masken oder im Anweisungsbereich verwendete Funktionen pure
sein. Was das genau bedeutet wird später im Kapitel Unterprogramme erläutert
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer, dimension(5) :: a = (/ 1, 2, 3, 4, 5 /)
integer :: i
! Die Funktion berechne_1() darf nicht innerhalb einer forall-Anweisung
! aufgerufen werden, da sie nicht "pure" ist
! forall( i = 1:4, a(i) > 2 ) a(i) = berechne_1( a(i) ) ! FEHLERMELDUNG
! Die Funktion berechne_2 macht das Gleiche wie berechne_1(),
! ist aber als "pure" gekennzeichnet und darf in der forall-Anweisung
! aufgerufen werden
forall( i = 1:4, a(i) > 2 ) a(i) = berechne_2( a(i) ) ! OK
write( *, * ) a
! Ausgabe: 1 2 9 12 5
contains
integer function berechne_1( val )
integer, intent( in ) :: val
integer :: res
res = val * 3
berechne_1 = res
end function berechne_1
pure integer function berechne_2( val )
integer, intent( in ) :: val
integer :: res
res = val * 3
berechne_2 = res
end function berechne_2
end program bsp
|
Kurzform
|
forall( index = subscript:subscript[:stride] )
anweisungsblock
end forall
|
|
Langform
|
name: forall( index = subscript:subscript[:stride] )
anweisungsblock
end forall name
|
|
Es gilt im Prinzip das gleiche wie für den forall-Einzeiler. Es sind innerhalb der Schleife eben mehrere Anweisungen erlaubt. Es gelten auch die Formen für mehrdimensionale Felder und mit Vorgabe einer Maske. Diese sind in diesem Abschnitt aber nicht mehr explizit angeführt.
Die stop
-Anweisung beendet das Programm.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
write( *, * ) 'Vor Stop-Statement'
stop
write( *, * ) 'Nach Stop-Statement'
! Ausgabe:
! Vor Stop-Statement
end program bsp
|
Der stop-Anweisung kann auch ein "stop-code" mitgegeben werden. Dieser "stop-code" kann eine Ganzzahl (maximal 5-stellig) oder eine Zeichenkette sein. Der "stop-code" wird bei der Programmtermination ausgegeben. Wo die Ausgabe erfolgt und wie sie aussieht ist aber betriebssystem- und compilerabhängig.
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i
read( *, * ) i
if( i == 1 ) then
stop 999
else
stop "Sonstiger Stop"
end if
! Ausgabe (bei Eingabe von 1):
! ifort (Linux):
! 999
! gfortran, g95 (Linux):
! STOP 999
! Sun f95 (Linux):
! STOP: 999
end program bsp
|
Mit der Anweisung exit
kann eine do-Schleife verlassen werden. Wird eine do-Schleife mit einer Bezeichnung versehen, so kann bei der exit
-Anweisung explizit auf die benannte Schleife Bezug genommen werden. Wird kein Schleifenname angegeben, so bezieht sich die exit
-Anweisung immer auf die innerste Schleife, mit der sie im Zusammenhang steht.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i, j
outerloop: do i = 1, 5
write( *, * ) "Outer", i, "Beginn"
innerloop: do j = 1, 3
if( i == 1 ) exit
if( i == 2 ) exit innerloop
if( i == 4 ) exit outerloop
write( *, * ) "Inner", j
end do innerloop
write( *, * ) "Outer", i, "Ende"
end do outerloop
! Ausgabe:
! Outer 1 Beginn
! Outer 1 Ende
! Outer 2 Beginn
! Outer 2 Ende
! Outer 3 Beginn
! Inner 1
! Inner 2
! Inner 3
! Outer 3 Ende
! Outer 4 Beginn
end program bsp
|
Mit cycle
wird der aktuelle do-Schleifendurchlauf beendet und wieder zum Schleifenkopf gesprungen. Wird eine do-Schleife mit einer Bezeichnung versehen, so kann bei der cycle
-Anweisung explizit auf die benannte Schleife Bezug genommen werden. Wird kein Schleifenname angegeben, so bezieht sich die cycle
-Anweisung immer auf die innerste Schleife, mit der sie im Zusammenhang steht.
Beispiel:
Fortran 90/95-Code (free source form)
|
program bsp
implicit none
integer :: i, j
outerloop: do i = 1, 5
write( *, * ) "Outer", i, "Beginn"
innerloop: do j = 1, 3
if( i == 1 ) cycle outerloop
if( i == 4 ) cycle innerloop
if( i == 5 ) cycle
write( *, * ) "Inner", j
end do innerloop
write( *, * ) "Outer", i, "Ende"
end do outerloop
! Ausgabe:
! Outer 1 Beginn
! Outer 2 Beginn
! Inner 1
! Inner 2
! Inner 3
! Outer 2 Ende
! Outer 3 Beginn
! Inner 1
! Inner 2
! Inner 3
! Outer 3 Ende
! Outer 4 Beginn
! Outer 4 Ende
! Outer 5 Beginn
! Outer 5 Ende
end program bsp
|