Fortran: Fortran 95: Unterprogramme
<<< zur Fortran-Startseite | |
<< Fortran 95 | Fortran 2003 >> |
< Standardfunktionen | Ein- und Ausgabe > |
Werden Programme umfangreicher und komplexer oder werden einzelne Programmabschnitte öfter verwendet, dann ist es sinnvoll Unterprogramme (Prozeduren) zu verwenden. Fortran 95 kennt zwei Typen von Unterprogrammen:
- Funktionen (
function
) - Subroutinen (
subroutine
)
Darüber hinaus existiert in Fortran die Möglichkeit Unterprogramme und Daten mittels Modulen in das Programm einzubeziehen.
Das function
Unterprogramm
[Bearbeiten]Eine Funktion ist ein Unterprogramm, das genau einen Wert zurück gibt. Welcher Art von Datentyp der Wert ist, wird durch eine Zuweisung an den Funktionsnamen erreicht ( z. B. real
).
Die allgemeine Funktionsdeklaration lautet:
datentyp function funktionsname ( [formale parameter] ) Vereinbarungsteil Anweisungsteil funktionsname = wert [return] end function funktionsname |
Wird der Datentyp bei der Funktionsdeklaration nicht angegeben, so muss der Datentyp der Funktion im Vereinbarungsteil festgelegt werden:
function funktionsname ( [formale parameter] ) Vereinbarungsteil datentyp :: funktionsname Anweisungsteil funktionsname = wert [return] end function funktionsname |
Mittels return
kann zur aufrufenden Programmeinheit zurückgekehrt werden. Ein return
unmittelbar vor der end
-Anweisung ist nicht unbedingt erforderlich, da das Programm beim Erreichen der end
-Anweisung zur aufrufenden Programmeinheit zurück springt. Deswegen sollte bei allen neueren Programmen darauf verzichtet werden. Wichtig ist die return
-Anweisung für alternative Rücksprünge zur aufrufenden Programmeinheit.
Eine Funktion wird meist mittels folgender allgemeiner Anweisung von der ausführenden Programmeinheit aufgerufen:
variable = funktionsname( aktuelle parameter ) |
Dabei orientiert sich diese Unterprogrammtechnik direkt an mathematischen Funktionen. Im folgenden Beispiel ruft das Programm test
die Funktion funk
auf, führt das Unterprogramm aus und gibt den entsprechenden Rückgabewert an das Programm test
zurück:
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program test ! implicit none real :: funk real :: x, y write(*,*) 'x -> ' read(*,*) x y = funk( x ) write(*,*) 'funk -> ', y end program test real function funk( a ) ! implicit none real :: a funk = a**2 end function funk |
Das Unterprogramm funk
kann sich direkt unter der aufrufenden Programmeinheit in der selben Textdatei oder in einer separaten Datei befinden. Befindet sich das Unterprogramm in einer separaten Datei, so muss diese und die aufrufende Programmeinheit zusammen kompiliert werden:
Übersetzung mit gfortran im vorliegenden Fall:
gfortran -o bsp bsp.f95
Übersetzung mit gfortran bei separaten Dateien (bspw.):
gfortran -o bsp bsp.f95 funk.f95
Es ist aber auch möglich eine Funktion nur durch ihren Namen aufzurufen. Der Rückgabewert der Funktion wird dann direkt ausgegeben:
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program test ! implicit none real :: funk real :: x write(*,*) 'x -> ' read(*,*) x write(*,*) 'funk -> ', funk( x ) end program test real function funk( a ) ! implicit none real :: a funk = a**2 end function funk |
Das subroutine
Unterprogramm
[Bearbeiten]Eine subroutine
ist ein Unterprogramm, das mehr als einen Wert zurückgeben kann. Eine Subroutine besitzt im Gegensatz zu einer Funktion keinen Datentyp und Rückgabewert.
Die allgemeine Deklaration einer Subroutine lautet:
subroutine subroutinenname ([formale parameter]) Vereinbarungsteil Anweisungsteil [return] end subroutine subroutinenname |
Mittels return
kann zur aufrufenden Programmeinheit zurückgekehrt werden. Ein return
unmittelbar vor der end
-Anweisung ist nicht unbedingt erforderlich, da das Programm beim Erreichen der end
zur aufrufendenen Programmeinheit zurück springt. Deswegen sollte bei allen neueren Programmen darauf verzichtet werden. Wichtig ist die return
Anweisung für alternative Rücksprünge zur aufrufenden Programmeinheit.
Aufgerufen wird eine Subroutine aus der aufrufenden Programmeinheit mittels der call
-Anweisung:
call subroutinenname [([aktuelle parameter])] |
Im folgenden Beispiel wird die Subroutine sub
aufgerufen. Diese gibt dann zwei Werte zurück:
Datei bsp.f95
Fortran 90/95-Code (free source form) |
program bsp ! implicit none real :: x, y, z write( *, * ) 'x -> ' read( *, * ) x call sub( x, y, z ) write( *, * ) 'y -> ', y, 'z -> ', z end subroutine sub( a, b, c ) ! implicit none real :: a, b, c b = a**2 c = a**2 + b end subroutine sub |
Weitere Anweisungen für die Unterprogrammtechnik
[Bearbeiten]Das intent
-Attribut
[Bearbeiten]Im Vereinbarungsteil der Funktion wird der Parameterdeklaration das Schlüsselwort intent
mitgegeben.
datentyp, intent( in ) :: var
Mit intent( in )
wird angezeigt, dass der Parameter var
in der Funktion nicht geändert wird und kein Rückfluss der Information in die aufrufende Programmeinheit stattfindet. Ein intent( out )
oder intent( inout )
wie bei Subroutinen wäre meist auch möglich, widerspricht aber dem Grundgedanken des Fortran-Funktionsbegriffs und der strukurierten Programmierung. Bei einer Funktion soll der Informationsrückfluss über den Rückgabewert stattfinden und nicht über Parameter.
Bei der Parameterübergabe bietet eine Subroutine folgende intent
-Möglichkeiten:
- datentyp, intent(in) :: var ... Informationfluss von der aufrufenden Programmeinheit in die Funktion
- datentyp, intent(out) :: var ... Informationfluss von der Subroutine zur aufrufenden Programmeinheit
- datentyp, intent(inout) :: var ... beidseitiger Informationsfluss
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none real :: a, b, c a = 1.0 b = 2.0 c = 3.0 call sub(a, b, c) write (*,*) 'Hauptprogramm: ', a, b, c ! Ausgabe: 1.000000 22.20000 33.30000 end subroutine sub(x, y, z) implicit none real, intent(in) :: x real, intent(out) :: y real, intent(inout) :: z write (*,*) 'Unterprogramm: ', x, y, z ! Ausgabe: 1.000000 2.000000 3.000000 y = 22.2 z = 33.3 end subroutine sub |
Die aktuellen und formalen Parameter müssen hinsichtlich Datentyp, Anzahl, Reihenfolge übereinstimmen.
Die pure
Anweisung
[Bearbeiten]Ein Unterprogramm welches keine Seiteneffekte hat ist ein bloßes bzw. reines (pure) Unterprogramm. Ein Unterprogramm erzeugt dann keine Seiteneffekte, wenn es weder seine Eingabedaten, noch die Daten verändert, die außerhalb des Unterprogrammes liegen, es sei denn, es wären seine Ausgabedaten. In einem reinen Unterprogramm haben die lokalen Variablen keine save
Attribute, noch werden die lokalen Variablen in der Datendeklaration initialisiert.
Reine Unterprogramme sind für das forall
-Konstrukt notwendig: das forall
-Konstrukt wurde für das parallele Rechnen konzipiert, weshalb hier der Computer entscheidet, wie das Konstrukt abgearbeitet werden soll. Dazu ist es aber notwendig, das es egal ist in welcher Reihenfolge das Konstrukt abgearbeitet wird. Gilt dies nicht - hat das Unterprogramm also Seiteneffekte - so kann das forall
-Konstrukt nicht verwendet werden.
Jedes Ein- und Ausgabeargument in einem reinen Unterprogramm muss mittels des intent
-Attributs deklariert werden. Darüberhinaus muss jedes Unterprogramm, das von einem reinen Unterprogramm aufgerufen werden soll, ebenfalls ein reines Unterprogramm sein. Sonst ist das aufrufende Unterprogramm kein reines Unterprogramm mehr.
Die elemental
Anweisung
[Bearbeiten]Ein Unterprogramm ist elementar, wenn es als Eingabewerte sowohl Skalare als auch Felder akzeptiert. Ist der Eingabewert ein Skalar, so liefert ein elementares Unterprogramm einen Skalar als Ausgabewert. Ist der Eingabewert ein Feld, so ist der Ausgabewert ebenfalls ein Feld.
Die return
Anweisung
[Bearbeiten]Eine return
-Anweisung darf nur im Gültigkeitsbereich von Unterprogrammen verwendet werden. Sie bewirkt einen Abbruch der Unterprogrammausführung und eine Rückkehr zum Aufrufpunkt, wo mit der nächsten Anweisung fortgesetzt wird.
Beispiel:
Fortran 90/95-Code (free source form) |
program main implicit none call xyz( -2 ) write( *, * ) "UP-Ende" stop write( *, * ) "Programmende" contains subroutine xyz( n ) integer, intent( in ) :: n integer :: k do k = n, n+20 write( *, * ) k if( k == 5 ) return end do write( *, * ) "k_max =", k end subroutine xyz ! Ausgabe: ! -2 ! -1 ! 0 ! 1 ! 2 ! 3 ! 4 ! 5 ! UP-Ende end program main |
Einige Compiler erlauben zwecks Programmabbruch auch ein return
anstelle des stop
im Hauptprogramm. Das ist aber nicht standardkonform. Andere Compiler würden solchen Code mit einer Fehlermeldung abweisen, das Programm wäre somit nicht mehr uneingeschränkt portabel.
Ein exit
in der Schleife anstelle der return
-Anweisung würde nur den Schleifendurchlauf abbrechen und die Unterprogrammausführung würde nach der Schleife fortgesetzt.
Felder als Parameter
[Bearbeiten]Beispiel: Übergabe eines ganzen Feldes
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(3,3) :: feld integer :: cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld) ! Ausgabe: 1 2 3 4 5 6 7 8 9 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, dimension(3,3), intent(in) :: arr write(*,*) arr end subroutine sub |
Beispiel: Übergabe einer Feld-Teilkette
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer,dimension(3,3) :: feld integer :: cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld(1:2,2:3)) ! Ausgabe: 4 5 7 8 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, dimension(0:1, 0:1), intent(in) :: arr write(*,*) arr end subroutine sub |
Beispiel: Übergabe eines Feld-Einzelelements
Datei bsp.f95:
Fortran 90/95-Code (free source form) |
program bsp implicit none integer, dimension(3,3) :: feld integer cnt, i, j cnt = 1 do i = 1, 3 do j = 1, 3 feld(j,i) = cnt cnt = 1 + cnt end do end do ! Unterprogrammaufruf call sub(feld(1,2)) ! Ausgabe: 4 end program bsp |
Datei sub.f95
Fortran 90/95-Code (free source form) |
subroutine sub(arr) implicit none integer, intent(in) :: arr write(*,*) arr end subroutine sub |
Prozeduren als Parameter
[Bearbeiten]Auch Prozeduren können als Parameter übergeben werden.
Standardfunktionen (intrinsic functions) werden dazu folgendermaßen im Vereinbarungsteil gekennzeichnet:
intrinsic namensliste |
oder
datentyp, intrinsic :: namensliste |
Eigene Unterprogramme oder Unterprogramme aus Bibliotheken mit:
external namensliste |
oder
datentyp, external :: namensliste |
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp real, parameter :: PI=3.1415927 ! intrinsic functions intrinsic sin, cos ! Unterprogrammaufrufe call sub(sin, PI) ! Ausgabe: 0.000000 call sub(cos, PI) ! Ausgabe: -1.000000 end program bsp subroutine sub(funk, x) implicit none real :: funk, x write(*,*) nint(funk(x)*1000)/1000.0 end subroutine sub |
Optionale Parameter
[Bearbeiten]Ab Fortran 90 sind auch optionale Parameter für Unterprogramme erlaubt. Solche Parameter sind durch das Attribut optional
zu kennzeichnen. Auch der aufrufenden Programmeinheit muss diese Parameter-Optionalität bekannt gegeben werden, z.B. über ein Interface. Das aktuelle Vorhandensein eines als optional markierten Parameters beim Unterprogrammaufruf kann im Unterprogramm selbst durch die Funktion present
geprüft werden.
Beispiel:
Fortran 90/95-Code (free source form) |
program bsp implicit none interface subroutine ab(a, b) integer, intent( in ) :: a integer, intent( in ), optional :: b end subroutine ab end interface call ab( 1 ) call ab( 8, 12 ) ! Ausgabe: ! Nur a gegeben 1 ! Beide Parameter gegeben 8 12 end program bsp subroutine ab(a, b) integer, intent( in ) :: a integer, intent( in ), optional :: b if( present( b ) ) then write (*,*) "Beide Parameter gegeben", a, b else write (*,*) "Nur a gegeben", a end if end subroutine ab |
Module
[Bearbeiten]Module ersetzen in Fortran 95 die FORTRAN 77-COMMON
-Blöcke. In einem Modul können Variablen, Konstanten und auch Unterprogramme abgelegt werden. Diese können dann von verschiedenen Programmeinheiten angesprochen werden.
module modulname [implicit none] [save] Deklaration von Variablen, Konstanten [contains Unterprogramme ] end module modulname |
Das Einbinden eines Moduls in eine Programmeinheit geschieht mittels der Anweisung
use modulname [, only liste] |
only
signalisiert, dass nur die in liste
angegebenen Variablen oder Konstanten verwendet werden sollen.
Beispiel:
Fortran 90/95-Code (free source form) |
module m1 implicit none save real, parameter :: PI=3.1415 real :: a end module m1 program bsp use m1 implicit none a = 1.5 write(*,*) 'Hauptprogramm 1: ', PI, a ! Ausgabe: Hauptprogramm 1: 3.141500 1.500000 call sub write(*,*) 'Hauptprogramm 2: ', PI, a ! Ausgabe: Hauptprogramm 2: 3.141500 2.500000 end program bsp subroutine sub use m1 implicit none write(*,*) 'Unterprogramm: ', PI, a ! Ausgabe: Unterprogramm: 3.141500 1.500000 a = 2.5 end subroutine sub |
Das save
-Statement in Modulen stellt sicher, dass die aktuellen Werte von Modulvariablen beim Wechsel zwischen den verschiedenen Programmeinheiten sicher erhalten bleiben.
Rekursiver Unterprogrammaufruf
[Bearbeiten]In Fortran 95 können Unterprogramme (Funktionen, Subroutinen) auch rekursiv aufgerufen werden. Rekursion bedeutet, dass ein Unterprogramm sich selbst wieder aufruft (lat. recurrere oder en. recur ... wiederkehren, zurückkommen).
Beispiel: Berechnung von n! (vereinfacht)
Fortran 90/95-Code (free source form) |
program bsp implicit none integer :: zahl, ergebnis, fac write(*,*) "Gib eine Ganzzahl ein: " read(*,*) zahl ergebnis = fac(zahl) write(*,*) "Ergebnis ist: ", ergebnis end program bsp recursive function fac(n) result(zerg) implicit none integer, intent(in) :: n integer :: zerg ! Vereinfacht: Keine Überprüfung ob Überlauf, negative Zahl, etc. zerg = n if (n > 1) then zerg = n * fac(n-1) end if return end function fac |
Der Funktionskopf ist bei diesem Beispiel etwas anders gestaltet als üblich. Während der Intel-Fortran-Compiler auch ein
recursive integer function fac(n)
oder ein
integer recursive function fac(n)
problemlos akzeptiert, wirft der gfortran-Compiler bei diesen Varianten einen Syntaxfehler.
<<< zur Fortran-Startseite | |
<< Fortran 95 | Fortran 2003 >> |
< Standardfunktionen | Ein- und Ausgabe > |