Nur noch kurze Frist für Einspruch bei den Normungsgremien von ANSI, ISO und DIN (Teil 2)

Radikalkur: Fortran soll grundlegend geändert werden

08.01.1988

Die genormte Programmiersprache Fortran macht zur Zeit die radikalste und weitreichendste Veränderung ihrer Existenz durch. Fortran-Experten aller Länder, insbesondere die Mitglieder der Arbeitsgruppe X3J3 des American National Standards Institute (ANSI) und der Working Group 5 der International Standardization Organisation (ISO), haben einen neuen Normentwurf erarbeitet, der unter dem Arbeitstitel "Fortran 8x" nun zur Kommentierung veröffentlicht wird.

IDENTIFY-Anweisung

Neben diesen Matrix-Manipulationen mit der SETRANGE-Anweisung gibt es die Möglichkeit, Matrizen mit der IDENTIFY-Anweisung Datenobjekten zuzuordnen, indem man einer Matrix einen Alias-Namen gibt und mit der IDENTIFY-Anweisung eine Abbildung durchführt. Dieser Vorgang ist dynamisch. Die IDENTIFY-Anweisung stellt eine elementweise Beziehung zwischen der Alias-Matrix und der Stamm-Matrix her. Die Alias-Matrix hat ähnliche Eigenschaften wie die Matrix-Abschnitte; sie ist nur wesentlich flexibler hinsichtlich der Bildung von Untermengen.

Beispiel:

REAL, ARRAY (1:10, 1:10) :: A

REAL, ARRAY (:), ALIAS :: DIAG

IDENTIFY (DIAG (I) = A (I, I), I = 1:10

In diesem Beispiel wird eine Alias-Matrix DIAG (eindimensional) definiert, die alle Elemente der Diagonalen der Matrix A vom Rang 2 enthält. Alias- und Stamm-Matrix müssen denselben Typ haben. Die Matrix DIAG muß das Attribut ALIAS besitzen und nimmt erst Gestalt an, nachdem sie durch eine IDENTIFY-Anweisung mit einer Stamm-Matrix assoziiert worden ist. Die IDENTIFY-Anweisung spezifiziert die Abbildung zwischen der Alias- und der Stamm-Matrix. Zwei Elemente einer Alias-Matrix dürfen nicht mit einem Element der Stamm-Matrix assoziiert werden.

Der Index-Ausdruck der Stamm-Matrix muß die Form

Co+C1*I1+C2*I2+...

haben, wobei cj ganzzahlige Konstante und Ij Indices der Alias-Matrix sind. Damit können parallelogrammförmige Ausschnitte mit "regelmäßigen Lücken" aus einer vorhandenen Matrix ausgewählt werden.

Wenn eine Alias-Matrix in mehreren IDENTIFY-Anweisungen auftritt, muß sie immer auf dieselbe Stamm-Matrix oder deren Teilobjekte Bezug nehmen. Wenn die Stamm-Matrix selbst eine Alias-Matrix ist, muß sie bereits assoziiert sein.

Maskierte Matrix-Zuweisung

Diese Anweisung kann entweder eine Zuweisung oder einen Konstrukt enthalten, der durch die WHERE-Anweisung ausgeführt wird.

Beispiel:

REAL, ARRAY(1:10, 1:20) TEMP, DIFF TEMP, DRUCK, DIFF DRUCK

...

WHERE (TEMP<100.0) TEMP = TEMP - DIFF TEMP

In diesem Beispiel handelt es sich um eine Zuweisung. Bei der Ausführung wird der boolesche Ausdruck in der Klammer ausgewertet und für alle Elemente, für die dieser Ausdruck den Wert .TRUE. annimmt wird den entsprechenden Elementen der Matrix TEMP das Ergebnis zugewiesen.

Der WHERE-Konstrukt darf ebenfalls nur Zuweisungen enthalten:

WHERE (TEMP>50.0)

DRUCK = DRUCK + DIFF DRUCK

TEMP = TEMP - DIFF TEMP

ELSEWHERE

DRUCK = 1.0

END WHERE

In diesem Beispiel wird zuerst der boolesche Klummerausdruck ausgewertet. Für alle Elemente, für die der Ausdruck den Wert .TRUE. annimmt, werden die Zuweisungen (elementweise) im WHERE-Block ausgeführt, für die übrigen Werte (.FALSE.) werden die Zuweisungen des ELSEWHERE-Blockes ausgeführt.

Arithmetische Genauigkeit

Der Datentyp der reellen Zahlen nimmt Werte an, um die reellen Zahlen aus der Mathematik zu approximieren. Ein Prozessor muß mindestens zwei Approximationsmethoden liefern, um die Menge der Daten vom Typ reell zu definieren. Jede Methode wird charakterisiert durch zwei Parameter: die effektive dezimale Genauigkeit und den effektiven dezimalen Exponentenbereich. Diese effektiven Größen ergeben sich aus der Maschinendarstellung der Daten (zum Beispiel für den Typ REAL und DOUBLE PRECISION). Bei der Definition von REAL-Größen können zusätzlich zwei Parameter angegeben werden, um die minimale dezimale Genauigkeit und den dezimalen Exponentenbereich zu spezifizieren:

REAL(PRECISION = 8,EXPONENT RANGE = 150) X(100)

In diesem Beispiel besitzt jedes Element der Matrix X eine Genauigkeit von wenigstens acht Dezimalziffern und einen Exponentenbereich, der von 10-l50 bis 10+150 reicht. Der Kompilierer benutzt diese Angaben, um die Variable auf die geeignetste interne Rechenvariable (in der Regel eine Gleitkommavariable) abzubilden, die diese Anforderungen befriedigt.

Die Vorschrift besagt, daß der Prozessor eine Approximationsmethode mit einer effektiven dezimalen Genauigkeit auswählen muß, die größer oder gleich der spezifizierten ist, und einem effektiven Exponentenbereich, der größer oder gleich dem spezifizierten Exponentenbereich ist. Wenn mehr als zwei Methoden existieren, muß die Methode ausgewählt werden, die die spezifizierte dezimale Genauigkeit am geringsten überschreitet.

Durch diese Methode wird es möglich, Fortran-Programme auf eine genormte Art besser portabel zu machen, was insbesondere für Bibliotheken mit arithmetischen Unterprogrammen relevant ist.

Die effektive dezimale Genauigkeit einer Methode kann man mit der Funktion EFFECTIVE PRECISION und den effektiven dezimalen Exponentenbereich mit der Funktion EFFECTIVE EXPONENT RANGE abfragen.

Um eine Konstante mit einer bestimmten dezimalen Genauigkeit und einem vorgegebenen Exponentenbereich anzugeben, muß man die EXPONENT-LETTER-Anweisung benutzen:

EXPONENT LETTER (8,150) L

...

CALL SUB (10.954785 L17)

...

SUBROUTINE SUB (DUMMY)

REAL (8,150) DUMMY

In diesem Fall muß die Anweisung EXPONENT LETTER benutzt werden, um Genauigkeit und Exponentenbereich für die an das Unterprogramm übergebene numerische Konstante vom Typ REAL zu spezifizieren.

Übergabegenauigkeit REAL(*. *)

Die jetzige Regelung für formale Parameter sieht vor: Wenn für einen formalen Parameter eine explizite Genauigkeit angegeben wurde, dann müssen die zugehörigen aktuellen Parameter dieselbe Genauigkeit haben. Da diese Einschränkung sehr restriktiv wirkt, gibt es darüber hinaus die Möglichkeit, die formalen REAL-Parameter mit der Genauigkeit REAL(*.*) zu spezifizieren. Diese Parameter dürfen mit aktuellen Parametern beliebiger Genauigkeit beim Aufruf versorgt werden; mit einer Einschränkung, daß alle so spezifizierten formalen Parameter einer Prozedur immer dieselbe Genauigkeit während eines Aufrufes haben müssen.

Beispiel:

FUNCTION BELGEN(X,Y)

REAL(*,*) :: X, Y

...

In diesem Fall darf die Funktion BELGEN mit zwei REAL-Argumenten beliebiger Genauigkeit aufgerufen werden, die beide jedoch immer dieselbe Genauigkeit haben müssen.

Datenstrukturen

Definition

Als zusätzlicher Datentyp wurden Datenstrukturen (Derived Types) eingeführt. Datenstrukturen werden in TYPE-Blöcken definiert; dazu muß der Name der Datenstruktur sowie Name und Typ seiner Komponenten angegeben werden.

Beispiel:

TYPE PERSON

INTEGER ALTER

CHARACTER (LEN = 50) NAME

END TYPE PERSON

In diesem Beispiel wird eine Datenstruktur mit dem Namen PERSON definiert, die die beiden Komponenten ALTER und NAME enthält. Es ist erlaubt, Datenstrukturen, die vorher definiert wurden, innerhalb von Datenstrukturen zu verwenden. Eine Datenstruktur-Definition darf auch eine Parameterliste enthalten.

Beispiel:

TYPE STRING (MAX LAENGE)

INTEGER LAENGE

CHARACTER (LEN - MAX LAENGE) TEXT

END TYPE STRING

In diesem Beispiel wird der Parameter MAX LAENGE benutzt, um die tatsächliche Länge der Zeichenkette TEXT festzulegen. Die in der Parameterliste enthaltenen Namen sind formale Parameter vom Typ ganzzahlig (INTEGER) und dürfen als Primärausdruck für die Deklaration der Komponenten benutzt werden. Wenn ein Objekt einer solchen Struktur deklariert wird, müssen Werte für diese formalen Parameter vereinbart werden. Es gibt zwei reservierte Wörter für diese Parameterliste: PRECISION und EXPONENT RANGE. Sie spezifizieren die Genauigkeit und den Exponentenbereich für Komponenten vom Typ REAL, COMPLEX und TYPE.

Beispiel:

TYPE MATRIX (PRECISION, EXPONENT RANGE, ORDER)

REAL (PRECISION, EXPONENT RANGE, ARRAY (ORDER, ORDER) ::

A END TYPE MATRIX

In dem obigen Beispiel wurden alle drei Parameterarten für die Definition einer Datenstruktur benutzt.

Deklaration

Die Deklaration (Einrichtung) einer Struktur erfolgt mit der Typ-Anweisung, wobei als Objekt auch eine Matrix einer Struktur gebildet werden darf.

Beispiele:

TYPE (PERSON) :: PERSONA

TYPE (PERSON), ARRAY(1:10) :: PERSONB

TYPE (STRING(MAX LAENGE = 20)) :: ARCHIV

PERSONA ist ein Skalar vom Typ PERSON, PERSONB ist eine Matrix vom Typ PERSON und ARCHIV ist eine Struktur vom Typ STRING mit maximal 20 Zeichen für die zweite Komponente.

Strukturkomponenten

Eine Strukturdefinition enthält eine oder mehrere Komponenten. Der Zugriff auf die Komponenten erfolgt mit dem Zeichen "%"; zum Beispiel:

PERSONA % ALTER

PERSONB (J) % ALTER

Konstruktion von Strukturwerten und -konstanten

Durch eine Folge von Werten, das heißt ein Wert je Komponente, erzeugt man einen Strukturwert.

Beispiel:

PERSON (21, 'JOHN SMITH')

STRING (20) (19, "NOW IS THE TIME FOR")

Operationen

Von den internen Operationen ist nur die Zuweisung anwendbar. Es können jedoch weitere Operationen mit eigenen Operatoren definiert werden (siehe Kapitel "Prozeduren").

Prozeduren

Folgende Prozeduren werden unterschieden: Externe Prozeduren, Modul-Prozeduren und interne Prozeduren. Daneben gibt es noch den Prozedur-Interface Block. Es gibt zwei prinzipiell unterschiedliche Prozedurenarten: Funktionen und Subroutinen.

- Eine externe Prozedur ist ein externes Unterprogramm, das sowohl mit Fortran als auch mit anderen Mitteln erzeugt werden kann.

- Eine Modul-Prozedur wird durch ein Unterprogramm in einem Modul definiert. Sie kann von einem Unterprogramm desselben Moduls und von jeder Programmeinheit, die das Modul benutzt, aufgerufen werden.

- Eine interne Prozedur wird von einem internen Unterprogramm definiert. Ein internes Unterprogramm kann zu einem Hauptprogramm oder zu einem Unterprogramm gehören. Es ist lokal zu diesem "Wirt" in dem Sinne, daß auf das interne Unterprogramm innerhalb des Gültigkeitsbereiches des Wirtes und all seiner internen Prozeduren zugegriffen werden darf; außerhalb dieses Bereiches kann nicht auf das interne Unterprogramm zugegriffen werden.

- In einem Prozedur-Interface-Block sollen die Prozedurparameter beschrieben werden. Er kontrolliert die Art des Aufrufes. Das Interface besteht aus den Charakteristika der Prozedur: den Namen mit den dazugehörigen Attributen für jeden formalen Parameter, dem definierten Operator, mit dem eine Funktion ebenfalls aufgerufen werden kann, und ob sich hinter dem Aufruf einer Subroutine eine definierte Zuweisungsanweisung verbirgt. Innerhalb eines Gültigkeitsbereiches darf es nur einen Interface-Block für eine Prozedur geben. Die Namen der Argumente und der Operator der Prozedur werden durch den Interface-Block überschrieben.

Beispiele:

- Modul-Unterprogramm:

MODULE VORZEICHEN

CONTAINS

INTEGER FUNCTION SIGNUM (I)

IF (I<0) THEN

SIGNUM = -1

ELSE IF (I = = 0) THEN

SIGNUM = 0

ELSE

SIGNUM = + 1

END IF

END FUNCTION SIGNUM

END MODULE

(siehe auch Kapitel "Modulkonzept").

- Interne Prozedur:

SUBROUTINE EXTERN

REAL A, B, C

...

CALL LOESCHEN

..

CONTAINS

SUBROUTINE LOESCHEN

REAL X

A = 0.0

B = 0.0

C = 0.0

...

END SUBROUTINE LOESCHEN

END SUBROUTINE EXTERN

In dem oben angegebenen Beispiel enthält die externe Subroutine EXTERN die interne Subroutine LOESCHEN, die die Variablen A, B, C des Unterprogramms EXTERN löscht. Die internen Prozeduren werden mit CONTAINS eingeleitet und stehen am Ende der externen Subroutine.

Prozedur-Interface-Block:

SUBROUTINE INVERSE (A, FN)

REAL A

INTERFACE

FUNCTION FN (B)

REAL FN, B

END INTERFACE

...

END SUBROUTINE

In diesem Beispiel spezifiziert der Interface-Block, daß das zweite Argument der Subroutine eine reelle Funktion mit einem reellen Argument ist.

Rekursive Prozeduren

Funktionen und Unterprogramm in Fortran dürfen künftig auch rekursiv sein, wenn sie das Schlüsselwort RECURSIVE in ihrem Prozedurkopf tragen. Dieses Schlüsselwort muß immer dann angegeben werden, wenn sich die Prozedur, direkt oder indirekt, selbst aufruft.

Beispiel:

RECURSIVE INTEGER FUNCTION

BEST (A, B) RESULT (BST)

INTERGER A, B

...

BST = BEST (A-1, B-1)

END FUNCTION BEST

Mit dem Schlüsselwort RESULT wird die Ergebnisvariable der Funktion spezifiziert (im obigen Beispiel: BST). Jedes Auftreten des Funktionsnamens im Ausführungsteil der Funktion ist ein rekursiver Funktionsaufruf. Wenn RESULT nicht angegeben wird, ist die Ergebnisvariable identisch mit dem Funktionsnamen und jedes Auftreten des Funktionsnamens im Ausführungsteil bezieht sich auf die Ergebnisvariable und ist kein rekursiver Funktionsaufruf.

Benutzer-definierte Operatoren

Für eine Funktion kann der Anwender auch einen Operator definieren. Dadurch wird es möglich, die Funktion auch mit diesem vereinbarten Operator aufzurufen, was jedoch nur für ein und zwei Argumente geht. Der Operator wird durch die OPERATOR-Option vereinbart, wobei er folgende Gestalt annehmen darf: ".Buchstabenfolge." zum Beispiel ".ENTHAELT.". Jede Referenz mit dem vereinbarten Operator, bei der die Operanden dieselben Charakteristika wie die formalen Funktionsparameter aufweisen, wird als Funktionsaufruf behandelt. Dadurch wird es möglich, daß ein Modul sämtliche Strukturdefinitionen und alle Prozeduren für die Definition der Operation mit diesen Datentypen enthält. Auf diese Art und Weise unterstützt die Modultechnik die Datenabstraktion und stellt einen Beitrag zur Modernisierung von Fortran dar.

Erweiterung interner Operatoren

Anstelle eines neuen Operators kann man auch einem internen Operatorsymbol eine zusätzliche Bedeutung geben, indem man dieses Operatorsymbol in der OPERATOR-Option einer FUNCTION-Anweisung für weitere Datentypen definiert.

Beispiel:

TYPE STRING (MAX LAENGE) TEXT

INTEGER LAENGE

CHARACTER (LEN = MAX LAENGE) TEXT

END TYPE STRING

...

CONTAINS

TYPE (STRING(*)) FUNCTION VERKETTEN (S1, S2) OPERATOR (//)

...

Diese Funktionsdefinition ermöglicht es, die Objekte vom Typ STRING durch Aufruf der Funktion VERKETTEN oder durch die Benutzung des Operators "//" zu verketten:

VERKETTEN (STR1, STR2) oder STR1 // STR2

In diesem Beispiel wird der interne Operator "//", der für Daten vom Typ CHARACTER definiert ist, auch für die Datenstruktur STRING definiert ("Operator Overloading").

Zuweisungssubroutine

Eine Subroutine kann man mit dem zusätzlichen Schlüsselwort ASSIGNMENT auch als Zuweisung spezifizieren. Diese Subroutine darf dann auch in Form einer Zuweisung aufgerufen werden. Die Subroutine muß in diesem Fall genau zwei Argumente haben, die entsprechend einer Zuweisung spezifiziert wurden.

Beispiel:

SUBROUTINE ASSIGN ZEICHENKETTE (STR, CHAR) ASSIGNMENT

TYPE (STRING (*)) :: STR

CHARACTER (LEN = *) :: CHAR

STR % LAENGE = LEN (CHAR)

STR % TEXT = CHAR

END SUBROUTINE ASSIGN ZEICHENKETTE

In dem Beispiel wird auf die oben erfolgte Definition der Struktur STRING zurückgegriffen. LEN ist eine innere Funktion, die die Lange der Zeichenkette liefert. Durch diese Definition der Subroutine ist es möglich, einer Struktur vom Typ STRING eine Variable vom Typ CHARACTER zuzuweisen:

STR1 = 'Anton'

Überlagerung von Namen (Name Overloading)

Auf zwei oder mehreren Funktionen darf mit demselben Namen zugegriffen werden. Die Unterscheidung, welche Funktion tatsächlich aufgerufen wird, muß sich eindeutig aus der Argumentliste ergeben (bei den inneren Funktionen kennt man dieses Prinzip schon länger, dort wird es "Generic Names" genannt). Wir sahen bereits, daß man in ähnlicher Weise mit einem Operatorsymbol auf mehrere Funktionen zugreifen und mit dem Zeichen "=" mehrere Subroutines aufrufen konnte. wird fortgesetzt