Fortran: la sintassi
Struttura dei programmi Fortran.
Tutte le istruzioni devono essere contenute all' interno di blocchi di
programma. Ogni programma deve contenere solo un blocco "program":
program nome
... istruzioni ...
end program nome
dove nome è un identificatore univoco costruito secondo le
regole per
i nomi delle variabili (max 63 caratteri, solo alfabetici, cifre e
underscore (_).
Le istruzioni sono classificate in eseguibili e non eseguibili.
Sono eseguibili tutte
le istruzioni che vengono tradotte dal
compilatore in istruzioni in linguaggio macchina eseguibili dal
processore. Le istruzioni non eseguibili
hanno invece il ruolo di
descrivere al compilatore aspetti dell' organizzazzione dei dati o del
programma necessari per permettere l' organizzazione e l' utilizzo dei
dati nella generazione del codice macchina.
Per esempio, istruzioni come
a=b*c+d
print*,x,y,z
if(det >= 0)then
sono tutte istruzioni eseguibili (un if...then
in realta' andrebbe
considerato insieme alle collegate istruzioni else if (...)then , else
ed end if).
Dichiarazioni come
integer :: i,j
complex :: z
o istruzioni come
use pippo
program nome
function f(x)
sono non-eseguibili.
Tipi dati
Tutti i dati (costanti, variabili o altro) di un programma Fortran
appartengono ad un tipo dati che corrisponde sia ad una particolare
codifica dei dati nella rappresentazione interna del computer (p.es.
quanti byte, con che significato dei bit, quali indirizzi di memoria
sono coinvolti,...) sia all' insieme delle operazioni consentite sui
dati stessi (p.es., mentre è definita la somma di due dati
numerici
non lo è per due dati di tipo logico).
I tipi primitivi del Fortran sono:
INTEGER
REAL
COMPLEX
LOGICAL
CHARACTER
Questi possono essere modificati mediante modificatori (p.es. per
utilizzare maggiore o minore numero di bit per ciascuna variabile di un
tipo, oppure posono essere utilizzati per creare tipi dati derivati
corrispondenti a insiemi di variabili dotate di una
strutturazione interna o designati per usi particolari
(array, tipi dati derivati, puntatori).
Sui tipi numerici (integer, real,
complex) sono definite le
operazioni dell' aritmetica ( somma, +; sottrazione, -;
moltiplicazione, *; divisione, /; elevazione a potenza, **) e
quelle di confronto (uguaglianza, ==; essere diversi, /=;
maggiore, >; minore, <; maggiore o uguale, >=; minore o
uguale, <=), dove previste dall' uso matematico (per i complex non
è definita una relazione d' ordine).
Da notare che la divisione tra numeri integer
obbedisce in senso
stretto le regole dell' aritmetica degli interi. In
particolare, la divisione tra interi in cui il divisore sia
maggiore del dividendo (p.es. 1/2) dà zero.
Le operazioni aritmetiche tra dati di uguale tipo numerico
danno un risultato dello stesso tipo numerico degli operandi.
Se viene richiesta un' operazione tra tipi dati numerici diversi (es.
un intero ed un reale o tra reali rappresentati con numero diverso di
bit) il Fortran prevede una conversione implicita (automatica) di uno
dei due operandi in modo da eseguire l' operazione su operandi
dello stesso tipo e con la stessa rappresentazione
interna.
La regola per la conversione è basata sul concetto di una
gerarchia tra i tipi dati e la conversione avviene trasformando il dato
piu' in basso nella gerarchia nel tipo dati del dato piu'
in alto.
L' ordine dei tipi nuerici (dal basso verso l' alto
è integer, real, complex
e, per dati dello stesso tipo, la rapprsentazione con maggior numero di
bit è considerata piu' in alto.
Pertanto, in una somma tra un intero ed un reale, l' intero viene
prima trasformato in un dato di tipo real
e poi viene eseguita la somma tra numeri real e il risultato sara' real. Similmente se si sommano due
interi, uno rappresentato mediante 2 byte e l' altro mediante 8
byte, quello rappresentato mediante 2 byte viene convertito
in intero ad 8 byte prima di eseguire al somma. Il risultato
sara' intero a 8 byte.
Per esempio, le seguenti due divisioni danno lo stesso risultato (0.5):
1.0/2.0
1.0/2
invece
1/2
da' come risutato 0 (divisione tra interi con risultato intero).
Le variabili di tipo character
sono utilizzate per immagazzinare singoli caratteri o sequenze. Nella
versione piu' semplice
character :: z
z rappresenta una variabile i cui possibili valori sono tutti i
caratteri dell' insieme di caratteri utilizzato (in genere
secondo la codifica ASCII). P.es. possiamo attribuira a z i valori:
z="a"
z="A"
z=";"
z="9"
z='"'
Da notare la presenza degli apici (" " oppure
equivalentemente ' ' ) per indicare che quanto tra essi compreso
va interpretato com carattere letterale.
Negli esempio di uso sopra riportato, non e' possibile assegnare a z
una sequenza di piu' di un carattere:
z="abc"
comporterebbe l' attribuzione a z del solo valore "a", tralasciando i
caratteri successivi ("bc"). Per poter immagazzinare sequenze di
lunghezza fissata la dichiarazione della variabile ti tipo character va
modificata:
character(len=10) :: z
permette di assegnare a z sequenze fino a 10 caratteri. Se la sequenza
assegnata a z fosse di lunghezza minore dei 10 caratteri, i rimanenti
caratteri di z sarebbero spazi (" ").
In lettura, mediante istruzione read*,z
, i valori corrispondenti a sequenze vanno assegnati ponendoli tra
apici sempici ('...') o doppi (" ... "). E' anche possibile non
uilizzare apici ma in questo caso un eventuale spazio viene
interpretato come fine della sequenza.
Es:
a fronte di un' istruzione read*,z
con z dichiarato come sopra, l' input
"abc defgh" attribuira' a z il valore abc defgh (spazi tra c e d inclusi)
abc defgh attribuira' a z il valore abc seguito da 7 spazi.
Per le espressioni di tipo character e' prevista l' operazione di
concatenazione (//). P. es. se x e y sono variabili di tipo
character(len=5) di valore "abcde" e "fghil" rispettivamente, l'
espressione x//y corrispondera' alla sequenza "abcdefghil"
Sono anche definiti gli operatori di confronto ( ==, /=, >, <,
>=, <= ) dove maggiore o minore significano seguire o precedere
nell' ordine lessicografico (quello del dizionario).
E' infine possible estrarre un singolo carattere o una sottosequenza da
una sequenza.
Es:
characher(len=10) :: z="abc123ef=?"
z(2:2) corrispondera' a "b" (il secondo carattere della sequenza)
z(1:3) corrispondera' a "abc" (sottosequenza che parte dal primo e
arriva fino al terzo carattere.
z(9:10) corrispondera' a "=?"
z(1:3)//z(7:8) corrispondera' a "abcef".
Il tipo dati logical prevede solo due possibili valori .true. e .false. (i punti fanno parte
integrante delle due costanti logiche).
Operazioni tra espressioni logiche sono costruibli a partire degli
operatori:
.and. (and logico:
x .and. y e' vero se e solo se x e y sono entrambi .true.)
.or. (or
logico: x .or. y e' false se e solo se x e y sono entrambi
.false.)
.not. (negazione:
.not. x e' .true. se x e' .false. e viceversa)
.eqv. (equivalenza
o doppia implicazione: x.eqv.y vera se e solo se x e y hanno lo stesso
valore)
.neqv. (inequivalenza:
x.neqv.y vera se e solo se x e y hanno valori logici diversi)
Costrutti di controllo
Ogni programma è realizzabile sfruttando tre
costrutti disponibili in qualsiasi linguaggio di programmazione
evoluto:
- la sequenza tra istruzioni
- il salto condizionato
- l' iterazione di istruzioni (ciclo)
La sequenza corrisponde banalmente al fatto che l' ordine con cui le
istruzioni appaiono nel linguaggio di alto livello corrisponde
al' ordine nel quale si richiede vengano eseguite e operazioni
dal computer. Pertanto l' ordine con cui andranno scritte le istruzioni
eseguibili del Fortran sara' esattamente quello dei vari passi
dell' algoritmo che si desidera implementare. Una conseguenza di
questo (sorgente di innumerevoli problemi per chi inizia a programmare)
è che non vanno
utilizzate variabili in nessuna istruzione senza prima aver loro
assegnato un valore.
Il salto condizionato corrisponde ad una violazione del principio di
sequenza stretta in funzione del verificarsi o meno (esser vero o
falso) di una condizione.
Il principale metodo di sato condizionato del Fortran è in
costrutto IF. Anche per ragioni di compatibilita', esistono
diversi cstrutti IF. Tuttavia in queste note ci atterremo solo a
due:
IF(condizione) istruzione
e IF...THEN...ELSE. Ad esempio:
IF(condizione)THEN
blocco istruzioni 1
ELSE IF(condizione 2)THEN
blocco istruzioni 2
ELSE IF(condizione 3)THEN
blocco istruzioni 3
ELSE
blocco "else"
END IF
Il primo costrutto, raccomandato se c' è una sola
istruzione da eseguire nel caso la condizione sia verificata,
funziona nel seguente modo:
- viene valutata la condizione (scritta rigorosamente sempre
tra parentesi tonde, attenzione per chi conosce il Pascal dove non sono
previste parentesi);
- se la condizione è vera si esegue l' istruzione a
destra della parentesi chiusa dell' IF e poi si passa all'
istruzione successiva;
- se la condizione è falsa si esegue direttamente l'
istruzione successiva all' IF.
Il secondo permette di eseguire blocchi diversi di codice a
seconda del verificarsi di un numero arbitrario di condizioni. Da
notare che tutti i sottoblocchi ELSE IF(...) THEN o ELSE non sono
obbligatori.
Pertanto la situazione piu' semplice è quella di un
costrutto come nel seguente esempio:
IF(X < 0)THEN
X = -X
END IF
equivalente a
IF(X<0) X=-X
in cui si cambia segno a X solo se il valore originale era
negativo.
Se invece appaiono altre condizioni ELSE IF(...) THEN, ciascuna
di
queste viene valutata in sequenza e la prima che risulta vera
provoca l' esecuzione del blocco di istruzioni seguenti fino al
successivo ELSE. A tal punto il sistema passa ad eseguire al
prima istruzione dopo la fine del blocco (END IF).
La condizione ELSE finale serve all' esecuzione di codice (blocco
"else") nella situazione in cui nessuna delle condizioni esplicite sia
verificata. Esempio:
IF(discriminante < 0)THEN
print*," non ci sono soluzioni reali"
ELSE
x1 = (-b +sqrt(b**2-4*a*c))/(2*a)
x2 = (-b -sqrt(b**2-4*a*c))/(2*a)
print*, x1,x2
END IF
Il ciclo o ripetizione di istruzioni permette di ripetere un gruppo di
istruzioni in modo controllato.
Il costrutto Fortran corrispondente è il ciclo DO.
La corrispondente sintassi è duplice:
ciclo senza contatore:
DO
blocco di istruzioni
IF(condizione di uscita) EXIT
blocco istruzioni
END DO
oppure ciclo con contatore:
DO I=I1,I2,I3
blocco istruzioni
END DO
Nel primo caso le istruzioni tra DO ed END DO saranno ripetute
fino al momento in cui la condizione dell' IF viene
verificata. A questo punto la prossima istruzione che verra'
eseguita è laprima dopo END DO.
Nel secondo caso la variabile I prende il valore I1 ed il blocco di
istruzioni viene eseguito solo se I*I3 risulta minore o
uguale a I1*I2 e poi viene incrementata la variabile I col
valore I3 e, col nuovo valore di I si rifa' il
test sulla condizione I*I3<=I2*I3.
Appena la condizione non è verificata il ciclo
termna.