Fortran: Fortran 95: Verzweigungen und Schleifen

Aus Wikibooks
Zur Navigation springen Zur Suche springen
<<< zur Fortran-Startseite
<< Fortran 95 Fortran 2003 >>
< Stringoperationen Datentypen höherer Genauigkeit >

Einleitung[Bearbeiten]

Neben der sequentiellen Programmausführung bietet Fortran 90/95 andere grundlegende Kontrollstrukturen zur Steuerung des Programmablaufs.

Verzweigungen Disambig gray.svg Schleifen Loop icon.svg Terminierung Term icon.svg
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.

if-Verzweigungen[Bearbeiten]

Der if-Einzeiler[Bearbeiten]

Form Strukturbild
if( logischer ausdruck ) anweisung
If1.png

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

if-then[Bearbeiten]

Kurzform Langform Strukturbild
if( logischer ausdruck ) then
  anweisungsblock
end if
name: if( logischer ausdruck ) then
  anweisungsblock
end if name
If2.png

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

if-then-else[Bearbeiten]

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
If3.png

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

else-if[Bearbeiten]

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 If4.png

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

Die select case-Verzweigung[Bearbeiten]

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
Case1.png

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

do-Schleifen[Bearbeiten]

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

do [...]
 ...
end do

do-if-exit[Bearbeiten]

Kurzform Langform Strukturbild
do
  anweisungsblock1
  if( logischer ausdruck ) exit
  anweisungsblock2
end do
name: do
  anweisungsblock1
  if( logischer ausdruck ) exit
  anweisungsblock2
end do name
Do1.png

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

Die do-Zählschleife[Bearbeiten]

Kurzform Langform
do zählvariable = startwert, endwert [, schrittweite]
  anweisungsblock
end do
name: do zählvariable = startwert, endwert [, schrittweite]
  anweisungsblock
end do name
Strukturbild Do2.png

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

do while[Bearbeiten]

Kurzform Langform Strukturbild
do while( logischer ausdruck )
  anweisungsblock
end do
name: do while( logischer ausdruck )
  anweisungsblock
end do name
Do3.png

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

Die implizite do-Liste[Bearbeiten]

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

Spezialkonstrukte für Felder[Bearbeiten]

where[Bearbeiten]

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.

Der where-Einzeiler[Bearbeiten]

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

where-elsewhere[Bearbeiten]

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

forall[Bearbeiten]

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.

Der forall-Einzeiler[Bearbeiten]

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

Das "forall construct"[Bearbeiten]

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.

Terminierung[Bearbeiten]

stop[Bearbeiten]

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

exit[Bearbeiten]

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

cycle[Bearbeiten]

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

<<< zur Fortran-Startseite
<< Fortran 95 Fortran 2003 >>
< Stringoperationen Datentypen höherer Genauigkeit >