next up previous contents
Nächste Seite: Pointer Aufwärts: Fortran 90 Vorherige Seite: Feldoperationen   Inhalt

Benutzerdefinierte Datentypen

Außer den im vorigen Kapitel behandelten Feldern kennt Fortran 90 auch Strukturen: benutzerdefinierte Datentypen, die aus einer endlichen Anzahl von (unterschiedlichen) Datentypen zusammengesetzt sind.
TYPE [[,PRIVATE | ,PUBLIC] ::] typname
  [PRIVATE]
  [SEQUENCE]
  typkomponenten_definition(en)
END TYPE [typname]
Wenn die Typdefinitionsanweisung in einem MODULE steht und somit global bekannt sein kann, können die Schlüsselworte PRIVATE oder PUBLIC angegeben werden. Default-Wert ist PUBLIC, d.h. dieser benutzerdefinierte Datentyp ist allen Programmkomponenten, die das MODULE mit einer USE -Anweisung zugänglich gemacht haben, bekannt.
PRIVATE innerhalb des Datentyps bedeutet, daß die einzelnen Typkomponenten nicht sichtbar sein sollen, der Datentyp selbst allerdings schon; auch an dieser Stelle darf PRIVATE nur innerhalb eines MODULEs stehen.
Mit SEQUENCE wird die Speicherreihenfolge definiert, was nur in COMMON- und EQUIVALENCE-Anweisungen von Bedeutung ist.
Die typkomponenten_definition ist entweder eine Definition eines vordefinierten oder eines benutzerdefinierten Datentyps, dabei sind keine Konstanten zugelassen (also kein PARAMETER-Attribut)

Die Definition eines Datenobjekts mit benutzerdefinertem Datentyp erfolgt dann durch

TYPE (typname) [[, attribute] ::] namens_liste
Als attribute kommen dabei ALLOCATABLE, DIMENSION, ... in Frage

Es gibt auch die Möglichkeit Strukturkonstante zu verwenden, sie haben die Form
typname (liste)
wobei die in liste angeführten (konstanten oder variablen) Ausdrücke in der entsprechenden Reihenfolge den Komponenten der Struktur (und damit des Typs) zugeordnet werden.
Einzelne Komponenten der Struktur werden (ähnlich wie bei objektorientierter Programmierung) über ihren "Namen" in der Struktur angesprochen:
variablen_name%komponenten_name

Sollten benutzerdefinierte Datentypen mit Unterprogrammen verwendet werden, so ist es günstig, ein MODULE zu schreiben, in dem die Datentypen definiert sind: sie sind dann in allen Unterprogrammen, in dem das MODULE mit USE eingebunden ist, definiert.

MODULE Kugeln                          ! globale Daten unter 'Kugeln'
 IMPLICIT NONE
 TYPE Punkt_2D                         ! Typdefinition:
  REAL, DIMENSION(2) :: x              ! 1-dimensionaler Vektor der Laenge 2
 END TYPE Punkt_2D

 TYPE Punkt_3D                         ! Typdefinition:
  REAL, DIMENSION(3) :: x              ! 1-dimensionaler Vektor der Laenge 3
 END TYPE

 TYPE kugel                            ! Typdefinition:
  REAL :: r                            ! 1 REAL-Zahl und
  TYPE(Punkt_3D) :: M                  ! eine Variable vom Typ Punkt_3D
 END TYPE

 TYPE kreis                            ! Typdefinition:
  TYPE(Punkt_2D) :: M                  ! eine Variable vom Typ Punkt_2D
  REAL :: r                            ! und eine REAL-Zahl
 END TYPE kreis
END MODULE

Bei der Übersetzung des Programms wird ein File kugeln.mod angelegt, auf den dann von anderen Programmen aus mit USE zugegriffen werden kann:

PROGRAM typ_test
 USE Kugeln                            ! USE musz an erster Stelle stehen !
 IMPLICIT NONE                         ! verwende die Definitionen in 'Kugeln'
 INTEGER      :: i
 TYPE (Kugel) :: Sphere                ! eine Variable vom Typ Kugel
 TYPE (Kreis) :: Circle, schnitt       ! eine Variable und eine Funktion vom
                                       ! Typ Kreis
 CALL eingabe (Sphere)                 ! lies Kugelwerte ein
 Circle = null (Sphere)                ! berechne den Schnittkreis mit xy-Ebene
 CALL ausgabe (Circle)                 ! gib Schnittkreis aus
END PROGRAM

Und genauso kann man es auch in Unterprogrammen einbinden:

SUBROUTINE eingabe (Kug)
 USE Kugeln, ONLY: Kugel               ! verwende nur den Typ Kugel aus Kugeln
 IMPLICIT NONE
 TYPE (Kugel) :: Kug                   ! Formalparameter vom Typ Kugel
 INTEGER      :: i

 WRITE (*,'(A)') 'Geben Sie eine Kugel ein:'
 WRITE (*,'(A)',ADVANCE='NO') 'Mittelpunkt: '
 READ (*,*) (Kug%M%x(i), i=1,3)        ! lies die Mittelpunktskoordinaten
 WRITE (*,'(A)',ADVANCE='NO') 'Radius: '
 READ (*,*) Kug%r                      ! lies den Radius
END
FUNCTION null (Sph)
 USE Kugeln                            ! verwende Definitionen aus Kugeln
 IMPLICIT NONE

 INTEGER      :: i
 TYPE (Kugel) :: Sph                   ! Formalparameter vom Typ Kugel
 TYPE (Kreis) :: null                  ! FUNCTION sei vom Typ Kreis

 IF (Sph%M%x(3) > Sph%r) THEN          ! wenn z-Wert des Kugelmittelpunktes
   null%r = Sph%r - Sph%M%x(3)         ! * groesser als Radius, dann gibt es
 ELSE IF (Sph%M%x(3) == Sph%r) THEN    !   keinen Schnittkreis
   null%r = 0.0                        ! * wenn gleich Radius, dann gibt es 
   DO i = 1, 2                         !   einen Beruehrpunkt
    null%M%x(i) = Sph%M%x(i)        
   ENDDO
 ELSE                                  ! * sonst gibt es einen Schnittkreis
   DO i = 1,2
    null%M%x(i) = Sph%M%x(i)
   ENDDO
   null%r = SQRT(Sph%r**2. - Sph%M%x(3)**2.)
 ENDIF
END FUNCTION

SUBROUTINE ausgabe (Circ)
 USE Kugeln, ONLY: Kreis               ! verwende nur die Kreise
 IMPLICIT NONE

 TYPE (Kreis) :: Circ                  ! Formalparameter vom Typ Kreis

 IF (Circ%r < 0.0) THEN
   WRITE (*,*) 'Kugel liegt ueber der xy-Ebene'
 ELSE IF (Circ%r == 0.0) THEN
   WRITE (*,*) 'Kugel beruehrt die xy-Ebene'
 ELSE
   WRITE (*,*) 'Kugel hat einen Schnittkreis mit xy-Ebene:'
   WRITE (*,*) 'Mittelpunkt und Radius: ', Circ
 ENDIF
END SUBROUTINE

Das läßt sich dann natürlich z.B. mit MODULE PROCEDUREn und überladenen Operatoren erweitern:

MODULE Kugeln
 ...

 INTERFACE OPERATOR (==)      ! Neudefinition von ==: falls == mit 2 Parametern
  MODULE PROCEDURE same_circ  ! vom Typ Kreis (siehe Def. same_circ) aufgerufen
 END INTERFACE                ! wird, dann wird die Fkt. same_circ aufgerufen

CONTAINS  ! Module-Unterprogramme - sind dann ebenfalls global bekannt
 LOGICAL FUNCTION same_circ (Kreis1, Kreis2)
  TYPE (Kreis), INTENT(IN) :: Kreis1, Kreis2
  same_circ = .FALSE.
  IF ((Kreis1%M%x(1) == Kreis2%M%x(1)) .AND. &
      (Kreis1%M%x(2) == Kreis2%M%x(2)) .AND. &
      (Kreis1%r == Kreis2%r)) same_circ = .TRUE.
 END FUNCTION

END MODULE

PROGRAM typ_test
 USE Kugeln                       ! USE musz an erster Stelle stehen !
 IMPLICIT NONE
 INTEGER      :: i, schnitt, mode
 TYPE (Kugel) :: Sphere(2)        ! 1-dim. Feld der Laenge 2 vom Typ Kugel
 TYPE (Kreis) :: Circle1, Circle2, null

 DO i = 1, 2
  CALL eingabe (Sphere(i))
 ENDDO
 Circle1 = null (Sphere(1))
 Circle2 = null (Sphere(2))
 CALL ausgabe (Circle1)
 CALL ausgabe (Circle2)
 IF (Circle1 == Circle2) THEN
   WRITE (*,*) 'die beiden Kreise decken sich!'
 ELSE
   mode = schnitt (Circle1, Circle2)
   IF (mode < -2) THEN
     WRITE (*,*) 'kleinerer Kreis liegt im groesseren'
   ELSE 
     WRITE (*,'(A,I1,A)') 'Diese Kreise haben ', ABS(mode), ' Schnittpunkte'
   ENDIF
 ENDIF
END PROGRAM

INTEGER FUNCTION schnitt (Circ1, Circ2)
  USE Kugeln
  IMPLICIT NONE
  TYPE (Kreis) :: Circ1, Circ2
  REAL :: abst, radien_min, radien_sum
  
  abst = abstand(Circ1%M,Circ2%M)
  radien_sum = Circ1%r+Circ2%r 
  radien_min = ABS(Circ1%r-Circ2%r)
  IF (abst < radien_min) THEN              ! abst + rk < rg
    schnitt = -3                           ! kleiner im groszen Kreis
  ELSE IF (abst == radien_min) THEN
    schnitt = -1                           ! Kreise beruehren sich innen
  ELSE IF (abst == radien_sum) THEN   
    schnitt = 1                            ! Kreise beruehren sich von auszen
  ELSE IF (abst < radien_sum) THEN
    schnitt = 2                            ! 2 Schnittpunkte
  ELSE
    schnitt = 0                            ! kein Schnittpunkt
  ENDIF
CONTAINS
  REAL FUNCTION abstand (Punkt1, Punkt2)
    USE Kugeln
    IMPLICIT NONE
    TYPE(Punkt_2D) :: Punkt1, Punkt2
    abstand = SQRT((Punkt1%x(1)-Punkt2%x(1))**2 + (Punkt1%x(2)-Punkt2%x(2))**2)
  END FUNCTION
END FUNCTION

next up previous contents
Nächste Seite: Pointer Aufwärts: Fortran 90 Vorherige Seite: Feldoperationen   Inhalt
Reinfried O. Peter 2001-09-07