Fortran
Moderna alternativ till historiska drag
Sök…
Implicita variabeltyper
När Fortran ursprungligen utvecklades var minnet högt. Variabler och procedurnamn kan ha högst 6 tecken, och variabler skrivs ofta implicit . Detta innebär att den första bokstaven i variabelns namn bestämmer dess typ.
- variabler som börjar med i, j, ..., n är
integer
- allt annat (a, b, ..., h, och o, p, ..., z) är
real
Program som följande är acceptabla 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
Du kan till och med definiera dina egna implicita regler med det implicit
uttalandet:
! all variables are real by default
implicit real (a-z)
eller
! 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)
Implicit typning anses inte längre vara bästa praxis. Det är mycket lätt att göra ett misstag med implicit typning, eftersom skrivfel kan gå obemärkt, t.ex.
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Det här programmet körs gärna och gör fel sak.
För att stänga av implicit typ kan det implicit none
uttalande användas.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Om vi hade använt implicit none
i programmet oops
ovan, skulle kompilatorn omedelbart ha märkt det och skapat ett fel.
Aritmetisk om uttalande
Aritmetisk if
uttalande tillåter en att använda tre grenar beroende på resultatet av ett aritmetiskt uttryck
if (arith_expr) label1, label2, label3
Detta if
uttalandet överför kontrollflödet till en av etiketterna i en kod. Om resultatet av arith_expr
är negativt är label1
involverat, om resultatet är noll label2
används, och om resultatet är positivt label3
sista label3
. Aritmetik if
krävs alla tre etiketter men det tillåter återanvändning av etiketter, därför kan detta uttalande förenklas till en tvågren if
.
Exempel:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Nu är den här funktionen föråldrad med samma funktionalitet som erbjuds av if
uttalandet och if-else
konstruktionen. Till exempel fragmentet
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
kan skrivas som if-else
konstruktionen
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
En if
uttalande ersättning för
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
kanske
if (X<=0) print*, "Negative or zero"
Icke-blockera DO-konstruktioner
Den icke-blockera do
konstruktionen ser ut
integer i
do 100, i=1, 5
100 print *, i
Det vill säga där det märkta uppsägningsuttalandet inte är ett continue
. Det finns olika begränsningar för uttalandet som kan användas som uppsägningsuttalande och hela saken är i allmänhet väldigt förvirrande.
En sådan icke-blockkonstruktion kan skrivas om i blockform som
integer i
do 100 i=1,5
print *, i
100 continue
eller bättre, med hjälp av en end do
avslutnings uttalande,
integer i
do i=1,5
print *, i
end do
Alternativ avkastning
Alternativ återgång är en anordning för att kontrollera körningsflödet vid retur från en subrutin. Det används ofta som en form av felhantering:
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
Den alternativa returen markeras med argumenten *
i listan med subroutine dummy.
I call
ovan *100
och *200
hänvisas till uttalanden märkta 100
respektive 200
.
I subrutinen själv return
uttalanden motsvarande alternativ avkastning har ett antal. Detta nummer är inte ett returvärde, men anger den medföljande etiketten som exekveringen skickas vid returen. I detta fall överlämnar return 1
exekvering till uttalandet som är märkt 100
och return 2
överför exekvering till uttalandet märkt 200
. En avskalade return
uttalande eller slutförande av exekverings subrutin utan return
uttalande passess utförande omedelbart efter samtalet uttalande.
Alternativ syntax är mycket annorlunda än andra former av argumentförening och anläggningen introducerar flödeskontroll i motsats till modern smak. Mer behaglig flödeskontroll kan hanteras med returnering av en heltalskod "status".
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
Fast källformulär
Fortran designades ursprungligen för ett fast formatformat baserat på ett stansat kort med 80 kolumner:
Ja: Det här är en rad med författarens egen kod
Dessa skapades på en kortstansmaskin, ungefär så här:
Bilder är originalfotografering av författaren
Formatet, som visas på det illustrerade provkortet, hade de första fem kolumnerna reserverade för uttalandeetiketter. Den första kolumnen användes för att beteckna kommentarer med en bokstav C. Den sjätte kolumnen användes för att beteckna en fortsättning av uttalandet (genom att infoga något annat tecken än noll '0'). De sista 8 kolumnerna användes för kortidentifiering och sekvensering, vilket var ganska värdefullt om du tappade ditt kortlek på golvet! Teckenkodningen för stansade kort hade bara en begränsad uppsättning tecken och var endast versaler. Som ett resultat såg Fortran-program så ut:
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
Rymdkaraktären ignorerades också överallt, utom i en Hollerith- karaktärskonstant (som visas ovan). Detta innebar att utrymmen kunde uppstå i reserverade ord och konstanter, eller helt missade. Detta hade bieffekten av några ganska vilseledande uttalanden som:
DO 1 I = 1.0
är en tilldelning till variabeln DO1I
medan:
DO1I = 1,0
är faktiskt en DO
slinga på variabeln I
Modern Fortran kräver inte denna fasta inmatningsform och tillåter fri form med hjälp av några kolumner. Kommentarer indikeras nu av a !
som också kan bifogas till en uttalande rad. Utrymmen är nu inte tillåtna någonstans och måste användas som separatorer, precis som på de flesta andra språk. Ovanstående program kan skrivas i modernt Fortran som:
! 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"
Även om den gamla stilen fortsätter inte längre används, illustrerar exemplet ovan att mycket långa uttalanden fortfarande kommer att inträffa. Modern Fortran använder en &
symbol i slutet och början av fortsättningen. Vi kan till exempel skriva ovanstående i en mer läsbar form:
! 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"
Vanliga block
I de tidiga formerna av Fortran är den enda mekanismen för att skapa global variabel butik synlig från subroutiner och funktioner att använda den COMMON
. Detta tillät sekvenser av variabler att namnges och delas gemensamt.
Förutom namngivna vanliga block kan det också finnas ett tomt (ej namngivet) gemensamt block.
Ett tomt gemensamt block kan förklaras som
common i, j
medan de nämnda variables
kan deklareras som
common /variables/ i, j
Som ett komplett exempel kan vi föreställa oss en heap-butik som används av rutiner som kan lägga till och ta bort värden:
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
Vanliga uttalanden kan användas för att implicit deklarera typen av en variabel och för att specificera dimension
attribut. Detta beteende ensam är ofta en tillräcklig källa till förvirring. Vidare gör den underförstådda lagringsassociationen och kraven på upprepade definitioner över programenheter användningen av vanliga block som är benägna att fel.
Slutligen är vanliga block mycket begränsade i de objekt de innehåller. Till exempel måste en matris i ett gemensamt block vara av uttrycklig storlek; allokerbara objekt kanske inte inträffar; härledda typer får inte ha standardinitialisering.
I moderna Fortran kan denna delning av variabler hanteras med hjälp av moduler . Exemplet ovan kan skrivas som:
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
Namngivna och tomma vanliga block har något olika beteenden. Av anmärkning:
- objekt i namngivna vanliga block kan definieras initialt; föremål i tomt gemensamt ska inte vara
- objekt i tomma gemensamma block fungerar som om det gemensamma blocket har attributet
save
; objekt i namngivna vanliga block utan attributetsave
kan bli odefinierade när blocket inte ligger inom ramen för en aktiv programenhet
Den senare punkten kan kontrasteras med modulvariablernas beteende i modern kod. Alla modulvariabler i Fortran 2008 sparas implicit och blir inte odefinierade när modulen går utanför räckvidden. Innan modulvariablerna för Fortran 2008, som variabler i nämnda gemensamma block, skulle också bli odefinierade när modulen gick utanför räckvidden.
Tilldelad GOTO
Tilldelad GOTO använder heltalvariabel till vilken en uttalande etikett tilldelas med ASSIGN-uttalandet.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
Tilldelad GOTO är föråldrad i Fortran 90 och raderas i Fortran 95 och senare. Det kan undvikas i modern kod genom att använda procedurer, interna procedurer, procedurpekare och andra funktioner.
Beräknad GOTO
Beräknad GOTO tillåter förgrening av programmet enligt värdet på ett heltaluttryck.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Om scalar-integer-expression
är lika med 1 fortsätter programmet vid uttalande label_1
, om det är lika med 2 går det till label_2
och så vidare. Om det är mindre än 1
eller större än n
fortsätter programmet på nästa rad.
Exempel:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
kommer att gå till uttalande etikett 20.
Denna form av goto
är föråldrad i Fortran 95 och senare och ersätts av select case
.
Tilldelade formatspecifikationer
Innan Fortran 95 var det möjligt att använda tilldelade format för input eller output. Överväga
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
assign
tilldelar en uttalande etikett till en heltalvariabel. Denna heltalvariabel används senare som formatspecifikationen i print
.
Sådan formatspecifikationstilldelning raderades i Fortran 95. I stället kan mer modern kod använda någon annan form av exekveringsflödeskontroll
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
eller en teckenvariabel kan användas som formatspecifikation
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
Uttalande funktioner
Tänk på programmet
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Här är f
en uttalande funktion. Den har heltal resultattyp, tar ett heltal dummy argument. 1
En sådan uttalande funktion finns inom ramen för den definieras. I synnerhet har den tillgång till variabler och namngivna konstanter som är tillgängliga inom det omfånget.
Uttagsfunktioner är emellertid föremål för många begränsningar och är potentiellt förvirrande (titta på en tillfällig blick som en uppsättning av arrayelement). Viktiga begränsningar är:
- funktionsresultatet och dummy-argumenten måste vara skalära
- dummy-argumenten är i samma omfattning som funktionen
- uttalande funktioner har inga lokala variabler
- uttalande funktioner kan inte skickas som faktiska argument
De viktigaste fördelarna med uttalande funktioner upprepas av interna funktioner
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Interna funktioner omfattas inte av de ovan nämnda begränsningarna, även om det kanske är värt att notera att ett internt underprogram inte kan innehålla ytterligare internt underprogram (men det kan innehålla en uttalande funktion).
Interna funktioner har sin egen räckvidd men har också tillgänglig värdförening.
1 I verkliga gamla kodexempel skulle det inte vara ovanligt att se dummy-argumenten för en uttalande funktion implicit skrivas, även om resultatet har en uttrycklig typ.