- Simon Braß, Doktorand in der AG Theoretische Teilchenphysik
- Jessica Reuter, Lehramt für Mathematik und Informatik
- Vorbereitung
- Geschichte
- Konzepte und Syntax
- Strukturen
- Datentypen
- Ein-/Ausgabe
derived types
- Zeiger und Prozeduren-Zeiger
allocatables
- Polymorphie und Vererbung
- Typ-gebundene Prozeduren
- EN-D308, 4.12.2012, 16:00 - 20:00 Uhr
- EN-D308, 5.12.2012, 16:00 - 20:00 Uhr
- Emacs
- Vim / vi / kak
- Sublime
- Atom
- nagfor
- ifort
- gfortran
Anleitungen und Material unter: http://www.tp.nt.uni-siegen.de/+brass/
- 1950er
- Entwicklung bei IBM um das Team von John Backus als Assembler-Alternative
- 1966
- 1. Standardisierung durch ANSI als
FORTAN 66
- 1978
- 2. Standardiserung als
FORTRAN 77
/ Durchbruch als Sprache - 1991
- Einführung von abstrakten Datentypen und data hiding, sowie Standardisierung der array language
- 1997
- Fortran 95 als high perfomance Sprache
- 2004
- Fortran 2003 mit Objekt-orientierten Konzepten
- 2010
- Fortran 2008 mit submodules und coarrays
- 2012-heute
- Fortran 2018 …
- einfach-lesbare und verständliche Syntax
- vergleichbare Effizienz zu Assembler-Code
- Mathematik-nahe Umsetzung FORmula TRANnslator
- fixed form Syntax für Lochkarten-Computer in
FORTAN 66/77
- free form seit
Fortran 90
als Standard
c independent variables for real graph: number of final state
c legs times 3, take away 4 for 4-momentum conservation, add 2
c for x_1 and x_2, and take away an overall azimuth
real * 8 btilde,xx(ndiminteg),www0
real * 8 xrad(3)
real * 8 xborn(ndiminteg-3)
call self%rand_gen%rand(randkoord)
randkoord = self%sys_len * randkoord
m = int(randkoord(1)) + 1
n = int(randkoord(2)) + 1
call cyclic_coord (self, m, n, up, down, left, right)
deltaE = 2.0_dp * ( self%couple_const * self&
&%grid(m, n)*(self%grid(m, left) + self%grid(m, right) +&
& self%grid(up, n) + self%grid(down, n)) + self%mag_field &
&* self%grid(m, n))
parametrized derived types
, Konstruktoren, Destruktoren und eingeschränkte Zugriffe- Polymorphie, Typ-Erweiterung, Vererbung, dynamische Typ-Allokierung und Typ-gebundene Prozeduren
- allokierbare Komponenten,
deferred type parameters
- Prozeduren-Zeiger
C bindings
- (fast) vollständige Unterstützung durch Gfortran
7.2.0
(GCC Wiki) - größte Kompiler-Unterstützung an sich (ifort/NAGfor/Gfortran)
- Parallelisierung mit OpenMP durch Direktiven mit Threads standardisiert
- Parallelisierung im Message-Passing Interface standardisiert
- gewöhnliche Sprache
- Syntax nahe an Matrix- und Vektorproblemen
- strenge Speicherverwaltung
- Polymorphie mit Typ-Sicherheit
by-reference
Argumentübergabe
- schlechte Kompiler-Unterstützung
- keine Templates (
C++
) - langsame Zeiger
Definition eines Scopes durch Strukturen
- Hauptprogramm
- Module
- Prozeduren
derived types
- Verschachtelung von Scopes
- Zugriff auf Daten eines niedrigeren Scopes
- kein Zugriff auf Daten eines höhren Scopes
- explizites Öffnen / Verschließen von Daten
program beispiel
! use beispiel_module
implicit none
! Variablen-Deklaration
! Programm
contains
! Prozeduren
! Zugriff auf alle Variablen
! des Hauptprogrammes
end program beispiel
module beispiel_module
use beispiel2_module
! Variablen-Deklarationen
! Typ-Definitioen
!
contains
! Module-Prozeduren
! Zugriff auf alle Variablen
! des Moduls
end module beispiel_module
- Kein Zugriff von
beispiel_module
aufprogram
- Zugriff von
contained procedure
inprogram
aufprogram
!
erstellt werden. Jeder Ausdruck nach !
wird vom Kompiler ignoriert.
Überlange Zeilen (>=127
Zeichen) werden mit &
gebrochen.
Mit einem ;
kann ein Statement-Ende gesetzt werden so, dass mehrere Statements in eine Zeile geschrieben werden können.
! Ich bin ein Kommenatar.
real :: x = 1.5 ! Ich bin auch ein Kommentar.
character(len=100) :: str = "Hallo Welt! Ich bin eine viel zu lange &
& Zeichenkette, denn ich passe nicht in eine Zeile."
i = 42; b = 10; c = (0.5, 4.5)
integer :: i ! Ganze Zahlen
real :: x ! Reelle Zahlen
complex :: z ! Komplexe Zahlen (5., 2.): 5+2i
character(len=100) :: str ! Zeichenkette mit Länge 100
logical :: flag ! Boolean (.True. oder .False.)
type(haus) :: eigenheim ! Derived Type
Wichtig: Aus Rückwartskompatibilität immer implicit none
benutzen, damit Fortran nicht automatisch Variablen mit entsprechenden Typen deklariert.
print *, "Hallo Welt!" ! Schreibt "Hallo Welt!" in die Standardausgabe
write (*, *) "Hallo Welt!"
write (*, *) "Geben Sie einen ganze Zahl ein:"
read (*, *) i ! Liest einen Wert von der Standardeingabe ein
write (u, *) "Ich werde in die unit = ", u, " geschrieben."
- andere unit als
*
(Standardein-/ausgabe) möglich - Standardausgabe-, Standardfehlerausgabe-/unit/ in
iso_fortran_env
use iso_fortran_env, only: error_unit
! Schreibt Fehlermeldung auf stderr
write (error_unit, *) "Fehlermeldung"
- statisch-kompilierte Sprache
- Kompilieren zu Machinen-Code
gfortran \ -Wall \ # alle Warnungen -g \ # Debug-Symbole -c example1.f08 -o example1.o
- Linken zu einem ausführbaren Programm
gfortran -o example1 example1.o
program example1
implicit none
integer :: i
print *, "*** Beispiel 1 ***"
print *, "Hallo Welt!"
i = 42
print *, "i = ", i
end program example1
gfortran -o example.o \
-c example.f08
gfortran -o example example.o
./example
*** Beispiel 1 ***
Hallo Welt!
i = 42
- Schreiben Sie das Programm: Beispiel 1 ab und kompilieren Sie es.
- Was ist der Unterschied zwischen
example.o
undexample.f08
? - Was geschieht beim sog. Linken mit
example.o
?
Benutzen Sie objdump -d example1.o
, um sich den Machinen-Code anzusehen.
Versuchen Sie das Beispiel ohne die print
-Anweisungen.
Alle Variablen können weitere Eigenschaften besitzen:
- bei
parameter
kann sich die Variable nicht mehr ändern, - durch
dimension
wird eine Variable zu einem Variablen-Array, - durch
allocatable
kann der Speicher dynamisch angefordert werden, - durch
pointer
kann mit einer Variable auf eine andere Variable mittarget
gezeigt werden.
Eigenschaften können sich gegenseitig ausschließen!
integer, pointer, target :: i1 ! Verboten
integer, allocatable, pointer :: i2 ! Verboten
integer, parameter, pointer :: i3 ! Verboten
! ...
print *, "a + b = ", a + b ! Addition
print *, "a - b = ", a - b ! Subtraktion
print *, "a * b = ", a * b ! Multiplikation
print *, "a / b = ", a / b ! Division
print *, "a ** b = ", a ** b ! Potenz
- alle mathematischen Operatoren und Funktionen für komplexe Zahlen
- Vielzahl von mathematischen Standardfunktionen:
mod
,sin
,log
,… - logische Operatoren:
.and.
,.or.
,.not.
,… - vergleichende Operatoren: ===,
/=
,>
,<
,>=
,<=
print *, ".true. .and. .false.", .true. .and. .false.
print *, ".true. .or. .true.", .true. .or. .true.
print *, "(a - b) /= (a + b)", (a - b) /= (a + b)
- Deklarieren Sie mit allen Datentypen Variablen.
- Definieren Sie für alle Variablen Werte.
- Geben Sie alle Variablen auf der Standardausgabe aus.
- Wenden Sie (ausgewählte) mathematische Operatoren auf Variablen gleichen und verschieden Typs an.
program aufgabe1
implicit none
integer :: zahl
! ...
zahl = 42
! ...
print *, "zahl = ", zahl
! ...
print *, "zahl + 1. = ", zahl + 1.
end program aufgabe1
- von allen Datentypen (auch
derived types
) - maximal 7-dimensionaler Array
- 1-dm. Konstruktor durch
[...]
- allokierbarer Array mit
allocatable
integer, dimension(20) :: iarray
integer, dimension(10, 2) :: iarray2
integer, dimension(:), allocatable :: iarray3
! Maximal 7-dim. Array
iarray = 10 ! alle Element auf 10 setzen
iarray = [(i,i=1, 20)] ! Konstruktor
iarray = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
iarray2(:, 1) = [(i, i = 1, 10)]
iarray2(:, 2) = [(i**2, i = 1, 10)]
iarray3 = [1, 2] ! Allocate on assignment
deallocate (iarray3)
allocate (iarray3(10), source = 1)
- 1-dim. Array kann mit
[...]
initialisiert werden - mehr-dim. Array kann mit der
reshape
-Funktion initialisiert werden reshape (1D-ARRAY, SHAPE...)
mit einem eindim.-Array von der Größe vonarray
und einemSHAPE
-Array
! Fülle iarray2 mit iarray2 auf
iarray2 = reshape (iarray, [10, 2])
- Zugriff auf Elemente eines Array durch Index
- Einzel-Element oder Unter-Array
- Zugriff auf slice durch
:
- Index-Variablen vom Typ
integer
- Index läuft von
1...N
integer, dimension(10, 3) :: 2d_array
! ...
print *, 2d_array(5, 2) ! Element
print *, 2d_array(1:5, 1) ! Unterarray
print *, 2d_array(5, :) ! Slice im höchsten Index
print *, 2d_array(2 * i, i) ! i = {1, 2, 3}
- Spalten-orientierter Array
- Anordnung von Array-Speicher nach niedrigster zur höchster Dimension
- Effizienz bei Schleifen über mehr-dim. Arrays
integer, dimension(100, 25) :: a
integer :: i, j
do i = 1, 100 ! linker, innerer Index
do j = 1, 25 ! rechter, äußerer Index
print *, a(i, j)
end do
end do
Die Schleife über den Index j
läuft über Spalten und der Index i
über die Reihen.
Daher muss im obigen Beispiel sehr viel im Speicher hin und her gesprungen werden.
integer, dimension(100, 25) :: a
integer :: i, j
do j = 1, 25 ! rechter, äußerer Index
do i = 1, 100 ! linker, innerer Index
print *, a(i, j)
end do
end do
Der Speicher wird in der vorgegebenen Reihenfolge abgelaufen so, dass der Array effizient ausgelesen wird.
Im Beispiel macht das natürlich keinen Unterschied wegen des print
auf die Standardausgabe!
size(ARRAY, DIM...)
gibt die Größe des Arrays zurück, mitDIM
kann eine Dimension ausgewählt werdenlbound(ARRAY, DIM...)
gibt alle kleinsten Index des Arrays (in der jeweiligen Dimension)ubound(ARRAY, DIM...)
gibt alle größten Index des Arrays (in der jeweiligen Dimension)
integer, dimension(10, 5, 20) :: array
print *, "size(array) = ", size (array)
print *, "size(array, dim=1) = ", size (array, dim=1)
print *, "lbound(array) = ", lbound(array)
print *, "ubound(array, dim=2)", ubound(array, dim=2)
print *, "lbound(array, dim=3) = ", lbound(array, dim=3)
if-clause
prüft logischen Ausdruck- logischen Ausdruck aus logischen Operatoren und Variablen
select case
do
/do while
/cycle
/exit
if (a == b) then ! /= für ungleich
print *, "a == b"
else if (a >= b) then ! >, <, <=
print *, "a >= b"
else
print *, "a steht in keiner Relation zu b"
end if
do i = 1, 10 ! Drittes Argument gibt Iterationsvorschrift
print *, "i = ", i
end do
do i = 10, 1, -1
print *, "i = ", i
end do
do while (a > b)
b = b + 2
print *, "b = b + 2 = ", b
end do
- Schleifen können ein (eindeutiges) Label haben
- Label kann wichtig sein für
cycle
oderexit
- Zugriff auf Schleife mit Label durch
cycle/exit label
infty: do ! "Endlos-Schleife"
! Tue etwas
if (bedingung1) then
cycle infty ! Wiederhole Schleife sofort
else if (bedingung2) then
exit infty ! Beende Schleife sofort
else
! Tue etwas ganz anderes
end if
end do infty
! select case als Ersatz für verschachtelte if-clauses
select case (test)
case(10)
print *, 10
case(:9)
print *, "<10"
case default
print *, "Was auch immer..."
end select
! elementweise Zuordnung
forall (i = 1:10)
array(i) = array(i) + 1
end forall
! elementweise Bearbeitung über Bedingung
where (array == 1)
array = array + 1
else where
array = 0
end where
- Deklarieren Sie eindimensionale Arrays von verschiedenen Datentypen.
- Testen Sie welche mathematischen Operatoren erlaubt sind:
- Skalar und Array
- Array und Array
- Deklarieren, (de)allokieren Sie Arrays.
- Nutzen Sie eine Schleife um einen Array elementeweise auszugeben.
- Testen Sie, ob ein
allocatable
Objekt allokiert ist, mitallocated(...)
. - Probieren Sie ein
allocate-on-assignment
aus. - Greifen Sie auf ein Element mit Index
i
mitiarray(i)
zu.
Eine Funktion liefert einen Rückgabe-Wert aus verschiedenen Argumenten. Ähnlich einer Abbildung aus der Mathematik.
! Funktion vom Typ integer
integer function add (a, b) result (c)
integer, intent(in) :: a
integer, intent(in) :: b
c = a + b
end function add
! ...
print *, "2 + 3 = ", add(2, 3)
pure
Funktionen sind ohne Nebeneffekte wie Ein-/Ausgabeelemental
Funktionen werden elementweise auf Arrays angewant, setztpure
voraus.- Bei Definition ohne
result
wird automatisch eine Rückgabe-Variable mit dem Namen und des Types der Funktion angelegt.
- Schreiben Sie eine ganzzahlige Funktion, welche einen 1-dim. Array vom Typ
integer
nimmt und die Summe der Elemente berechnet. - Schreiben Sie eine reelle Funktion, welche einen 1-dim. Array vom Typ
real
nimmt und das Produkt aller Elemente bildet. - Schreiben Sie eine logische Funktion, welche nur
.true.
liefert, wenn kein Element einesinteger
Array null ist. - Schreiben Sie eine reelle Funktion, welche einen 2-dim. Array vom Typ
real
nimmt und die Elemente der ersten Dimension durch Elemente der zweiten Dimension teilt. - Schreiben Sie eine reelle Funktion, welche das Skalarprodukt zwischen zwei gleich-großen Vektoren bestimmt.
- Mit der Funktion
size
können Sie die Größe eines Arrays bestimmen. - Lösung
Subroutinen können mehrere Argumente zurückgeben.
subroutine volume_and_length (a, b, volume, circ)
real, dimension(3), intent(in) :: a
real, dimension(3), intent(in) :: b
real, intent(out) :: volume
real, intent(out) :: circ
real, dimension(3) :: c
call vector_product (a, b, c)
volume = sqrt(dot_product(c, c))
circ = 2*(sqrt(dot_product(a, a)) + sqrt(dot_product(b, b)))
contains
subroutine vector_product (a, b, c)
real, dimension(3), intent(in) :: a, b
real, dimension(3), intent(out) :: c
c = [(a(1) * b(2) - a(2) * b(1)), &
(a(1) * b(3) - a(3) * b(1)), &
(a(2) * b(3) - a(3) * b(2))]
end subroutine vector_product
end subroutine volume_and_length
- Funktionen und Subroutinen können andere Prozeduren über
contains
enthalten. intent
-Angabe geben Veränderlichkeit von Argumenten an:in
,out
,inout
optional
-Argumente können, müssen aber nicht übergeben werden.
character(:), allocatable, optional :: text
if (present (text)) print *, text
- Übergabe der Argumente in Reihenfolge
- Übergabe der Argumente über Name des Arguments:
a=array, b=array2
intent(in/inout)
-Arrays können assumed-shape haben
subroutine map_index (index1, index2)
integer, dimension(:), intent(in) :: index1
integer, dimension(size(index)), intent(out) :: index2
end subroutine map_index
! im Programm
integer, dimension(5) :: i1, i2
call map_index (i1, i2)
- Strukturierung und logische Ordnung
- Modularisierung
- Zugriffsrechte
- Schnittstellen und Definitionen
- Wiederverwendbarkeit
module vektoren
implicit none
private ! Immer alles auf private setzen
public :: vector3_product ! explizit exportieren
contains
subroutine vector3_product (a, b, c)
real, dimension(3), intent(in) :: a, b
real, dimension(3), intent(out) :: c
c = [(a(1) * b(2) - a(2) * b(1)), &
(a(1) * b(3) - a(3) * b(1)), &
(a(2) * b(3) - a(3) * b(2))]
end subroutine vector3_product
end module vektoren
- Einbindung von Modulen durch
use
- Beschränkung durch
only
- Umbenennung von geladenen Prozeduren
neu => alt
private
Elemente nicht nach außen sichtbarpublic
Elemente nach außen sichtbar
program main
use vektoren, only: vector_product => vector3_product
! Nur vector3_product importieren und umbenennen
! ...
call vector_product (a, b, c)
print *, "a x b = ", c
end program main
- Zeichenketten sind quasi 1-dim. Arrays vom Typ
character
- Spezielle Syntax
character(:)
- Zugriff auf Teile einer Zeichenkette durch Indizes
- Zeichenketten zusammenfügen mit
//
character(len=100) :: str character(:), allocatable :: str2, str3 ! allocatable-string str = "Hallo Welt!" print *, str str2 = "Hallo Welt! Ich bin neuer!!" print *, str2 str3 = "Und meine Geschichte ist es..." str2 = str2 // " " // str3 ! String konkatieren mit // print *, str2
Was nicht behandelt wurde:
- Datentyp-Genauigkeit über
kind
- Vertiefung von Ein- und Ausgabe
- Umgang mit Dateien
return
in Prozeduren- Rekursion
interfaces
/ Operator-=interfaces=- Prozeduren als Argumente
- Intrinsische Datentypen
- Bedingungen und Schleifen
- Funktionen und Subroutinen
- Module
Objekt-orientierte Liste mit beliebig erweiterbaren Listen-Elementen und Strukturierung auf verschiedene Module und Dateien.
- Zeiger entspricht einer Speicher-Adresse einer anderen Variable
- Ziel-Objekt muss vom Typ
target
sein - Zeiger-Sicherheit: Programmabbruch bei Zugriff auf undefinierten Zeiger
real, dimension(:, :), pointer :: p_array ! undefiniert
real, dimension(10, 10), target :: array1, array2
array1 = 1.
array2 = 2.
! Zuweisung Zeiger auf Ziel
p_array => array1
if (.not. associated (p_array)) then ! Zeiger ist nicht zugewiesen
p_array => array2
end if
print *, p_array(:, 1)
nullify (p_array) ! Zurücksetzen
p_array => array2
print *, p_array(:, 1)
nullify (p_array)
if (.not. associated (p_array)) then
print *, p_array
end if
Ein interface
stellt dem Kompiler aller Informationen zur Prozedur-Art, sowie Argumente usw. zur Verfügung.
- Implizites
interface
automatisch durch Deklaration einer Prozedur - Explizite Definition eines
interfaces
für Prozeduren, Operatoren oder Konstruktoren - Überladen eines
interfaces
mit einer weiteren Definitiongeneric interface
interface
logical function non_zero (a) return (yorn)
integer, intent(a) :: a
end function non_zero
end interface
- Operator-=interface= durch Modul-Prozedur
- Funktion mit zwei
intent(in)
-Argumenten - Assignment-=interface= durch Modul-Prozedur
- Subroutinen mit erstem Argument mit
intent(out)
oderintent(inout)
und zweitem Argumentintent(in)
module math
implicit none
interface operator(*)
module procedure matrix_vector_mult
end interface operator(*)
contains
function matrix_vector_mult (A, x) result (y)
integer, dimension(:, :), intent(in) :: A
integer, dimension(:), intent(in) :: x
integer, dimension (size (x)) :: y
integer :: i
do i = 1, size (x)
y(i) = sum(A(:, i)) * x(i)
end do
end function matrix_vector_mult
end module math
- Prozeduren als Argument
- Argument durch
interface
real function integrate (func, a, b) result (value)
interface
real function func (x)
real, intent(in) :: x
end function func
end interface
real, intent(in) :: a, b
! Integrieren mit Simpson-Regel
end function integrate
- eigene Typ-Definition aus intrinsischen Typen und
derived types
derived type
kannallocatables
undpointer
beinhalten- vordefinierter Konstruktor
- Typ-Name per Konvention mit
_t
private
/public
(allgemein oder elementweise)
! Typ Definition
type :: haus_t
private
real, public :: length
real, public :: width
real, public :: height
end type haus_t
type(haus_t) :: eigenheim
! Konstruktor
eigenheim = haus_t (10.0, 8.0, 12.0) ! Länge, Breite und Höhe
eigenheim = haus_t ( & ! Beliebige Reihenfolge
width = 8.0, & ! mit explizitem Aufruf
height = 12.0, & ! der Argumente
length = 10.0)
! Oder eigener Konstruktor durch ein überladenes Interface von haus_t
print *, "eigenheim%height = ", eigenheim%height ! Zugriff auf Element
- Erweiterung eines
derived type
durch Vererbung - Überschreiben aller Eigenschaften möglich
- Zugriff auf alle Elemente durch
variable%element
- Zugriff auf ursprüngliche Elemente des Basis-Typ durch
new_type%basic_type%element
type, extends(haus_t) :: halle_t
character(len=20) :: fassadenfarbe = "grün"
logical :: brandschutz = .false.
end type halle_t
type(halle_t) :: gewerbe ! hat alle Eigenschaften von haus_t und hallo_t
gewerbe = halle_t ( &
width = 8.0, height = 12.0, length = 20., &
fassadenfarbe = "rot", brandschutz = .true.)
print *, gewerbe%haus_t%width, gewerbe%width
- abstrakter Typ als Basis-Typ ohne direkte Verwendung
- abstrakter Typ muss erweitert werden
type, abstract :: grundstueck_t
logical :: bebaut = .false.
end type grundstueck_t
type, extends(grundstueck_t) :: bau_grundstueck_t
real :: length, height, width
logical :: construction
end type bau_grundstueck_t
type, extends(grundstueck_t) :: wald_t
real :: anteil_baeume
end type wald_t
- polymorphe Objekte/=class(…)=-Objekte
- polymorpe Objekte erben alle Eigenschaften, bzw. können alle Eigenschaften überladen
- Typ-Sicherheit durch
select type
class(grundstueck_t), allocatable :: grundstueck
allocate (wald_t :: grundstueck)
select type (grundstueck)
type is (wald_t)
grundstueck%length = 100
grundstueck%height = 15
grundstueck%width = 250
grundstueck%anteil_baeume = 0.8
! type is (bau_grundstueck_t)
! class is (...)
! class default
end select
- Typ-gebundene Prozeduren
- 1. Argument ist
pass
-Objekt durchclass(...)
- explizite Angabe des
pass
-Objekt mitpass(...)
nopass
,pass
,private/public
,deferred
- Überladen von Prozeduren kann verhindert werden durch
non_overridable
deferred
-Prozeduren nur beiabstract types
generic
final
type :: func_t
contains
procedure, pass(object), public :: write => func_write
procedure, private :: quadrature => func_quadrature
procedure, private :: simpson => func_simpson
generic, public :: integrate => simpsons, quadrature
final :: func_finalize
end type func_t
Implementieren Sie eine Objekt-orientierte Liste mit beliebig erweiterbaren Listen-Elementen. Nehmen Sie folgende Punkte als Hilfestellung:
- Schreiben Sie ein Modul
list
. - Das Modul definiert einen abstrakten Datentyp
basic_list_element_t
für ein beliebiges Listen-Element, welches nur einen Zeiger auf das nächste Element enthält und einen Index-=integer=. - Der abstrakte Typ
basic_list_element_t
soll einedeferred
Typ-gebundene Prozedurwrite
besitzen. Geben Sie einabstract interface
vor. - Das Modul definiert einen Typen
basic_list_t
, welcher einen Zeiger auf das erste Elemente und das nächste (letzte) Element besitzt. - Der Typ
basic_list_t
soll mehrere Typ-gebundene Prozeduren besitzen, eineadd
-Prozedur, welche ein Element nimmt und zur Liste anfügt, sowie einewrite
-Prozedur. - Der Typ
basic_list_t
soll einefinal
-Prozedur besitzen, welche die Liste elementweise deallokiert. - Schreiben Sie ein Modul
list_integer
und erweiteren Sie darin, den abstrakten Typbasic_list_element_t
, so dass Sie eineninteger
-Wert im Element speichern können. - Implementieren Sie im Modul
list_integer
die deferred Prozedurwrite
vonbasic_list_element_t
. - Schreiben Sie ein Testprogramm, welches die Liste mit mehreren
integer
-Werten füllt und geben Sie sich diese aus.
- abstrakter Datentyp
basic_list_element_t
mit Zeiger auf das nächste Element und dem Index des Elements - Datentyp
basic_list_t
als Verwalter der Listen-Struktur mitadd
,write
undfinal
abstract interface
für deferred Typ-gebundene Prozedurbasic_list_element_write
, welche später vom erweiternden Typen implementiert werden muss.
module list
type, abstract :: basic_list_element_t
class(basic_list_element_t), pointer :: next => null ()
integer :: index = -1
contains
procedure(basic_list_element_write), deferred, public :: write
end type basic_list_element_t
type :: basic_list_t
private
class(basic_list_element_t), pointer :: first => null (), last => null ()
contains
procedure, public :: add => basic_list_add
procedure, public :: write => basic_list_write
final :: basic_list_final
end type basic_list_t
abstract interface
subroutine basic_list_element_write (self, unit)
import :: basic_list_element_t
class(basic_list_element_t), intent(in) :: self
integer, intent(in) :: unit
end subroutine basic_list_element_write
end interface
contains
<<module_list_contains>>
end module list
basic_list_add
nimmt ein polymorphes Objekt vom Typbasic_list_element_t
und fügt an das Ende der Liste an und setzt den Index des Elementes.basic_list_write
schreibt den Inhalt der Liste auf die Standardausgabe.basic_list_final
implementiert das Deallokieren der Listen-Elemente und wird automatisch aufgerufen, wenn das Listen-Objekt zerstört wird.
subroutine basic_list_add (self, element)
class(basic_list_t), intent(inout) :: self
class(basic_list_element_t), intent(inout), target :: element
if (.not. associated (self%first)) then
self%first => element
self%last => element
element%index = 1
else
self%last%next => element
element%index = self%last%index + 1
self%last => element
end if
end subroutine basic_list_add
subroutine basic_list_write (self, unit)
class(basic_list_t), intent(in) :: self
integer, intent(in) :: unit
class(basic_list_element_t), pointer :: element
print *, "*** basic_list_write ***"
element => self%first
do while (associated (element))
call element%write (unit)
element => element%next
end do
end subroutine basic_list_write
subroutine basic_list_final (self)
type(basic_list_t), intent(inout) :: self
class(basic_list_element_t), pointer :: element
do while (associated (self%first))
element => self%first
self%first => self%first%next
deallocate (element)
end do
end subroutine basic_list_final
- Der Typ
list_int
erweitert den abstrakten Datentyp so, dass das Element eininteger
-Wert beinhaltet. - Das
interface list_element_int_t
überlädt das Standard-=interface= des Typ-Konstruktor mit einer eigenen Funktion.
module list_int
use list, only: basic_list_element_t
implicit none
private
type, extends (basic_list_element_t) :: list_element_int_t
private
integer :: value = 0
contains
procedure, public :: write => list_element_int_write
end type list_element_int_t
interface list_element_int_t
module procedure list_element_int_init
end interface list_element_int_t
public :: list_element_int_t
contains
<<module_list_int_contains>>
end module list_int
- Die Funktion
list_element_int_init
wird über dasinterface
zum Konstruktor des Datentyps. - Die Prozedure
list_element_int_write
implementiert die deferred Typ-gebundene Prozedur.
type(list_element_int_t) function list_element_int_init (value) result (self)
integer, intent(in) :: value
self%value = value
end function list_element_int_init
subroutine list_element_int_write (self, unit)
class(list_element_int_t), intent(in) :: self
integer, intent(in) :: unit
print *, "value(", self%index, ") = ", self%value
end subroutine list_element_int_write
- Die Liste wird mit jeweils neu-allokierten Elementen gefüllt und im Anschluss wird die Liste auf der Standardausgabe ausgegeben.
program example
use list
use list_int
implicit none
type(basic_list_t) :: l
integer :: i
do i = 1, 10
call add_int_element (i * (i - 1))
end do
call l%write (0)
contains
subroutine add_int_element (value)
integer, intent(in) :: value
class(basic_list_element_t), pointer :: element
allocate (list_element_int_t :: element)
select type (element)
type is (list_element_int_t)
element = list_element_int_t (value)
end select
call l%add (element)
end subroutine add_int_element
end program example
<<example1>>
program example2
implicit none
integer :: i
<<example2>>
print *, "*** Example 2 ***"
print *, "iarray = ", iarray
print *, "iarray2 = ", iarray2
print *, "iarray3 = ", iarray3
end program example2
program example3
complex :: a
complex :: b
print *, "*** Example 3 ***"
a = (2, 1)
b = (-1, 2)
print *, "a = ", a
print *, "b = ", b
<<example3>>
end program example3
program example46
implicit none
integer :: a = 5, b = -1
integer :: i
print *, "*** Beispiel 4-6 ***"
print *, "a = ", a
print *, "b = ", b
<<example_conditionals>>
<<example_loops>>
end program example46
program example7
implicit none
real, dimension(3) :: a, b
real :: vol, circ
print *, "*** Beispiel 7 ***"
a = [1., 2., 3.]; b = [-1., 2., 1.]
call volume_and_length (a, b, vol, circ)
print *, "a = ", a, "b = ", b
print *, "vol = ", vol, "circ = ", circ
end program example7
<<example7>>
program example8
implicit none
<<example8>>
end program example8
program example9
implicit none
<<example9>>
end program example9
program example10
implicit none
<<example10>>
end program example10
program example
implicit none
<<example_array_functions>>
end program example
<<example_operator>>
program example
use math
implicit none
integer, dimension(3, 2) :: A
integer, dimension(2) :: x
A = reshape ([1, 5, 4, 2, 3, 4], shape=[3, 2])
x = [10, -3]
print *, "A.x = ", A * x
end program example
program solution1
implicit none
integer :: i
integer, dimension(10) :: i_vector
real, dimension(5) :: r_vector
print *, "*** Lösung 1 ***"
i_vector = [(i, i = 1, 10)]
r_vector = [(0.2 * i, i = 1, 5)]
print *, "sum_vector: ", sum_vector (i_vector)
print *, "product_vector: ", product_vector (r_vector)
print *, "non_zero_element: ", non_zero_element (i_vector - 1)
contains
<<solution1>>
<<solution2>>
<<solution3>>
end program solution1
integer function sum_vector (vector) result (sum)
integer, dimension(:), intent(in) :: vector
integer :: i, n_vector
n_vector = size (vector)
sum = 0
do i = 1, n_vector
sum = sum + vector(i)
end do
end function sum_vector
real function product_vector (vector) result (product)
real, dimension(:), intent(in) :: vector
integer :: i, n_vector
n_vector = size (vector)
product = 1
do i = 1, n_vector
product = product * vector(i)
end do
end function product_vector
logical function non_zero_element (vector) result (yorn)
integer, dimension(:), intent(in) :: vector
yorn = .not. any (vector == 0) ! all (vector /= 0)
end function non_zero_element
<<module_list>>
<<module_list_int>>
<<test_list>>