Programmieren in Fortran 90/95


  1. Intrinsisch in Fortran enthaltene Funktionen

    In Fortran stehen eine Reihe mathematischer Funktionen, die bei der Auswertung von mathematischen, numerischen, physikalischen und technischen Zusammenhängen benötigt werden, generisch zur Verfügung.

    Wichtige Funktionen sind z.B.

Funktionsnamen in Fortran

 mathematische Bedeutung

exp(x)

 Exponentialfunktion von x

log(x)

 der natürliche Logarithmus von x ( math: ln(x) )

log10(x)

 der dekadische Logarithmus von x ( math: log(x) )

sqrt(x)

 die Quadratwurzel aus x

abs(x)

 der Betrag von x

sin(x)

 der Sinus von x ( math: sin(x) ) Achtung: x in 'Radiant' angeben

cos(x)

 der Cosinus von x (math: cos(x) ) Achtung: x in 'Radiant' angeben

tan(x)

 der Tangens von x (math: tan(x)) Achtung: x in 'Radiant' angeben

asin(x)

 arcsin(x), die Umkehrfunktion von sin(x)

acos(x)

 arccos(x), die Umkehrfunktion von cos(x)

atan(x)

 arctan(x), die Umkehrfunktion von tan(x)

Als Argumente der obigen Funktionen lassen sich

einsetzen. Ein kleines Beispielprogramm
sinus.f90.

Zur expliziten Datentyp-Konversion lässt sich u.a. verwenden:

Funktionsname in Fortran mathematische Bedeutung

int(x)

Umwandlung der real-Zahl x in den Datentyp integer durch Abschneiden der Nachkommastellen

nint(x)

Umwandlung der real-Zahl x in den Datentyp integer durch Runden

real(i)

Umwandlung der integer-Zahl i in den Datentyp real

achar(i)

gibt das  character-Zeichen zurück, welches an der i-ten Stelle in der ASCII-Tabelle steht

iachar(c)

gibt die Stelle aus, an der das character-Zeichen c in der ASCII-Tabelle steht

Beispiel zu achar und iachar: ascii_tabelle.f90.

Auch Funktionen zur Bestimmung des Minimums und des Maximums zweier Zahlen sind in Fortran eingebaut:

Funktionsname in Fortran mathematische Bedeutung

min(a,b)

 gibt als Funktionswert die kleinere Zahl von a oder b zurück

max(a,b)

 gibt als Funktionswert die größere Zahl von a oder b zurück

Beispiel zu min und max: min_max.f90.

Hinweis: Bei den Funktionen min() und max() können auch mehr als 2 Argumente angegeben werden, so dass z.B. auch leicht mit max() das Maximum dreier und mehr Zahlen bestimmt werden kann.

  • Entwurfsstrategien für Programme
  • Nur einfachste Programme zeigen einen sequentiellen oder linearen Ablauf (z.B. ein Programm, um einen DM-Betrag einzulesen und diesen in Euro umzurechnen ueb02_01.f90). Programme zur Lösung komplexerer Probleme weisen fast immer Verzweigungen und/oder Schleifen auf, so dass nur einzelne Teile des Programms ausgeführt werden, wenn bestimmte Bedingungen erfüllt sind (Verzweigungen) oder einzelne Anweisungsblöcke werden wiederholt ausgeführt (Schleifen).

    Bei der Entwicklung komplexerer Programme hat sich unter Aspekten der Aufwandsminimierung und der Fehlerreduzierung als Methode das des

    Top-Down-Entwurfs-Prinzip

    bewährt: Bei den algorithmisch orientierten Programmiersprachen kommt der Entwicklung des Lösungsverfahrens eine zentrale Bedeutung zu. Der erste Schritt hierzu besteht darin, zuerst die zugrunde liegende Problemstellung zu verstehen.

    Danach wird festgelegt, welche Eingaben das Programm benötigt und was das Programm ausgeben soll.

    Größere Probleme werden in kleine Teilschritte zerlegt, die wiederum in weitere Teilaufgaben zergliedert werden können. Zu jeden Teilschritt entwickelt der Programmierer den dazugehörigen Lösungsalgorithmus. Zur Entwicklung der Lösungsalgorithmen ist es fast immer sinnvoll, zunächst für einen einfachen Datensatz die Lösung per Hand/Taschenrechner/Computeralgebrasystem herzuleiten und die daraus gewonnenen Erkenntnisse danach weiter zu verallgemeinern.

    Als Hilfsmittel zur Entwicklung der Algorithmen und des Programmablaufs können Sie einen sogenannten Programmablaufplan (engl. flow chart) zeichnen oder den Algorithmus zunächt in Pseudocode (eine Mischung aus Fortran und Englisch) aufschreiben.

    Erst nachdem im Detail der Lösungsweg auf dem Papier ausgearbeitet wurde, beginnt die Umsetzung in Programmcode. Durch die intensive gedankliche Vorarbeit - so zeigt die Erfahrung vieler Programmentwickler - werden bereits im Vorfeld die wichtigen Details geklärt - und es entstehen übersichtlichere, struktuiertere und weniger fehleranfällige Programme als bei einem überhasteten, spontanem Programmieren. Der methodische Ansatz ist in der Regel auch schneller, das sehr viele Fehler, die bei spontanem Vorgehen erst gefunden und eliminiert werden müssen aufgrund der intensiven gedanklichen Vorarbeit gar nicht erst auftreten.

    Wurde der Programmcode geschrieben, werden am besten die einzelnen Teilschritte separat getestet und dann die Teilschritte zu dem Gesamtprogramm zusammengesetzt. Es ist absolut notwendig, das Gesamtprogramm mit allen möglichen Arten von zugelassenen Eingabedaten zu testen und sicherzustellen, dass sämtliche Verzweigungen und Schleifen richtig abgearbeitet werden.

    Deshalb sollte auf die Auswahl der Testdatensätze größte Sorgfalt verwendet werden. Die richtige Lösung für die Testdaten sollte in einem unabhängigen Verfahren (je nach Problemstellung per Hand, Taschenrecher, Computeralgebrasystem, Lösungen aus anderen (numerischen) Verfahren, von anderen Wissenschaftlern aus Publikationen, abgeleitete Werte aus einer reinen theoretischen Beschreibung) hergeleitet und mit dem Ergebnis des entwickelten Programms verglichen werden.

    Erst wenn Sie absolut sicher sind, dass Ihr Programm in allen Fällen richtig arbeitet, sollten Sie es einsetzen bzw. freigeben.

    Nochmals die Grundprinzipien des Top-Down-Entwurfs in der Übersicht:

    1. Fertigen Sie eine eindeutige und klare Problembeschreibung an.
    2. Beschreiben Sie genau, welche Informationen eingegeben und welche ausgegeben werden sollen.
    3. Arbeiten Sie für einen einfachen Datensatz die Lösung per Hand/Taschenrechner/Computer-Algebra-System aus.
    4. Strukturieren Sie nun für das generalisierte Problem die einzelnen Schritte. Teilen Sie dazu das Gesamtproblem in immer feinere Teilschritte auf, die sukzessive weiter verfeinert werden können. ("vom Groben zum Feinen = Top-Down-Entwurf") (Bei diesem Schritt können Sie Datenfluß-Diagramm oder Pseudocode zu Hilfe nehmen).
    5. Setzen Sie erst jetzt den von Ihnen entwickelten Lösungsalgorithmus in Programm-Code um. Realisieren Sie evtl. einzelne, in sich abgeschlossene Programmteile als Unterprogramme.
    6. Durchlaufen Sie den oberen Fehlersuch- und Eliminierungszyklus so oft wie nötig.
    7. Speziell bei der Auswahl Ihrer Testdaten sollten Sie sich Gedanken machen, ob die verwendeten Testdatensätze soweit wie möglich voneinander unabhängig sind und die Spezifität des Problems hinreichend verkörpern.

    Gängige Symbole für Programmablaufpläne (engl. flow charts )

    Gaengige Symbole fuer Programmablaufplaene

  • Kontrollstrukturen
    1. Verzweigungen
    2. Unter Verzweigungen versteht man in Fortran Anweisungen, die es erlauben, bestimmte Teile eines Programms auszuführen, während andere übersprungen werden.

      Die Block-if-Konstruktion ( if then - end if -Konstruktion)

      Die Struktur einer einfachen Verzweigung ist die folgende
          if ( < logischer Ausdruck > ) then
                 Anweisung 1
                 ...
                 Anweisung n
          end if
      
      Falls der logischer Ausdruck wahr ist (< logischer Ausdruck > == .TRUE.), wird der zwischen if und end if eingeschlossene Anweisungsblock ausgeführt. Falls der logische Ausdruck den Wert .FALSE. haben sollte, so wird die nach dem end if folgende Anweisung ausgeführt. In einem Programmablaufplan (oft auch Flußdiagramm oder engl. flow chart genannt) veranschaulicht folgendes symbolisches Bild den zugrunde liegenden Sachverhalt

      Block-if im Programmablaufplan

      Ein konkretes Beispiel:
      Zu der quadratischen Gleichung
         a*x**2 + b*x + c = 0, a ungleich 0
      
      sollen die reellen Nullstellen berechnet werden. Je nach Wert der Diskriminante D = b**2 - 4*a*c exisitieren zwei (D > 0), eine doppelte (D = 0) oder keine reellen Nullstellen (D < 0).

      ein konkretes Block-if Beispiel im
 Programmablaufplan

      Und als Programmcode
         if ( b**2 - 4.0*a*c < 0.0) then
            write(*,*) 'Keine reellen Nullstellen'
         end if
      

      Das if - else if - end if - Konstrukt

      hat die Form
          if ( < logischer Ausdruck 1 > ) then
                 Anweisung 1
                 ...
                 Anweisung n
                 else if ( < logischer Ausdruck 2 > ) then
                           Anweisung m
                           ...
                           Anweisung o
                           else
                              Anweisung p
                              ...
                              Anweisung q
          end if
      
      und lässt sich graphisch im flow chart darstellen als

      if - else if - else - end if - Konstrukt im Programmablaufplan

      Im Programmcode könnte man z.B. im konkreten Beispiel schreiben
         implicit none
         real :: diskriminante
         diskriminante = b**2 - 4.0*a*c 
      
         if ( diskriminante < 0.0) then
            write(*,*) 'Keine reellen Nullstellen'
      
            else if ( diskriminante == 0.0) then 
                    write(*,*) 'Die doppelte Nullstelle lautet:'
                    ! Nullstelle berechnen und ausgeben
      
                    else
                       write(*,*) 'Die beiden Nullstellen sind:'
                       ! die beiden Nullstellen berechnen und ausgeben
         end if
      
      Bemerkungen:
      Diese Verzweigungs-Konstruktion ist sehr flexibel:

      Die einzeilige if-Konstruktion

      Braucht man nur eine Anweisung, die als Folge einer logisch wahren Abfrage ausgeführt werden soll, so kann man als Einzeiler
           if ( < logischer Ausdruck > ) < Anweisung > 
      
      als kürzere Version von
           if ( < logischer Ausdruck > ) then
                < Anweisung > 
           end if  
      
      verwenden.

      Das select case-Konstrukt (nur Fortran 90/95)

      Will man eine Art Auswahlmenü programmieren, könnte man dies über eine if then - else if - else if - ...- else - end if - Konstruktion tun. Ein Beispiel wäre z.B. die
      Übungsaufgabe vom 4. Übungsblatt (WS 2001/02) in der man jetzt die Menüauswahl nach Minuten, Stunden, Tagen und Jahre als if then - else if - else if - ...- else - end if - Konstruktion umsetzen könnte. In Fortran 90/95 existiert dazu eine Alternative:
      Falls ein Ausdruck, nach dessen Wert Sie jeweils verschiedene weitere Programmanweisungsblöcke abarbeiten lassen wollen, vom Datentyp integer, character oder logical ist, bietet das case-Konstrukt dafür eine übersichtliche Alternative an:
      <Name des case-Konstr.>: select case ( <case-Ausdruck> )
                                     case (<case-Fall 1>) <Name des case-Konstrukts>   
                                          Anweisungsblock 1
                                     case (<case-Fall 2>) <Name des case-Konstrukts>   
                                           Anweisungsblock 2
                                     case (<case-Fall 3>) <Name des case-Konstrukts>   
                                           Anweisungsblock 3
                                     case  ...
                                        ...
      
                                     case default
                                           alternativer Anweisungsblock 
                                           falls keiner der oben abgefragten
                                           Faelle zutreffen sollte
      end select <Name des case-Konstrukts>
      

      Die Benennung des select case-Konstrukts ist optional, aber wiederum bei Auswahlmenüs mit sehr langen Anweisungsblöcken empfehlenswert!

      Ein einfaches Anwendungsbeispiel: select_case.f90

      Wie man in dem Beispiel sieht, können hinter den einzelnen case Einträgen statt einzelner selektiver Werte auch Intervalle (diese werden mit einem Doppelpunkt angegeben) verwendet werden.

      Statt Intervallen kann man bei Bedarf auch mehrere durch Kommatas angegebene Werte verwenden, z.B. wenn auf eine Frage ein Zeichen eingelesen wird, das j,J,y oder Y sein darf, wenn im folgenden ein bestimmter Anweisungsblock ausgeführt werden soll:

          ...
          character :: antwort
          ...
          read(*,*) antwort
          
          select case (antwort)
                 case ('j','J','y','Y')
                      ! Anweisungsblock fuer den Fall, dass die Antwort positiv war 
          end select 
          ...
      
      Wird wie im obigen Beispiel der Default-Fall nicht benötigt, kann der Teil ab case default ganz weggelassen werden.

      Die stop-Anweisung (Programmabbruch)

      Manchmal ist es notwendig, sobald ein Ausdruck einen bestimmten Wert annimmt, keinerlei weitere Programmanweisungen ausführen zu lassen, sondern stattdessen die Abarbeitung des Programms sofort zu beenden. Dazu dient die stop-Anweisung:
          stop
      
      Manchmal ist es sinnvoll den Anwender zu informieren, warum die Programmabarbeitung abgebrochen wird. Dazu kann man vorher noch eine write-Anweisung mit den zu übermittelnden Informationen einschieben. Will man nur einen einzigen Satz ausgeben könnte man dies tun, indem man eine Zeichenkette hinter dem stop anfügt, z.B.
          stop 'Wert von a=0 (Programmabbruch, sonst division by zero - error)'  
      

    3. Schleifen
    4. Sollen einzelne Anweisungsblöcke unter bestimmten Voraussetzungen mehrmals abgearbeitet werden, so kann man dies durch Schleifenkonstruktionen realisieren.

      Die flexibelste Form ist

      Die do - if exit - end do-Schleife (Fortran 90/95)

      Die allgemeine Syntax lautet
           do
              Anweisungsblock 1
           if ( < logischer Ausdruck > ) exit
              Anweisungsblock 2
           end do
      
      und im Programmablaufplan kann man die Funktionsweise dieser Schleife wie folgt darstellen

      do - if exit - end do - Schleife im Programmablaufplan

      Zunächst werden die Anweisungen abgearbeitet (Anweisungsblock 1), die unmittelbar auf den Kopf der Schleife, nach dem do folgen.

      Mit der if-Abfrage wird geprüft, ob der Wert eines logischen Ausdruck wahr ist; wäre dies der Fall, würde wegen des exits das Konstrukt verlassen werden und als nächste Anweisung die unmittelbar hinter dem end do folgende Anweisung abgearbeitet werden.

      Ist der logische Ausdruck bei der Abfrage nicht wahr, so wird der Anweisungsblock 2 abgearbeitet und danach anschließend wiederum Anweisungsblock 1, bevor erneut geprüft wird, ob der logische Ausdruck jetzt den Wert .TRUE. hat. Solange dies nicht der Fall ist, wird abermals Anweisungsblock 2 ausgeführt und danach Anweisungsblock 1. Erst wenn der logische Wert wahr wird, kann die Schleife verlassen werden.

      Ja nach Abfrage kann es passieren, dass die Schleife nie verlassen werden kann. In diesem Fall hätte man eine Endlos-Schleife programmiert.

      Ein einfaches Beispiel für eine do - if exit - end do-Schleife: do_schleife_mit_exit.f90.

      Ein weiteres Anwendungsbeispiel:
      Um den Mittelwert und die Standardabweichung bei einer beliebigen Zahl an nicht-negativen Werten zu berechnen (z.B. um eine Punktezahl-Statistik von Klausurpunkten zu erstellen), kann als "Trigger" für den Ausstieg aus der Daten-Einlese-Schleife die Eingabe eines negativen Punktwerts, der in diesem Fall in der Praxis nicht auftreten kann, eingesetzt werden: statistik.f90. Der Mittelwert von N Daten ist gegeben durch

      Berechnungsformel fuer den Mittelwert

      und die Standardabweichung durch

      Berechnungsformel fuer die Standardabweichung

      Wie das Programm statistik.f90 arbeitet und insbesondere wie die do - if exit - end do - Schleife eingesetzt wird, zeigt folgender Programmablaufplan

      Programmablaufplan zum Programm statistik.f90

      Generell gilt: Die do - if exit - end do - Schleife ist recht flexibel:
      Wenn man keine Anweisungen vor dem if (..) exit benötigt, kann dieser Anweisungsblock auch weggelassen werden. In diesem Fall kann man die do - if exit - end do in eine do while - end do-Schleife umbauen.

      Die do while - end do - Schleife (Fortran 90/95)

      hat die allgemeine Syntax
          do while (< logischer Ausdruck >) 
               Anweisungsblock
          end do
      
      Zu Beginn des Schleifenkopfes wird geprüft, ob der logische Ausdruck den Wert .TRUE. aufweist. Nur wenn dies der Fall sein sollte, wird der Anweisungsblock ausgeführt und dann erneut geprüft, wie der Wert des logischen Ausdrucks jetzt ist. Solange dieser Wert wahr ist, wird wird jeweils der Anweisungsblock abgearbeitet, ist der Wert des logischen Ausdrucks hingegen .FALSE. wird die Programmabarbeitung nach dem end do fortgesetzt.

      Beispiel: do_while.f90, Bildschirmausgabe: do_while.erg.
      Alternative Realisierung: do_ersatz_do_while.f90, Bildschirmausgabe: do_ersatz_do_while.erg

      Die do while - Konstruktion lässt sich ersetzen durch

          do 
               if ( .not. < logischer Ausdruck > ) exit
               Anweisungsblock
          end do
      

      Die Zählschleife

      Im Gegensatz zu den obigen flexiblen Schleifenkonstruktionen ist bei Zählschleifen (engl. counting loops) bereits beim Einstieg in die Schleife festgelegt, wie oft die Schleife durchlaufen werden soll. Eine Zählschleife folgt der Syntax
            do schleifenvariable = anfangswert, endwert, schrittweite
               Anweisung 1
               ...
               Anweisung n
            end do
      
      Der sogenannte Index der Zählschleife, die schleifenvariable kann vom Datentyp integer oder real sein. An der Stelle von anfangswert, endwert, schrittweite können Werte, Konstanten, Variablen und Ausdrücke des entsprechenden Datentyps stehen. Wird die do - Zeile ausgewertet, so werden die Ausdrücke für anfangswert, endwert, schrittweite ausgewertet und sind damit festgelegt.

      Flowchart-Symbol für die Zählschleife:

      Zu Beginn der Schleife wird der Wert der Schleifenvariable auf den Anfangswert gesetzt. Ist dieser Wert kleiner als der Endwert (bei positiver Schrittweite), so wird der in der Schleife enthaltene Anweisungsblock abgearbeitet. Mit jedem Schleifendurchlauf wird am Ende des Anweisungsblocks zu dem Wert der Schleifenvariablen der Zahlenwert der angegebenen Schrittweite hinzuaddiert.

      Im Diagramm gilt das rot eingezeichnete Symbol <= für positive Schrittweiten. Ist die Schrittweite negativ wird mit jedem Schleifendurchlauf die Schleifenvariable um die Schrittweite reduziert und der Block an Fortran-Anweisungen abgearbeitet, bis die Schleifenvariable kleiner als der Endwert geworden ist.

      Sobald der Wert der Schleifenvariable größer als der an 2. Position im Schleifenkopf angegebene Endwert sein sollte (bei positiver Schrittweite bzw. kleiner bei negativer Schrittweite), wird das Programm unterhalb von end do fortgesetzt.

      Gibt man im Zählschleifen-Kopf keinen Zahlenwert für die Schrittweite an, so wird als Schrittweite 1 angenommen.

      Dies ist z.B. bei dem kleinen Beispielprogramm der Fall: zaehlschleife.f90, Bildschirmausgabe: zaehlschleife.erg

      Man beachte, dass der Wert der Schleifenvariable in Fortran (anders als in C) innerhalb des in der Schleife nicht durch eine explizite Anweisung verändert werden darf. Ein derartiger Versuch würde beim Compilieren zu einem Fehler führen. (falscher_umgang.f90, falscher_umgang.err)

      Warum der Schleifenindex vom Datentyp integer sein sollte

      Um Fehler in der Anzahl der Schleifendurchgänge aufgrund der endlichen Darstellungsgenauigkeit der Zahlen des Datentyps real zu vermeiden, sollten in Fortran 90 Schleifen besser nur über integer-Konstruktionen realisiert werden. (Fortran 95 lässt bereits Zählschleifen nur noch über integer-Indizes zu).

      Ein Beispiel, bei dem die Schleife über real zu einem unerwünschten Verhalten führen kann, lässt sich z.B. mit dem Programm sinus.f90 aufzeigen.

      Zum Beispiel erhält man auf einzelnen Systemen und einzelnen Compilern, wenn man die als Kommentar eingegebenen Werte angibt, aufgrund von Rundungsfehlern einen Schleifendurchlauf weniger als man erwarten würde.

      Die Ursache für dieses Verhalten liegt in der notwendigerweise endlichen Darstellungsgenauigkeit von Zahlen das Datentyps real begründet (meist 4 Byte), so dass beim Aufbau von Schleifen über den Datentyp real die Gefahr besteht, dass aufgrund von Rundungsfehlern Schleifen nicht immer mit der erwarteten Präzession (d.h. mit der erwarteten Zahl an Durchläufen) abgearbeitet werden.

      Deshalb sollten Schleifen immer auf integer-Indizes umgestellt werden wie z.B. in sinus_integer_schleife.f90.

      Nach dem neuen Fortran 95 - Standard wird vorausgesetzt, dass Zählschleifen immer über den Datentyp integer aufgebaut werden. Dadurch kann gewährleistet werden, dass stets die gewünschte Anzahl an Schleifendurchgängen abgearbeitet wird, weil der Rechner auf Basis des Datentyps integer exakt "zählt".

      Will man z.B. ein Intervall mit x von xu bis xu mit der Schrittweite dx durchlaufen, sollte man dies nicht über

          real :: x, xu, xo, dx
      
          do x = xu, xo, dx
             ! Anweisungsblock
          end do
      
      tun, sondern über einen Schleifenindex vom Datentyp integer die Schleife durchlaufen. Dazu muss die Anzahl der Intervallschritte s berechnet werden:
           s = max ( nint( (xo-xu+dx)/dx ), 0) 
      
      Die Schleife lässt sich dann realisieren über

           integer :: i, s
           real    :: x, xu, xo, dx
      
           s = max ( nint( (xo-xu+dx)/dx ), 0)
      
           do i = 0, s-1
              x = xu + real(i) * dx
              ! Anweisungsblock
           end do
      

      Das Verlassen von do-Schleifen mit exit (Fortran 90/95)

      do-Schleifen können bei Bedarf jederzeit mit exit und cycle verlassen werden. Wird innerhalb einer do-Anweisung eine exit-Anweisung abgearbeitet, so wird die Programmausführung unmittelbar nach dem zugehörigen end do fortgesetzt.
      Siehe dazu das Beispiel von oben:
      statistik.f90.

      Sind die do-Schleifen ineinander geschachtelt, so wird mit exit die aktuelle Schleife verlassen, und das Programm hinter dem end do der aktuellen Schleife fortgesetzt.
      Beispiel: do_do_do_exit.f90 mit dem zugehörigen Programm-Output: do_do_do_exit.erg.

      Handelt es sich allerdings um benannte do-Schleifen und hinter der exit-Anweisung wird der Name einer Schleife aus der Schachtelungshierarchie angegeben, so wird genau diese Schleife beendet und das Programm am Ende einer Schleife des angegebenen Namens und damit in der Hierarchiebene oberhalb dieser angegebenen Schleife fortgesetzt. named_do_do_do_exit.f90 mit der zugehörigen Ausgabe named_do_do_do_exit.erg.

      Die Verwendung von cycle in do-Schleifen (Fortran 90/95)

      Die Abarbeitung von Teilen von do-Schleifen lässt sich ebenfalls mit cycle umgehen. Im Vergleich zu exit hat cycle die umgekehrte Wirkung: bei cycle wird an den Kopf der do-Schleife gesprungen. Demo-Beispiel: do_with_cycle.f90 mit dem zugehörigen Programm-Output: do_with_cycle.erg.

      Auch hier lässt sich bei geschachtelten do-Schleifen über die Benennung der Schleifen und der Angabe des entsprechenden Schleifennamens hinter exit gezielt an den Kopf der entsprechenden Schleife springen.

      Grundstruktur einer do-Schleife mit cycle und exit:

          do 
             ! Anweisungsblock 1
      
          if (  ) cycle  ! falls die Bedingung erfuellt ist, gehe zum Schleifenkopf
      
             ! Anweisungsblock 2
      
          if (  ) exit   ! falls diese Bedingung erfuellt ist, verlasse die Schleife
      
             ! Anweisungsblock 3
      
          end do
      
      Bei Bedarf kann natürlich dieses Konstrukt angepasst werden (z.B. kann die Reihenfolge von cycle und exit vertauscht und/oder weiter -Abfragen eingefögt werden.

      Mit do - exit - cycle - end do Eingabefehler des Anwenders abfangen

      Wird von einem Anwender die Eingabe von Werten erwartet, kann es passieren, dass sich dieser vertippt und z.B. statt des erwarteten Zahlenwertes einen Buchstaben eingibt. Dieser Fehler würde normalerweise zu einem Programmabbruch aufgrund eines Laufzeitfehlers (runtime error) führen. Dies lässt sich z.B. mit dem Programm eingabefehler.f90 testen.

      Der bisher zum Einlesen (von der Standardeingabe) eingesetzte Befehl, z.B.

          real :: x
      
          read(*,*) x 
      
      bietet eine Erweiterung, die es uns ermöglicht, Eingabefehler des Anwenders abzufangen
          real :: x
          integer :: io_err
      
          read(*,*,iostat=io_err) x 
      
      Durch die Erweiterung iostat=>Variable_vom_Datentyp_integer< wird beim Einlesen durch iostat=io_err der Variable io_err dieses selbstgewählten Namens ein Zahlenwert vom Datentyp integer zugewiesen, die einem Fehlercode entspricht.

      Nur wenn der Rückgabewert von iostat, welcher nun in der Variablen io_err gespeichert wurde, dem Wert 0 entspricht, ist konnte beim Einlesevorgang der Variablen x ein reeller Zahlenwert zugewiesen werden.

      Hat der Anwender nun statt eines Zahlenwertes z.B. versehentlich einen Buchstaben eingegeben, so erfolgt bei Verwendung von iostat=io_err innerhalb der read-Anweisung diesmal kein Programmabbruch aufgrund eines Laufzeitfehlers - vielmehr wird stattdessen der hinter iostat angegebenen Variablen (hier: io_err) ein Fehlercode ungleich 0 zugewiesen.

      Dieser Wert dieses Fehlercodes kann innerhalb einer geschickt konstruierten do-Schleife mit als Bedingung für exit bzw. cycle eingesetzt werden, um bei einer fehlerhaften Eingabe von Anwender erneute Eingabe zu erzwingen (eingabefehler_abfangen.f90)

      program eingabefehler_abfangen
         implicit none
         real    :: x
         integer :: io_err
         
         einlesen: do
           write(*,'(A$)') 'Geben Sie eine reelle Zahl ein: '
           read(*,*,iostat=io_err) x
           if ( io_err == 0) exit
           write(*,*) '=> Achtung: Eingabefehler, bitte Eingabe wiederholen!'
           cycle    
         end do einlesen
      
         write(*,*) 'Eingelesen wurde : ', x
      
      end program eingabefehler_abfangen
      

      do-Ersatz für das veraltete goto von FORTRAN 77

      Mit goto und Angabe einer Zeilennummer (engl. statement label) wurden in FORTRAN 77 u.a. bedingungsabhängige Sprünge innerhalb des Anweisungsteils einer Programmeinheit realisiert. Da die FORTRAN 77-Befehle weitestgehend in Fortran 90/95 enthalten sind, ist der Einsatz von goto zwar noch möglich, sollte aber zugunsten der besseren Übersichtlichkeit und Klarheit der Fortran 90/95 - Programme durch (benannte) do-Konstruktionen ersetzt werden.

      Im folgenden soll in dem zugegebenermaß noch einfachen und übersichtlichen FORTRAN 77 - Programm alt.f die goto-Anweisung ersetzt werden.

            PROGRAM ALT
            IMPLICIT NONE
            REAL X, SUMME
      C
            WRITE(*,*) 
           *    'Das Programm addiert die von Ihnen eingegebenen Zahlen'
            WRITE(*,*)
              WRITE(*,*) 'Wollen Sie die Summation abschliessen,' 
            WRITE(*,*) 'so geben Sie als Zahl 0 ein'
            WRITE(*,*)
      C 
            SUMME = 0.0
            X = 0.0
      C      
      100   CONTINUE
            SUMME = SUMME + X
            WRITE(*,'(1X,A$)') 'Geben Sie die Zahl ein: '
            READ(*,*) X
            IF ( X .NE. 0.0) GOTO 100
      C
            WRITE(*,*) 
            WRITE(*,*) 'Die Summe betraegt: ', SUMME
            END PROGRAM
      

      In dem Beispielprogramm wird, falls die Bedingung X .NE. 0.0 wahr ist, zu der Anweisung mit dem statement label100 gesprungen und der Programmablauf mit der Anweisung CONTINUE fortgesetzt. Die Befehlszeile, an die mit GOTO gesprungen werden soll, muss eine ausführbare Anweisung sein. Manchmal fungiert eine mit der entsprechenden Zeilenmarkierung versehene CONTINUE-Anweisung als "Einsprung-Markierung" zu dem korrespondierenden GOTO-Befehl. Das GOTO lässt sich in Fortran 90/95 vollständig ersetzen durch eine do-Schleife (neu.f90):

      program neu
         implicit none
         real :: x = 0.0, summe = 0.0
         
          write(*,*) 'Das Programm addiert die von Ihnen eingegebenen Zahlen'
          write(*,*)
          write(*,*) 'Wollen Sie die Summation abschliessen, so geben Sie als Zahl 0 ein'
          write(*,*)
          
      schleife: do 
          summe = summe + x
          write(*,'(1X,A$)') 'Geben Sie die Zahl ein: '
          read(*,*) x
          if ( x /= 0.0) cycle schleife
          exit schleife
      end do schleife
      
          write(*,*) 
          write(*,*) 'Die Summe betraegt: ', summe      
      
      end program neu
      

    Zurück zur Vorlesungsseite


    Heidrun.Kolinsky@uni-bayreuth.de
    (Dr. Heidrun Kolinsky, Rechenzentrum der Universität Bayreuth, Gebäude NW2, Raum 159, Universitätsstraße 30, D-95440 Bayreuth, Tel. 0921/55-2687)