Fortran
Alternative moderne a caratteristiche storiche
Ricerca…
Tipi di variabili implicite
Quando Fortran era originariamente sviluppato, la memoria era un premio. Le variabili e i nomi delle procedure potevano avere un massimo di 6 caratteri e le variabili venivano spesso digitate implicitamente . Ciò significa che la prima lettera del nome della variabile determina il suo tipo.
- le variabili che iniziano con i, j, ..., n sono
integer
- tutto il resto (a, b, ..., h, e o, p, ..., z) sono
real
Programmi come il seguente sono accettabili 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
Potresti anche definire le tue regole implicite con la dichiarazione implicit
:
! all variables are real by default
implicit real (a-z)
o
! 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)
La tipizzazione implicita non è più considerata la migliore pratica. È molto facile commettere un errore utilizzando la digitazione implicita, poiché gli errori di battitura possono passare inosservati, ad es
program oops
real :: somelongandcomplicatedname
...
call expensive_subroutine(somelongandcomplEcatedname)
end program oops
Questo programma funzionerà felicemente e farà la cosa sbagliata.
Per disattivare la digitazione implicita, è possibile utilizzare l'istruzione implicit none
.
program much_better
implicit none
integer :: j = 4
real :: x = 3.142
print*, "j = ", j, "x = ", x
end program much_better
Se avessimo usato implicit none
nel programma oops
sopra, il compilatore si sarebbe accorto immediatamente e avrebbe prodotto un errore.
Aritmetica se dichiarazione
L'istruzione aritmetica if
consente di utilizzare tre rami in base al risultato di un'espressione aritmetica
if (arith_expr) label1, label2, label3
Questa istruzione if
trasferisce il flusso di controllo a una delle etichette in un codice. Se il risultato di arith_expr
è negativo, l' label1
è coinvolta, se il risultato è zero label2
viene utilizzato e se il risultato è positivo, viene applicata l'ultima label3
. Aritmetica if
richiede tutte e tre le etichette ma consente il riutilizzo di etichette, quindi questa affermazione può essere semplificata in due rami if
.
Esempi:
if (N * N - N / 2) 130, 140, 130
if (X) 100, 110, 120
Ora, questa funzione è obsoleta con la stessa funzionalità offerti dal if
dichiarazione e if-else
costrutto. Ad esempio, il frammento
if (X) 100, 110, 120
100 print*, "Negative"
goto 200
110 print*, "Zero"
goto 200
120 print*, "Positive"
200 continue
può essere scritto come il costrutto if-else
if (X<0) then
print*, "Negative"
else if (X==0) then
print*, "Zero"
else
print*, "Positive"
end if
Una sostituzione di dichiarazione if
per
if (X) 100, 100, 200
100 print *, "Negative or zero"
200 continue
può essere
if (X<=0) print*, "Negative or zero"
Costrutti DO non bloccanti
La non-blocco do
costruire assomiglia
integer i
do 100, i=1, 5
100 print *, i
Cioè, dove la dichiarazione di terminazione etichettata non è una dichiarazione continue
. Ci sono varie restrizioni sulla dichiarazione che può essere usata come dichiarazione di terminazione e l'intera faccenda è generalmente molto confusa.
Tale costrutto non blocco può essere riscritto in forma di blocco come
integer i
do 100 i=1,5
print *, i
100 continue
o meglio, usando una dichiarazione di end do
,
integer i
do i=1,5
print *, i
end do
Ritorno alternativo
Il ritorno alternativo è una funzione per controllare il flusso di esecuzione al ritorno da una subroutine. È spesso usato come una forma di gestione degli errori:
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
Il ritorno alternativo è contrassegnato dagli argomenti *
nell'elenco degli argomenti fittizi subroutine.
Nell'istruzione di call
sopra *100
e *200
riferiscono rispettivamente alle dichiarazioni 100
e 200
.
Nella subroutine stessa le dichiarazioni di return
corrispondenti al ritorno alternativo hanno un numero. Questo numero non è un valore di ritorno, ma indica l'etichetta fornita a cui viene trasmessa l'esecuzione al reso. In questo caso, return 1
passa l'esecuzione all'istruzione 100
e return 2
passaggi all'esecuzione etichettata 200
. Una dichiarazione di return
senza ornamenti, o il completamento dell'esecuzione di subroutine senza una dichiarazione di return
, l'esecuzione della passata immediatamente dopo l'istruzione di chiamata.
La sintassi di ritorno alternativa è molto diversa dalle altre forme di associazione degli argomenti e la struttura introduce il controllo del flusso contrario ai gusti moderni. Un controllo di flusso più gradevole può essere gestito con la restituzione di un codice di "stato" intero.
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
Modulo Fisso
Originariamente Fortran è stato progettato per un formato di formato fisso basato su una scheda perforata a 80 colonne:
Sì: questa è una riga del codice dell'autore
Questi sono stati creati su una macchina punch card, molto simile a questo:
Le immagini sono fotografie originali dell'autore
Il formato, come mostrato nella scheda campione illustrata, aveva le prime cinque colonne riservate per le etichette di istruzioni. La prima colonna è stata utilizzata per denotare i commenti con una lettera C. La sesta colonna è stata utilizzata per indicare una continuazione dell'istruzione (inserendo un carattere diverso da zero '0'). Le ultime 8 colonne sono state utilizzate per l'identificazione e il sequenziamento delle carte, il che è stato di grande valore se hai lasciato cadere il mazzo di carte sul pavimento! La codifica dei caratteri per le schede perforate aveva solo un numero limitato di caratteri ed era solo in maiuscolo. Di conseguenza, i programmi Fortran assomigliavano a questo:
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
Anche il carattere dello spazio è stato ignorato ovunque, tranne all'interno di una costante di carattere di Hollerith (come mostrato sopra). Ciò significava che gli spazi potevano verificarsi all'interno di parole e costanti riservate, o completamente persi. Ciò ha avuto l'effetto collaterale di alcune affermazioni piuttosto fuorvianti come:
DO 1 I = 1.0
è un compito assegnato alla variabile DO1I
mentre:
DO1I = 1,0
è in realtà un ciclo DO
sulla variabile I
Il moderno Fortran non richiede ora questa forma di input fissa e consente la forma libera utilizzando qualsiasi colonna. I commenti ora sono indicati da un !
che può anche essere aggiunto a una riga di istruzioni. Gli spazi ora non sono consentiti da nessuna parte e devono essere usati come separatori, proprio come nella maggior parte delle altre lingue. Il suddetto programma potrebbe essere scritto in Fortran moderno come:
! 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"
Sebbene la continuazione vecchio stile non sia più utilizzata, l'esempio sopra illustra che si verificano ancora dichiarazioni molto lunghe. Il Fortran moderno usa un simbolo &
alla fine e all'inizio della continuazione. Ad esempio, potremmo scrivere quanto sopra in una forma più leggibile:
! 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"
Blocchi comuni
Nelle prime versioni di Fortran, l'unico meccanismo per la creazione di archivi di variabili globali visibili da subroutine e funzioni consiste nell'utilizzare il meccanismo di blocco COMMON
. Questo consentiva che le sequenze di variabili fossero nomi e condivisi in comune.
Oltre ai blocchi comuni denominati potrebbe esserci anche un blocco comune vuoto (senza nome).
Un blocco comune vuoto potrebbe essere dichiarato come
common i, j
mentre le variables
blocco nominate potrebbero essere dichiarate come
common /variables/ i, j
Come esempio completo, possiamo immaginare un archivio di heap che viene utilizzato da routine che possono aggiungere e rimuovere valori:
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
È possibile utilizzare dichiarazioni comuni per dichiarare implicitamente il tipo di una variabile e specificare l'attributo della dimension
. Questo comportamento da solo è spesso una fonte sufficiente di confusione. Inoltre, l'associazione di memorizzazione implicita ei requisiti per le definizioni ripetute su unità di programma rendono l'uso di blocchi comuni inclini all'errore.
Infine, i blocchi comuni sono molto limitati negli oggetti che contengono. Ad esempio, una matrice in un blocco comune deve essere di dimensioni esplicite; gli oggetti allocabili potrebbero non verificarsi; i tipi derivati non devono avere l'inizializzazione predefinita.
Nel moderno Fortran questa condivisione di variabili può essere gestita mediante l'uso di moduli . L'esempio sopra può essere scritto come:
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
I blocchi comuni nominati e vuoti presentano comportamenti leggermente diversi. Di nota:
- gli oggetti in blocchi comuni denominati possono essere inizialmente definiti; gli oggetti in bianco comune non devono essere
- gli oggetti nei blocchi comuni vuoti si comportano come se il blocco comune avesse l'attributo di
save
; gli oggetti nei blocchi comuni denominati senza l'attributo disave
potrebbero non essere definiti quando il blocco non si trova nell'ambito di un'unità di programma attiva
Quest'ultimo punto può essere contrapposto al comportamento delle variabili del modulo nel codice moderno. Tutte le variabili del modulo in Fortran 2008 vengono salvate in modo implicito e non diventano indefinite quando il modulo esce dal campo di applicazione. Prima che le variabili del modulo di Fortran 2008, come le variabili nei blocchi comuni denominati, diventassero indefinite anche quando il modulo usciva dall'ambito.
GOTO assegnato
GOTO assegnato utilizza la variabile intera a cui è assegnata un'etichetta di istruzione utilizzando l'istruzione ASSIGN.
100 CONTINUE
...
ASSIGN 100 TO ILABEL
...
GOTO ILABEL
GOTO assegnato è obsoleto in Fortran 90 e eliminato in Fortran 95 e versioni successive. Può essere evitato nel codice moderno utilizzando procedure, procedure interne, indicatori di procedura e altre funzionalità.
GOTO calcolato
GOTO calcolato consente la diramazione del programma in base al valore di un'espressione intera.
GOTO (label_1, label_2,... label_n) scalar-integer-expression
Se l' scalar-integer-expression
è uguale a 1, il programma continua con l'etichetta label_1
, se è uguale a 2 va a label_2
e così via. Se è inferiore a 1
o maggiore di n
programma continua sulla riga successiva.
Esempio:
ivar = 2
...
GOTO (10, 20, 30, 40) ivar
salterà all'etichetta dell'istruzione 20.
Questa forma di goto
è obsoleta in Fortran 95 e successivi, essendo sostituita dal costrutto di select case
.
Specificatori di formato assegnati
Prima di Fortran 95 era possibile utilizzare i formati assegnati per l'input o l'output. Tenere conto
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
L'istruzione assign
assegna un'etichetta di istruzione a una variabile intera. Questa variabile intera viene in seguito utilizzata come identificatore di formato nell'istruzione print
.
Tale attribuzione di identificatori di formato è stata eliminata in Fortran 95. Invece, un codice più moderno può utilizzare un'altra forma di controllo del flusso di esecuzione
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
o una variabile di carattere può essere usata come specificatore di formato
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
Funzioni di dichiarazione
Considera il programma
implicit none
integer f, i
f(i)=i
print *, f(1)
end
Qui f
è una funzione di dichiarazione. Ha un tipo di risultato intero, prendendo un argomento fittizio intero. 1
Tale funzione di dichiarazione esiste all'interno dell'ambito in cui è definita. In particolare, ha accesso a variabili e costanti con nome accessibili in tale ambito.
Tuttavia, le funzioni di dichiarazione sono soggette a molte restrizioni e sono potenzialmente confuse (guardando un'occhiata casuale come una dichiarazione di assegnazione di elementi dell'array). Restrizioni importanti sono:
- il risultato della funzione e gli argomenti fittizi devono essere scalari
- gli argomenti fittizi sono nella stessa portata della funzione
- le funzioni di istruzione non hanno variabili locali
- le funzioni di istruzioni non possono essere passate come argomenti reali
I principali vantaggi delle funzioni di statement sono ripetuti dalle funzioni interne
implicit none
print *, f(1)
contains
integer function f(i)
integer i
f = i
end function
end
Le funzioni interne non sono soggette alle restrizioni sopra menzionate, anche se è forse interessante notare che un sottoprogramma interno non può contenere ulteriori sottoprogrammi interni (ma può contenere una funzione di istruzione).
Le funzioni interne hanno il loro ambito ma hanno anche un'associazione host disponibile.
1 Negli esempi di codice vecchio e reale, non sarebbe inusuale vedere gli argomenti fittizi di una funzione di istruzione digitati implicitamente, anche se il risultato ha un tipo esplicito.