Contact me sending an e-mail (antispam defense activated)
Title: Appunti Fortran
Author: Sandro Tosi
Last modified: 12/02/2004
Cerchero' qui di elencare alcune `informazioni' utili per chi deve
iniziare a programmare in ForTran: Formula Translator.
Magari, una conoscenza di altri linguaggi di programmazione puo'
aiutare a capire meglio questo documento...
***Introduzione
Quelle che seguono sono le informazioni che ho raccolto mentre
imparavo Fortran per preparare la tesi di laurea. Non mi sono posto
l'obiettivo di essere completo o esatto e per questo ogni correzione o
suggerimento e' ben accetto. Quello che si trovera' in questi appunti
sono alcune nozioni di base, che certo non escludono uno studio piu'
approfondito del linguaggio su un manuale, che invito vivamente a
leggere per tutte le particolarita' che non ho affrontato e quindi
neanche riportato, ma che possono essere molto utili.
Fortran e' utilizzato principalmente per elaborazioni di calcolo
numerico, dove l'efficienza e la velocita' di esecuzione che offre,
sono ritenute piu' importanti rispetto ad altri aspetti del
linguaggio.
***Informazioni generali
Fortran non e' case sensitive.
La memorizzazione delle matrici avviene per colonne. L'allocazione
della memoria e' statica; al momento della dichiarazione delle
variabili.
Fortran _non_ e' un liguaggio ricorsivo, quindi sottoprogrammi non
potranno richiamare se stessi.
Un consiglio che e' valido per qualunque linguaggio di programmazione:
COMMENTATE IL CODICE!! In questo modo si rendera' chiaro cosa deve
fare il codice che stiamo scrivendo, consentira' una sua futura
modifica in modo puntuale ed, inoltre, una sua comprensione da parte
di altre persone oltre a chi scrive il codice.
***Perche' usare Fortran?
Iniziamo con le note dolenti: rispetto ad altri linguaggio di
programmazione, Fortran risulta piu' essenziale, ad esempio per la sua
gestione completamente statica delle risorse, prima tra tutte le
memoria; inoltre, offre ben pochi strumenti per la tipizzazione e la
strutturazione dei dati ed aspetti della programmazione strutturata
non sono ancora diffusi nel linguaggio.
Tuttavia, Fortran presenta molti punti a suo favore: consente di
utilizzare numeri reali con un elevato numero di cifre significative,
la possibilita' di utilizzare numeri ed aritmetica complessi,
l'esistenza di un gran numero di librerie altamente testate ed
efficienti ed anche la possibilita' di richiamare sottoprogrammi
scritti in linguaggi diversi dal Fortran.
Queste proprieta' lo rendono molto adatto al calcolo scientifico.
***Le colonne
Le colonne in cui viene scritto il codice sorgente, in Fortran, sono
molto importanti: uno schema potrebbe essere il seguente...
12345 6 7............72
|---| ^ |-------------|
label | codice
|
continuazione
della riga prec.
Nelle prime 5 colonne e' possibile inserire delle label (vedremo in
seguito cosa significa), sulla sesta, inserendo un qualsiasi
carattere, e' possibile continuare la riga precendente (un esempio
potrebbe essere quello della dichiarazione di un gran numero di
variabili); fra la settima e la 72-esima si devono inserire le
istruzioni: e' consigliato non oltrepassare la colonna 72, anche se
alcuni compilatori si dimostrano abbastanza duttili da tollerare
questa "scorrettezza".
Inoltre, se si inserisce una `c', `C' oppure un asterisco `*' nella
prima colonna, tutto il testo sulla stessa riga viene considerato
commento (quindi non valgono le impostazioni per le colonne). Esiste
anche la possibilita' di inserire un punto esclamativo `!' che
indichera' di ignorare tutti i caratteri alla sua destra: risulta
utile per commentare una riga di codice, dove si scrive l'istruzione e
di seguito si aggiunge il commento.
Una label e' un'etichetta unica all'interno del programma (i
sottoprogrammi sono programmi a se, sotto questo punto di vista) che
identifica una linea di codice a cui si puo' fare riferimento tramite
l'istruzione di salto incondizionato `goto'.
Per chi si chiedesse come mai e' necessaria una formattazione cosi'
rigorosa, va detto che la sintassi e' infatti rimasta molto vincolata
al periodo delle schede perforate, la cui lunghezza di riga era
limitata e suddivisa nel modo appena descritto.
***Struttura di un programma Fortran
Potremmo schematizzare un generico programma Fortran in 3 sezioni:
***Intestazione
In questa sezione si dichiara cosa andremo a scrivere:
1. un programma principale: `program'
2. un sottoprogramma: `subroutine'
3. una funzione: `function'
(1)Una intestazione per un programma principale e' della forma:
program
Anche se e' opzionale come dichiarazione, e' sempre bene metterla in
modo da evitare possibili errori difficili poi da individuare; le
regole che deve soddisfare sono:
1. deve iniziare con una lettera
2. deve contenere solo lettere o cifre
3. non deve essere piu' lungo di 6 caratteri
(2)Una intestazione per un sottoprogramma ha questa forma:
subroutine ()
Nella lista di parametri sono presenti sia quelli di input (i
parametri necessari per l'esecuzione dell'algoritmo) sia quelli di
output (quelli in cui andranno messi i risultati).
Fra i parametri e' possibile mettere anche un nome di un'altra
subroutine o di una funzione, che dovra' essere dichiarata `external'
all'interno del sottoprogramma.
Inoltre una subroutine deve essere terminata dalle istruzioni
return
end
(3)Una intestazione per una funzione ha questa forma:
function ()
Il funzionamento e' molto simile a quanto visto per le subroutine, con
l'unica differenza che la lista dei parametri contiene solo quelli di
input, in quanto il valore di ritorno e' la funzione stessa.
All'interno del corpo della funzione deve quindi essere presente
un'assegnamento al nome della funzione, che costituisce il valore di
ritorno.
Anche `function' deve essere terminata con la coppia di istruzioni
return
end
***Informazioni generali su sottoprogrammi
E' utile sapere che ogni `parte' del programma e' a se stante: a
differenza del Pascal, per esempio, nel Fortran il programma
principale non contiene le subroutine che verranno utilizzate: ogni
programma e' indipendente, e ci si rifa ad esso tramite l'istruzione
external
che associa a una subroutine o una funzione che potra', a
questo punto, essere utilizzata nel codice corrente (nel caso sia una
funzione, e' necessario anche definire il tipo della funzione, che
viene fatto nello stesso modo in cui si dichiara una variabile).
Inoltre, Fortran considera i sottoprogrammi come separati dal corpo
del programma principale e quindi li compilera' anche in modo
indipendente.
Si noti, inoltre, che nel Fortran e' assente la ricorsione, quindi gli
algoritmi dovranno essere scritti tenendo presente questa mancanza.
Non esistono variabili globali, quindi lo scambio di dati tra
programma chiamante e programma chiamato puo' avvenire solo con i
parametri (o meglio, esiste anche l'istruzione Common, ma non la
vedremo). Inoltre, non esistono i parametri per valore.
Un'altra nota e' da farsi riguardo alla dichiarazione dei parametri:
essi devono essere dichiarati sia nel programma principale sia nel
sottoprogramma, ed in particolar modo per i vettori, che devo essere
dimensionati in entrambe le parti, ma _soltanto_ alla dichiarazione
all'interno del programma principale corrisponde una effettiva
allocazione di memoria.
***Dichiarative
In questa sezione sono presenti le dichiarazioni delle variabili,
delle funzioni esterne e delle costanti.
Fortran ha la caratteristica di considerare, in via normale, come
reali le variabili che iniziano con la lettera A..H e O..Z e come
intere le variabili che iniziano con le restanti lettere; e' comunque
possibile ridefinire questo comportamento tramite l'utilizzo della
direttiva
implicit (), (<...>)
indicando il tipo delle variabili che inizieranno con la lista di
lettere che segue (una serie di lettere consecutive si indica come
A-H, per esempio le lettere da A ad H comprese).
E' bene, pero', eliminare questa caratteristica, tramite la direttiva
Implicit None
che disattiva l'opzione dell'autotipo; questa dichiarazione va
indicata in ogni parte del programma (diciamo nella parte dichiarativa
del programma principale, dei sottoprogrammi e delle funzioni) e
quindi dichiarare _esplicitamente_ il tipo delle variabili che andremo
ad utilizzare.
L'uso di dichiarazioni implicite rende il programma meno leggibile e
consente di introdurre errori non individuabili dal compilatore
poiche' una variabile non dichiarata esplicitamente viene considerata
reale o intera sulla base della lettera iniziale; si consiglia,
quindi, di utilizzare sempre una dichiarazione esplicita delle
variabili, in modo da essere sicuri di quello che verra' eseguito.
***Variabili
Di seguito troviamo un elenco dei tipo di variabili maggiormente
utilizzati in fortran:
Integer con segno 4 byte
Real singola precisione 4 byte
Double precision doppia precisione 8 byte
Complex complessi a singola precisione
Double complex complessi a doppia precisione
Boolean valori booleani
Character*n stringa di n caratteri
Il nome di una variabile e' una sequenza al piu' di 31 caratteri che
possono essere lettere, numeri e caratteri speciali come `$' e `_' ma
non puo' iniziare con un numero; va inoltre notato che su alcune
versioni non sono riconosciuti nomi piu' lunghi di 6 caratteri.
Cerchiamo di esaminare piu' in attenzione questi tipi:
-Integer
sono i classici numeri interni degli altri linguaggi di
programmazione.
-Real
sono i numeri reali in singola precisione, che si possono indicare sia
in virgola mobile che in virgola fissa, ed in quest'ultima notazione
sono ammessi numeri senza cifre prima o dopo il punto decimale. E'
ammessa anche la lettera `D' al posto della lettera `E' nella parte
dell'esponente.
-Complessi
sono rappresentati come coppie di numeri reali (o interi), dove il
primo elemento rappresenta la parte reale, mentre la seconda
componente rappresenta la parte immaginaria del numero complesso.
***Costanti
per definire una costante si usa l'istruzione
Parameter(=)
che associa ad un nome simbolico un valore; il tipo della costante e'
definito se e' il nome di una variabile precedentemente
dichiarata, altimenti si usano le regole di dichiarazione implicita
delle variabili:
1 e' costante intera
1., 1E0 e' una costante reale
1D0 e' una costante reale in doppia precisione
Il nome di una costante ha associato un valore che non sara'
modificabile in futuro.
Una volta dichiarata una costante, e' possibile utilizzarla per la
definizione di altre costanti:
parameter(n=5)
parameter(n2=n*n)
ed n2 contiene il quadrato di n: nel nostro caso sara' 25, ma
cambiando il valore ad n, automaticamente cambiera' anche quello di
n2...
***Dichirazione di variabili
Cerchiamo ora di elencare alcune dichiarazioni di variabili per
mostrare come farlo all'interno della sezione di dichiarazione:
Integer i,j
dichiara due variabili `i' e `j' intere: quindi, per dichiarare una
variabile di un certo , si pone prima il tipo della/e
variabile/i e dopo il nome di questa/e in modo unico (non possono
esistere variabili, anche di tipo diverso, ma con nome uguale):
***Vettori e matrici
Vediamo la dichiarazione di vettori e matrici come avviene:
integer v(24)
dichiara il vettore `v' di 24 elementi interi, l'offset parte da 1
integer a(10,10)
dichiara la matrice `a' di interi con 10 righe e 10 colonne
integer v(n)
dichiare il vettore `v' di interi con un numero di elementi dipendenti
da un parametro definito in precedenza, `n'
integer vec(*)
Come detto l'allocazione della memoria e' statica, ma e' cmq possibile
definire un vettore di interi di dimensione non fissata, che contiene
gli elementi necessari; questa pratica e' _vivamente sconsigliata_,
soprattutto per i calcoli numerici.
Le dimensioni devono essere degli interi, ed e' anche possibile
specificare estremo inferiore e superiore di ogni dimensione del
vettore scrivendo due costanti intere separate da `:' (naturalmente
l'estremo inferiore deve essere numericamente minore dell'estremo
superiore).
Agli elementi del vettore si accede tramite il meccanismo dell'indice:
si fa riferimento ad un determinato elemento del vettore indicando il
nome del vettore seguito dalla specifica dell'indice racchiusa tra
parentesi tonde.
***Variabili in precisione multipla
Dal momento che Fortran e' particolarmente orientato ai calcoli
numerici, si consiglia di utilizzare le variabili in precisione
multipla (quando merita ed e' possibile) in quanto, a scapito di una
esecuzione piu' lenta, forniscono dei risultati nettamente piu'
precisi dei numeri in singola precisione. Sono disponibili, sia
variabili reali che intere definite in questo modo:
Integer*8
Real*8
e proprio il nome sta ad indicare l'utilizzo del doppio dello spazio
di memoria normalmente utilizzato per variabili in singola precisione.
Per ragioni di compatibilita', e possibile dichiarare variabili reali
in doppia prezione tramite le parole chiave `double precision' seguite
dal nome della variabile. Le costanti, invece, possono essere
dichiarate in due modi: il primo, senza esponente, ma le cifre
significative devo superare la precisione dei reali, altrimenti sono
considerate in singola precisione; il secondo, con esponente, mettendo
al posto della `E' che caratterizza le costanti in singola precisione
una `D'.
Le funzioni di libreria per i numeri reali in doppia precisione sono
le stesse che si trovano per i numeri in precisione singola,
eventualmente precedute da una `d'.
***Numeri complessi
Al contrario di molti altri linguaggio di programmazione, Fortran
consente di operare su numeri complessi in modo nativo. Per Fortran i
complessi sono rappresentati da una coppia di numeri reali: la parte
reale e la parte immaginaria; dal momento che sono disponibili sia
reali in singola che doppia precisione, cosi' avremo anche i numeri
complessi in singola (`complex') e doppia (`double complex')
precisione.
Le costanti sono definite in questo modo:
(, )
dove sia che sono costranti reali: una dichiarazione
come (1.0,2.0) e' corretta mentre NON lo e' (1,2).
Per ottenere un numero complesso da interi oppure utilizzando per
comporre un complesso risultati di alcune funzioni, si usa
l'istruzione `cmplx' che prende due argomenti e resituisce un
complesso.
Dal momento che il supporto ai numeri complessi e' `built-in' in
Fortran, tutte le operazioni di somma, sottrazione, etc. sono
disponibili anche per questo insieme di numeri; inoltre alcune
funzioni che possono risultare utili sono:
real(x) restituisce la parte reale di x
imag(x) restituisce la parte immaginaria di x
cabs(x) restituisce il modulo di x
e le varianti per i double complex.
Infine, un numero complesso NON puo' essere elevato ad esponenti
reali, ma solo a potenze intere.
Si consiglia vivamente, nel caso si debba scrivere del codice che
utilizza numeri complessi, di consultare dei testi che spieghino
l'aritmetica complessa, leggermente differente da quella ordinaria.
***L'istruzione DATA
Risulta spesso utile assegnare dei valori iniziali a delle variabili,
anche come test dell'algoritmo, senza poi dover rispettare il vincolo
di non-modificabilita' delle costanti; per fare questo, Fortran mette
a disposizione l'istruzione DATA che effettua l'assegnazione di valori
iniziali a variabili.
L'istruzione assume questa forma:
data //,.. //
ad esempio
data k/0/
data a,b,c/1,2,3/, z/(3.3)/
e' inoltre possibile assegnare a piu' variabili lo stesso valore
inziale, specificando la molteplicta' di detto valore:
data m,n,o,p,q/5*0/
Naturalmente, l'istruzione DATA deve seguire le istruzioni
dichiarative (infatti le variabili sono specificate senza il tipo, che
quindi deve essere dichiarato in precedenza) e solitamente precede le
istruzioni eseguibili.
***Codice eseguibile
La terza ed ultima parte di un programma Fortran e' quella in cui
viene effettivamente scritto il codice che dovra' essere poi
compilato. Le istruzioni sono simili a quelle presenti in ogni altro
linguaggio di programmazione e, naturalmente, l'ordine di esecuzione
delle istruzioni e' sequenziale.
***Assegnamento
=
dove si associa ad un identificativo di una variabile (il suo nome)
l'espressione alla destra dell'uguale; la variabile e l'espressione
devono essere dello stesso tipo; sono pero' concesse alcune eccezioni
che prevedono una conversione di tipo dell'espressione per adattarlo
alla variabile (se e' reale e e' intera si considera
solo la parte intera dell'espressione).
***Operazioni aritmetiche
Le operazioni sono quelle usuali:
+ somma
- sottrazione
* moltiplicazione
/ divisione
** elevamento a potenza (2**3 indica due alla terza)
sqrt radice quadrata
etc.
Le operazioni hanno la stessa precedenza che in aritmetica; e'
possibile raggruppare espressioni tra parentesi tonde in modo da
apportare la precedenza dei calcoli come meglio si crede.
***Operazioni di confronto
Sono operazioni leggermente differenti dall'usuale: in generale sono
della forma
A.op.B
dove `.op.' e' l'operatore di confronto ed `A' e `B' sono gli elementi
da confrontare.
Le operazioni di confronto sono:
.ge. maggiore uguale
.gt. maggiore stretto
.le. minore uguale
.lt. minore stretto
.eq. uguale
.ne. diverso
.not. NOT logico
.and. AND logico
.or. OR logico
e l'ordine degli operatori logici e' dal basso verso l'alto: il `not'
ha precedenza sugli altri, e l'`and' ha precedenza sull'`or'. Sono
inoltre disponibili due costanti logiche, true e false:
.true. TRUE logico
.false. FALSE logico
E' anche possibile eseguire delle operazioni durante i confronti:
(n+1).eq.(3*k)
e' un confronto valido.
***Particolarita' di espressioni ed operatori aritmetici
Come detto, le operazioni hanno la priorita' solita dell'aritmetica,
quindi l'elevamento a potenza precede moltiplicazione e divisione che,
a loro volta, precedono somma e sottrazione; inoltre, operazioni con
lo stesso ordine di priorita' vengono eseguite da sinistra verso
destra eccetto nel caso di elevamento a potenza.
Si noti che, in Fortran, non c'e' distinzione tra divisione fra interi
e divisione fra reali: quando gli operandi di "/" sono numeri interi,
il risultato sara' un intero.
L'esecuzione di espressioni con operatori eterogenei rispetta la
seguente tabella di priorita' (dall'alto verso il basso):
operatori aritmetici
operatori relazionali
operatori logici
Nel caso in cui compaiano termini non omogenei nelle espressioni,
Fortran esegue una conversione di tipo verso il piu' generico,
seguendo questo schema:
interi < reali < complessi
il quale vuole significare che se ci sono valori interi e reali, il
risultato sara' reale, mentre, ad esempio, complessi e reali danno un
risultato complesso. Le regole di conversione avvengono seguendo
l'ordine di esecuzione dell'espressione.
***Selezioni e cicli
Secondo il teorema di Boehm-Jacopini, tutto il codice e' esprimibile
tramite assegnamenti, selezioni e cicli: allora non possono certo
mancare neanche in Fortran quest'ultime due primitive di
programmazione.
***Selezione
E' il classico costrutto if..then..else:
if () then
...
...
elseif () then
...
...
else (un solo ramo else)
...
...
endif
Come evidenziato sopra, possono esistere piu' rami `elseif' ma un solo
ramo `else'. E' bene notare che le parentesi esterne ad
sono necessarie; naturalmente, se non sono necessari, i rami `elseif'
ed `else' possono essere omessi.
Se l'istruzione da eseguire nel caso sia verificata e'
una sola, possiamo usare un costrutto alternativo e piu' compatto:
if ()
esistono pero' delle limitazioni sull'istruzione da eseguire.
***Salti incondizionati
E' presente anche l'istruzione `goto', a volte indispensabile, ma se
ne _sconsiglia_ vivamente il suo utilizzo; esso e' tollerato solo per
uscire da cicli o selezioni, ma ESCLUSIVAMENTE se il salto da compiere
e' in avanti.
Si ricorda che le label sono definibili in ogni posizione delle prime
5 colonne.
***Cicli
In Fortran sono presenti i cicli iterativi (diciamo i `for') ed i
cicli condizionati (i classici `while'):
do i=n1,n2{,k}
...
...
enddo
dove n1 ed n2 rapprensentano l'intervallo dove l'indice del ciclo deve
variare, con la possibilita' di specificare anche il passo di
incremento k: nel caso non venga specificato, l'incremento e' posto
pari ad 1. Da notare che l'indice del ciclo assume il valore n2+k al
termine dell'iterazione e che il controllo di fine loop avviene prima
dell'esecuzione del blocco di istruzioni all'interno.
do while()
...
...
enddo
si tenga presente che l'istruzione `while' non e' disponibile su tutti
i compilatori.
Il primo tipo di ciclo e' nella forma che andrebbe sempre utilizzata,
ma e' concesso anche una formulazione equivalente nel caso in cui non
sia disponibile il `while' e che contiene salti al suo interno:
do