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 > |