Fortran
Moderne alternatieven voor historische elementen
Zoeken…
Impliciete variabeletypen
Toen Fortran oorspronkelijk werd ontwikkeld, was geheugen een premium. Variabelen en procedurenamen konden maximaal 6 tekens bevatten en variabelen werden vaak impliciet getypt . Dit betekent dat de eerste letter van de variabelenaam het type bepaalt.
- variabelen die beginnen met i, j, ..., n zijn
integer
- al het andere (a, b, ..., h en o, p, ..., z) is
real
Programma's zoals de volgende zijn acceptabel 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
U kunt zelfs uw eigen impliciete regels definiëren met de implicit
verklaring:
! all variables are real by default
implicit real (a-z)
of
! 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)
Impliciet typen wordt niet langer als beste praktijk beschouwd. Het is heel gemakkelijk om een fout te maken met impliciet typen, omdat typefouten bijvoorbeeld onopgemerkt kunnen blijven
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Dit programma wordt graag uitgevoerd en doet het verkeerde.
Om impliciet typen uit te schakelen, kan de implicit none
instructie worden gebruikt.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Als we implicit none
hadden gebruikt in het programma oops
hierboven, zou de compiler het onmiddellijk hebben opgemerkt en een fout produceren.
Rekenkundige als statement
Rekenkundige if
instructie maakt het mogelijk om drie takken te gebruiken, afhankelijk van het resultaat van een rekenkundige uitdrukking
if (arith_expr) label1, label2, label3
Dit if
statement transfers control flow aan één van de labels in een code. Als het resultaat van arith_expr
negatief is, is label1
betrokken, als het resultaat nul is, wordt label2
gebruikt en als het resultaat positief is, wordt het laatste label3
toegepast. Rekenkunde if
alle drie labels vereist zijn, maar het hergebruik van labels mogelijk maakt, daarom kan deze verklaring worden vereenvoudigd tot een tak met twee if
.
Voorbeelden:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Nu is deze functie verouderd met dezelfde functionaliteit die wordt aangeboden door de if
instructie en if-else
constructie. Bijvoorbeeld het fragment
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
kan worden geschreven als het if-else
construct
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Een if
statement-vervanging voor
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
kan zijn
if (X<=0) print*, "Negative or zero"
Niet-blok DO-constructen
Het non-block do
construct ziet eruit
integer i
do 100, i=1, 5
100 print *, i
Dat wil zeggen, wanneer de gelabelde beëindigingsverklaring geen continue
verklaring is. Er zijn verschillende beperkingen op de verklaring die kunnen worden gebruikt als de beëindigingsverklaring en de hele zaak is over het algemeen erg verwarrend.
Een dergelijk niet-blokconstructie kan in blokvorm worden herschreven als
integer i
do 100 i=1,5
print *, i
100 continue
of beter, met behulp van een end do
beëindiging statement,
integer i
do i=1,5
print *, i
end do
Alternatief rendement
Alternatief rendement is een faciliteit om de uitvoeringsstroom bij terugkeer vanuit een subroutine te regelen. Het wordt vaak gebruikt als een vorm van foutafhandeling:
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
De alternatieve return wordt gemarkeerd door de argumenten *
in de subroutine dummy argumentlijst.
In de call
hierboven verwijzen *100
en *200
naar de verklaringen met het label respectievelijk 100
en 200
.
In de subroutine zelf hebben de return
die overeenkomen met alternatieve retouren een nummer. Dit nummer is geen retourwaarde, maar geeft het opgegeven label aan waarnaar uitvoering wordt teruggestuurd. In dit geval geeft return 1
uitvoering door aan de instructie met het label 100
en return 2
uitvoering door aan de instructie met het label 200
. Een onopgesmelde return
, of voltooiing van subroutine-uitvoering zonder een return
, passess-uitvoering tot onmiddellijk na de oproepverklaring.
De alternatieve syntaxis van het rendement is heel anders dan andere vormen van argumentassociatie en de faciliteit introduceert flow control in tegenstelling tot moderne smaken. Meer aangename flow control kan worden beheerd met de terugkeer van een geheel "status" code.
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
Formulier vaste bron
Fortran is oorspronkelijk ontworpen voor een vast formaat op basis van een ponskaart met 80 kolommen:
Ja: dit is een regel van de eigen code van de auteur
Deze zijn gemaakt op een kaartponsmachine, ongeveer zoals dit:
Afbeeldingen zijn originele fotografie door de auteur
Het formaat, zoals weergegeven op de geïllustreerde voorbeeldkaart, had de eerste vijf kolommen gereserveerd voor statementlabels. De eerste kolom werd gebruikt om opmerkingen aan te duiden met een letter C. De zesde kolom werd gebruikt om een voortzetting van de instructie aan te geven (door een ander teken dan een nul '0' in te voegen). De laatste 8 kolommen werden gebruikt voor kaartidentificatie en sequencing, wat behoorlijk waardevol was als je je stapel kaarten op de grond liet vallen! De karaktercodering voor ponskaarten had slechts een beperkt aantal karakters en was alleen hoofdletters. Daarom zagen Fortran-programma's er als volgt uit:
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
Het ruimtekarakter werd ook overal genegeerd, behalve in een Hollerith- karakterconstante (zoals hierboven weergegeven). Dit betekende dat spaties konden voorkomen in gereserveerde woorden en constanten, of volledig konden worden gemist. Dit had het neveneffect van enkele nogal misleidende uitspraken zoals:
DO 1 I = 1.0
is een toewijzing aan de variabele DO1I
terwijl:
DO1I = 1,0
is eigenlijk een DO
lus op de variabele I
Modern Fortran heeft nu deze vaste vorm van invoer niet nodig en staat vrije vorm toe met behulp van kolommen. Reacties worden nu aangegeven met een !
die ook aan een afschriftregel kan worden toegevoegd. Spaties zijn nu nergens toegestaan en moeten worden gebruikt als scheidingstekens, net als in de meeste andere talen. Het bovenstaande programma kan in het moderne Fortran worden geschreven 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"
Hoewel de oude stijl niet langer wordt gebruikt, illustreert het bovenstaande voorbeeld dat er nog steeds zeer lange uitspraken zullen voorkomen. Modern Fortran gebruikt een &
-symbool aan het einde en begin van de voortzetting. We zouden het bovenstaande bijvoorbeeld in een beter leesbare vorm kunnen schrijven:
! 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"
Gemeenschappelijke blokken
In de vroege vormen van Fortran is het enige mechanisme voor het creëren van een globale variabele store zichtbaar vanuit subroutines en functies het gebruik van het COMMON
-blokmechanisme. Hierdoor konden sequenties van variabelen namen zijn en gemeenschappelijk worden gebruikt.
Naast benoemde gemeenschappelijke blokken kan er ook een blanco (naamloos) gemeenschappelijk blok zijn.
Een leeg gemeenschappelijk blok kan worden gedeclareerd als
common i, j
terwijl de genoemde variables
zouden kunnen worden verklaard
common /variables/ i, j
Als een volledig voorbeeld kunnen we ons een heap-winkel voorstellen die wordt gebruikt door routines die waarden kunnen toevoegen en verwijderen:
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
Algemene instructies kunnen worden gebruikt om impliciet het type van een variabele aan te geven en het dimension
kenmerk op te geven. Alleen al dit gedrag is vaak een voldoende bron van verwarring. Verder maakt de impliciete opslagassociatie en vereisten voor herhaalde definities tussen programma-eenheden het gebruik van gemeenschappelijke blokken vatbaar voor fouten.
Tot slot zijn gemeenschappelijke blokken erg beperkt in de objecten die ze bevatten. Een array in een gemeenschappelijk blok moet bijvoorbeeld een expliciete grootte hebben; toewijsbare objecten mogen niet voorkomen; afgeleide typen mogen geen standaardinitialisatie hebben.
In het moderne Fortran kan dit delen van variabelen worden afgehandeld door het gebruik van modules . Het bovenstaande voorbeeld kan worden geschreven als:
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
Benoemde en lege gemeenschappelijke blokken hebben een enigszins ander gedrag. Van belang:
- objecten in benoemde gemeenschappelijke blokken kunnen aanvankelijk worden gedefinieerd; objecten in blanco zijn dat niet
- objecten in lege gemeenschappelijke blokken gedragen zich alsof het gemeenschappelijke blok het kenmerk
save
heeft; objecten in benoemde gemeenschappelijke blokken zonder het kenmerksave
kunnen ongedefinieerd worden wanneer het blok niet binnen het bereik van een actieve programma-eenheid valt
Dit laatste punt kan worden afgezet tegen het gedrag van modulevariabelen in moderne code. Alle modulevariabelen in Fortran 2008 worden impliciet opgeslagen en worden niet ongedefinieerd wanneer de module buiten bereik valt. Vóór Fortran 2008 zouden modulevariabelen, zoals variabelen in benoemde gemeenschappelijke blokken, ook ongedefinieerd worden wanneer de module buiten bereik zou vallen.
Toegewezen GOTO
Toegewezen GOTO gebruikt de variabele integer waaraan een instructie-label wordt toegewezen met behulp van de ASSIGN-instructie.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Toegewezen GOTO is verouderd in Fortran 90 en verwijderd in Fortran 95 en later. Het kan in moderne code worden vermeden door procedures, interne procedures, procedurepunten en andere functies te gebruiken.
GOTO berekend
Berekende GOTO maakt vertakking van het programma mogelijk volgens de waarde van een geheel getal-uitdrukking.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Als scalar-integer-expression
gelijk is aan 1, gaat het programma verder met statement label label_1
, als het gelijk is aan 2 gaat het naar label_2
enzovoort. Als het minder dan 1
of groter dan n
programma wordt voortgezet op de volgende regel.
Voorbeeld:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
springt naar statement label 20.
Deze vorm van goto
is verouderd in Fortran 95 en later en wordt vervangen door het select case
construct.
Toegewezen opmaakspecificaties
Vóór Fortran 95 was het mogelijk om toegewezen formaten te gebruiken voor invoer of uitvoer. Overwegen
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
Met de assign
statement wordt een instructielabel assign
aan een variabele met een geheel getal. Deze variabele met geheel getal wordt later gebruikt als de opmaakaanduiding in de print
.
Een dergelijke indelingstoewijzingstoewijzing is verwijderd in Fortran 95. In plaats daarvan kan modernere code een andere vorm van uitvoering van stroombeheer gebruiken
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
of een tekenvariabele kan worden gebruikt als de opmaakspecificatie
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
Statement-functies
Overweeg het programma
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Hier is f
een statement-functie. Het heeft een integer resultaattype, waarbij één integer dummy-argument wordt gebruikt. 1
Een dergelijke statement-functie bestaat binnen het bereik waarin deze is gedefinieerd. In het bijzonder heeft het toegang tot variabelen en benoemde constanten die binnen dat bereik toegankelijk zijn.
Statement-functies zijn echter onderhevig aan vele beperkingen en kunnen potentieel verwarrend zijn (kijkend naar een casual blik zoals een toewijzingsstatement van een array-element). Belangrijke beperkingen zijn:
- het functieresultaat en dummyargumenten moeten scalair zijn
- de dummy-argumenten vallen binnen hetzelfde bereik als de functie
- instructiefuncties hebben geen lokale variabelen
- instructiefuncties kunnen niet worden doorgegeven als feitelijke argumenten
De belangrijkste voordelen van statement-functies worden herhaald door interne functies
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Interne functies zijn niet onderworpen aan de hierboven genoemde beperkingen, hoewel het misschien vermeldenswaard is dat een intern subprogramma mogelijk geen verder intern subprogramma bevat (maar het kan een statementfunctie bevatten).
Interne functies hebben hun eigen toepassingsgebied, maar hebben ook beschikbare host-associatie.
1 In echte oude codevoorbeelden zou het niet ongebruikelijk zijn om te zien dat de dummy-argumenten van een statement-functie impliciet worden getypt, zelfs als het resultaat een expliciet type heeft.