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:

Geben Sie hier die Bildbeschreibung ein

Ja: Dies ist eine Zeile mit dem Code des Autors

Diese wurden auf einer Kartenstanzmaschine erstellt, ähnlich wie folgt:

Geben Sie hier die Bildbeschreibung ein

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 das save 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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow