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:
where
forall
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.
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 bspimplicit noneinteger::ii=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 aberif(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-statementsif(i==3)print*,'Hallo 1';print*,'Hallo 2'! Ausgabe: ! Hallo! Hallo! Hallo 2 end program
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 bspimplicit noneinteger::ii=2if(i==2)then print*,'Hallo1'end ifCHECKIT:if(i/=3)then print*,'Hallo2'exit CHECKITprint*,'Hallo3'! <- wird nicht mehr ausgefuehrtend if CHECKIT! Ausgabe: ! Hallo1! Hallo2end program
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 bspimplicit nonereal::t,tc,trcharacter::convprint*,"Gib Temperatur in Kelvin ein:"read(*,*)tCHECKRANGE:if(t>=0.0)then print*,"Umrechnung (c -> in Celsius, anderes Zeichen -> in Rankine):"read(*,*)convif(conv=="c")thentc=t-273.15print*,tcelsetr=1.8*tprint*,trend if else CHECKRANGEprint*,"Fehler: t < 0.0 K"end if CHECKRANGEend program
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 bspimplicit noneinteger::iprint*,"Gib eine natuerliche Zahl ein:"read(*,*)i! *** Langform ***I999:if(i==1)then print*,"A"else if((i>1).and.(i<=5))then I999print*,"BCDE"else if((i>5).and.(i<=11))then I999print*,"FGHIJK"else I999print*,"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
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.
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
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
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.
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.
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
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.
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.
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
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.
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