Fortran
Moderne Alternativen zu historischen Merkmalen
Suche…
Implizite Variablentypen
Als Fortran ursprünglich entwickelt wurde, war der Speicher von besonderer Bedeutung. Variablen und Prozedurnamen können maximal 6 Zeichen lang sein, und Variablen wurden häufig implizit eingegeben . Dies bedeutet, dass der erste Buchstabe des Variablennamens seinen Typ bestimmt.
- Variablen, die mit i, j, ..., n beginnen, sind
integer
- alles andere (a, b, ..., h und o, p, ..., z) ist
real
Programme wie die folgenden sind akzeptabel Fortran:
program badbadnotgood
j = 4
key = 5 ! only the first letter determines the type
x = 3.142
print*, "j = ", j, "key = ", key, "x = ", x
end program badbadnotgood
Sie können sogar Ihre eigenen impliziten Regeln mit der implicit
Anweisung definieren:
! all variables are real by default
implicit real (a-z)
oder
! variables starting with x, y, z are complex
! variables starting with c, s are character with length of 4 bytes
! and all other letters have their default implicit type
implicit complex (x,y,z), character*4 (c,s)
Die implizite Typisierung gilt nicht mehr als bewährte Methode. Es ist sehr einfach, einen Fehler durch implizite Typisierung zu machen, da Tippfehler unbemerkt bleiben können, z
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Dieses Programm wird glücklich laufen und das Falsche tun.
Um die implizite Typisierung zu deaktivieren, kann die implicit none
Anweisung verwendet werden.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Wenn wir früher hatten implicit none
im Programm oops
oben, würde der Compiler sofort bemerkt, und einen Fehler erzeugt.
Arithmetik if-Anweisung
Die arithmetische if
Anweisung erlaubt die Verwendung von drei Zweigen, abhängig vom Ergebnis eines arithmetischen Ausdrucks
if (arith_expr) label1, label2, label3
Diese if
Anweisung überträgt den Steuerfluss an eine der Kennzeichnungen in einem Code. Wenn das Ergebnis von arith_expr
negativ ist, ist label1
beteiligt, wenn das Ergebnis null ist, wird label2
verwendet, und wenn das Ergebnis positiv ist, wird das letzte label3
angewendet. Arithmetik if
erfordert alle drei Labels, erlaubt aber die Wiederverwendung von Labels. Daher kann diese Anweisung auf zwei Zweige vereinfacht werden, if
.
Beispiele:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Jetzt ist diese Funktion veraltet, da die if
Anweisung und das if-else
Konstrukt dieselbe Funktionalität bieten. Zum Beispiel das Fragment
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
kann als if-else
Konstrukt geschrieben werden
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Eine if
Anweisung als Ersatz für
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
könnte sein
if (X<=0) print*, "Negative or zero"
Nicht-Block-DO-Konstrukte
Der Nicht-Block do
konstruieren sieht aus wie
integer i
do 100, i=1, 5
100 print *, i
Das heißt, wenn die gekennzeichnete Beendigungserklärung keine continue
Anweisung ist. Es gibt verschiedene Einschränkungen für die Anweisung, die als Abbrucherklärung verwendet werden kann, und das Ganze ist im Allgemeinen sehr verwirrend.
Ein solches Nicht-Block-Konstrukt kann in Blockform als neu geschrieben werden
integer i
do 100 i=1,5
print *, i
100 continue
oder besser, mit einer end do
Kündigungserklärung,
integer i
do i=1,5
print *, i
end do
Alternative Rückkehr
Alternativer Rücksprung ist eine Funktion zum Steuern des Ausführungsablaufs bei Rücksprung aus einer Subroutine. Es wird oft als eine Form der Fehlerbehandlung verwendet:
real x
call sub(x, 1, *100, *200)
print*, "Success:", x
stop
100 print*, "Negative input value"
stop
200 print*, "Input value too large"
stop
end
subroutine sub(x, i, *, *)
real, intent(out) :: x
integer, intent(in) :: i
if (i<0) return 1
if (i>10) return 2
x = i
end subroutine
Der alternative Rücksprung wird durch die Argumente *
in der Dummy-Argumentliste des Unterprogramms markiert.
In der call
- Anweisung oben *100
und *200
beziehen sich auf die Aussagen der Bezeichnung 100
und 200
jeweils.
In der Subroutine selbst haben die return
Anweisungen, die alternativer return entsprechen, eine Nummer. Diese Nummer ist kein Rückgabewert, sondern bezeichnet das angegebene Label, an das die Ausführung bei der Rückgabe übergeben wird. In diesem Fall übergibt return 1
die Ausführung an die Anweisung mit der Bezeichnung 100
und return 2
die Ausführung an die Anweisung mit der Bezeichnung 200
. Eine schmucklose return
Anweisung oder der Abschluss der Ausführung einer Subroutine ohne return
Anweisung wird sofort nach der call-Anweisung ausgeführt.
Die alternative Return-Syntax unterscheidet sich stark von anderen Formen der Argumentzuordnung, und die Einrichtung führt eine Flusssteuerung ein, die dem modernen Geschmack entgegensteht. Eine ansprechendere Flusssteuerung kann durch Rückgabe eines ganzzahligen "Status" -Codes verwaltet werden.
real x
integer status
call sub(x, 1, status)
select case (status)
case (0)
print*, "Success:", x
case (1)
print*, "Negative input value"
case (2)
print*, "Input value too large"
end select
end
subroutine sub(x, i, status)
real, intent(out) :: x
integer, intent(in) :: i
integer, intent(out) :: status
status = 0
if (i<0) then
status = 1
else if (i>10)
status = 2
else
x = i
end if
end subroutine
Festes Quellformular
Fortran wurde ursprünglich für ein Format mit festem Format entwickelt, das auf einer Lochkarte mit 80 Spalten basiert:
Ja: Dies ist eine Zeile mit dem Code des Autors
Diese wurden auf einer Kartenstanzmaschine erstellt, ähnlich wie folgt:
Bilder sind Originalfotos des Autors
Wie auf der illustrierten Musterkarte gezeigt, waren die ersten fünf Spalten für Anweisungsbeschriftungen reserviert. Die erste Spalte wurde verwendet, um Kommentare durch einen Buchstaben C zu kennzeichnen. Die sechste Spalte wurde verwendet, um eine Anweisungsfortsetzung zu bezeichnen (durch Einfügen eines anderen Zeichens als Null "0"). Die letzten 8 Spalten wurden für die Identifizierung und Sequenzierung der Karten verwendet. Die Zeichencodierung für Lochkarten hatte nur eine begrenzte Anzahl von Zeichen und bestand nur aus Großbuchstaben. Fortran-Programme sahen daher folgendermaßen aus:
DIMENSION A(10) 00000001
C THIS IS A COMMENT STATEMENT TO EXPLAIN THIS EXAMPLE PROGRAM 00000002
WRITE (6,100) 00000003
100 FORMAT(169HTHIS IS A RATHER LONG STRING BEING OUTPUT WHICH GOES OVE00000004
1R MORE THAN ONE LINE, AND USES THE STATEMENT CONTINUATION MARKER IN00000005
2COLUMN 6, AND ALSO USES HOLLERITH STRING FORMAT) 00000006
STOP 00000007
END 00000008
Das Leerzeichen wurde auch überall ignoriert, außer in einer Hollerith- Zeichenkonstante (wie oben gezeigt). Dies bedeutete, dass Leerzeichen in reservierten Wörtern und Konstanten vorkommen oder vollständig übersehen wurden. Dies hatte den Nebeneffekt einiger irreführender Aussagen wie:
DO 1 I = 1.0
ist eine Zuordnung zur Variablen DO1I
:
DO1I = 1,0
ist eigentlich eine DO
Schleife für die Variable I
Modernes Fortran benötigt diese feste Form der Eingabe jetzt nicht und erlaubt die freie Form unter Verwendung beliebiger Spalten. Kommentare werden jetzt mit einem angezeigt !
die auch an eine Anweisungszeile angehängt werden kann. Leerzeichen sind jetzt an keiner Stelle erlaubt und müssen wie in den meisten anderen Sprachen als Trennzeichen verwendet werden. Das obige Programm könnte in modernem Fortran geschrieben werden als:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH no longer GOES OVER MORE THAN ONE LINE, AND does not need to USE THE STATEMENT CONTINUATION MARKER IN COLUMN 6, or the HOLLERITH STRING FORMAT"
Obwohl die Fortführung im alten Stil nicht mehr verwendet wird, zeigt das obige Beispiel, dass sehr lange Anweisungen weiterhin auftreten. Modernes Fortran verwendet ein &
-Symbol am Ende und Anfang der Fortsetzung. Zum Beispiel könnten wir das Obige in einer besser lesbaren Form schreiben:
! This is a comment statement to explain this example program
Print *,"THIS IS A RATHER LONG STRING BEING OUTPUT WHICH still &
&GOES OVER MORE THAN ONE LINE, AND does need to USE THE STATEMENT &
&CONTINUATION notation"
Gemeinsame Blöcke
In den frühen Formen von Fortran besteht der einzige Mechanismus zum Erstellen eines globalen Variablenspeichers aus Unterprogrammen und Funktionen darin, den COMMON
verwenden. Dies erlaubte, dass Folgen von Variablen Namen haben und gemeinsam genutzt werden können.
Neben benannten allgemeinen Blöcken kann es auch einen leeren (unbenannten) gemeinsamen Block geben.
Ein leerer allgemeiner Block könnte wie deklariert werden
common i, j
wohingegen die benannten Blockvariablen wie deklariert variables
könnten
common /variables/ i, j
Als vollständiges Beispiel können wir uns einen Heap-Store vorstellen, der von Routinen verwendet wird, die Werte hinzufügen und entfernen können:
PROGRAM STACKING
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = 0
READ *, IVAL
CALL PUSH(IVAL)
CALL POP(IVAL)
END
SUBROUTINE PUSH(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
ICOUNT = ICOUNT + 1
ISTACK(ICOUNT) = IVAL
RETURN
END
SUBROUTINE POP(IVAL)
COMMON /HEAP/ ICOUNT, ISTACK(1023)
IVAL = ISTACK(ICOUNT)
ICOUNT = ICOUNT - 1
RETURN
END
Allgemeine Anweisungen können verwendet werden, um implizit den Typ einer Variablen zu deklarieren und das dimension
anzugeben. Dieses Verhalten allein ist oft eine ausreichende Verwirrung. Ferner machen die implizite Speicherzuordnung und die Anforderungen für wiederholte Definitionen über Programmeinheiten die Verwendung allgemeiner Blöcke fehleranfällig.
Schließlich sind allgemeine Blöcke in den darin enthaltenen Objekten sehr eingeschränkt. Ein Array in einem allgemeinen Block muss beispielsweise eine explizite Größe haben. zuordenbare Objekte dürfen nicht vorkommen; Abgeleitete Typen dürfen keine Standardinitialisierung haben.
In modernen Fortran kann dieses Teilen von Variablen durch die Verwendung von Modulen erfolgen . Das obige Beispiel kann wie folgt geschrieben werden:
module heap
implicit none
! In Fortran 2008 all module variables are implicitly saved
integer, save :: count = 0
integer, save :: stack(1023)
end module heap
program stacking
implicit none
integer val
read *, val
call push(val)
call pop(val)
contains
subroutine push(val)
use heap, only : count, stack
integer val
count = count + 1
stack(count) = val
end subroutine push
subroutine pop(val)
use heap, only : count, stack
integer val
val = stack(count)
count = count - 1
end subroutine pop
end program stacking
Benannte und leere allgemeine Blöcke weisen ein leicht unterschiedliches Verhalten auf. Anmerkung:
- Objekte in benannten gemeinsamen Blöcken können anfänglich definiert werden; Objekte in Blanko gemein sind nicht
- Objekte in leeren allgemeinen Blöcken verhalten sich so, als hätte der allgemeine Block das
save
. Objekte in benannten allgemeinen Blöcken ohne dassave
können undefiniert werden, wenn sich der Block nicht im Bereich einer aktiven Programmeinheit befindet
Dieser letzte Punkt kann dem Verhalten von Modulvariablen im modernen Code gegenübergestellt werden. Alle Modulvariablen in Fortran 2008 werden implizit gespeichert und werden nicht undefiniert, wenn das Modul den Gültigkeitsbereich verlässt. Vor Fortran 2008-Modulvariablen, wie Variablen in benannten allgemeinen Blöcken, würden auch undefiniert, wenn das Modul den Gültigkeitsbereich verlässt.
Zugewiesenes GOTO
Assigned GOTO verwendet eine Integer-Variable, der mit der ASSIGN-Anweisung eine Anweisungskennung zugewiesen wird.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Das zugewiesene GOTO ist in Fortran 90 veraltet und in Fortran 95 und höher gestrichen. Dies kann im modernen Code durch Verwendung von Prozeduren, internen Prozeduren, Prozedurzeigern und anderen Funktionen vermieden werden.
Berechnetes GOTO
Berechnetes GOTO ermöglicht die Verzweigung des Programms entsprechend dem Wert eines ganzzahligen Ausdrucks.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Wenn der scalar-integer-expression
gleich 1 ist, fährt das Programm mit der Anweisung label_1
, wenn es gleich 2 ist, wird label_2
usw. Wenn es weniger als 1
oder mehr als n
Programm in der nächsten Zeile fortgesetzt.
Beispiel:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
springt zum Anweisungsetikett 20.
Diese Form von goto
ist in Fortran 95 veraltet und wird durch das select case
Konstrukt abgelöst.
Zugeordnete Formatbezeichner
Vor Fortran 95 konnten zugewiesene Formate für die Eingabe oder Ausgabe verwendet werden. Erwägen
integer i, fmt
read *, i
assign 100 to fmt
if (i<100000) assign 200 to fmt
print fmt, i
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
Die assign
weist einer Integer-Variablen eine Anweisungsbezeichnung zu. Diese Ganzzahlvariable wird später als Formatbezeichner in der print
.
Diese Zuweisung von Formatbezeichnern wurde in Fortran 95 gelöscht. Stattdessen kann moderner Code eine andere Form der Ablaufsteuerung verwenden
integer i
read *, i
if (i<100000) then
print 100, i
else
print 200, i
end if
100 format ("This is a big number", I10)
200 format ("This is a small number", I6)
end
oder eine Zeichenvariable kann als Formatbezeichner verwendet werden
character(29), target :: big_fmt='("This is a big number", I10)'
character(30), target :: small_fmt='("This is a small number", I6)'
character(:), pointer :: fmt
integer i
read *, i
fmt=>big_fmt
if (i<100000) fmt=>small_fmt
print fmt, i
end
Anweisungsfunktionen
Betrachten Sie das Programm
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Hier ist f
eine Anweisungsfunktion. Es hat einen ganzzahligen Ergebnistyp und nimmt ein ganzzahliges Dummy-Argument an. 1
Eine solche Anweisungsfunktion existiert in dem Umfang, in dem sie definiert ist. Insbesondere hat er Zugriff auf Variablen und benannte Konstanten, auf die in diesem Bereich zugegriffen werden kann.
Anweisungsfunktionen unterliegen jedoch vielen Einschränkungen und sind möglicherweise verwirrend (wenn Sie den zufälligen Blick wie eine Array-Elementzuweisungsanweisung betrachten). Wichtige Einschränkungen sind:
- Das Funktionsergebnis und die Dummy-Argumente müssen skalar sein
- Die Dummy-Argumente liegen im selben Bereich wie die Funktion
- Anweisungsfunktionen haben keine lokalen Variablen
- Anweisungsfunktionen können nicht als tatsächliche Argumente übergeben werden
Die Hauptvorteile von Anweisungsfunktionen werden von internen Funktionen wiederholt
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Interne Funktionen unterliegen nicht den oben genannten Einschränkungen, obwohl es vielleicht erwähnenswert ist, dass ein internes Unterprogramm kein weiteres internes Unterprogramm enthalten kann (es kann jedoch eine Anweisungsfunktion enthalten).
Interne Funktionen haben einen eigenen Geltungsbereich, verfügen jedoch auch über eine Host-Zuordnung.
1 In echten alten Codebeispielen ist es nicht ungewöhnlich, dass die Dummy-Argumente einer Anweisungsfunktion implizit typisiert werden, selbst wenn das Ergebnis einen expliziten Typ hat.