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:

  1. la sequenza tra istruzioni
  2. il salto condizionato
  3. 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:
  1. viene valutata la  condizione (scritta rigorosamente sempre tra parentesi tonde, attenzione per chi conosce il Pascal dove non sono previste parentesi);
  2. se la condizione è  vera si esegue l' istruzione a destra della parentesi chiusa dell' IF e poi si passa all'  istruzione successiva;
  3. 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.