Parte A. Introduzione al FORTRAN. | Parte B. I principali comandi del FORTRAN. |
Parte C. La struttura dei programmi FORTRAN. |
1 Sistemi di numerazione | 7 Un esempio di programma | 13 Il programma principale e i sottoprogrammi |
2 La rappresentazione dei numeri nel Fortran |
8 La definizione delle variabili | 14 Variabili globali |
3 Le costanti | 9 Le istruzioni di scrittura e lettura | |
4 Le variabili |
10 L'istruzione DO | |
5 Assegnazione |
11 Decisioni | |
6 Espressioni aritmetiche |
12 L'istruzione GOTO e i loop "condizionali" |
L'unità elementare di informazione nei computer può assumere solo due valori, simbolicamente definiti come 0 ed 1. Questa unità è chiamata bit (da "binary digit"); 8 bits formano il byte. Tutti i dati presenti nella memoria del computer saranno rappresentati da un sistema numerico a base 2 (sistema binario). Qui non ci dilungheremo sull'uso dei sistemi di numerazione differenti da quello decimale ma ricordiamo solamente che un numero di m cifre x1x2...xm rappresentato nel sistema con base b equivale a
x1 bm-1 + x2 bm-2+ ... + xm b0 ,
quindi, per esempio, il numero 10011 in notazione decimale diventa 1 24 + 1 21 +1 20 = 19. Un altro sistema utile è quello esadecimale, cioè in base 16; Le cifre di questo sistema sono
0 1 2 3 4 5 6 7 8 9 A B C D E F.
Per esempio, FF in notazione esadecimale diventa 15 161 + 15 160 = 255 in notazione decimale.
2 La rappresentazione
dei numeri nel Fortran
Nel fortran i numeri sono considerati avere un numero fisso di bytes; il primo bit rappresenta il segno del numero:0 º +, mentre 1 º -.
2.1 numeri interi
I numeri interi sono l'esatta traslazione del numero binario nell'equivalente in base decimale. Possono essere di due tipi:
- tipo I2, lunghi due bytes: il primo bit è, come già stato detto, utilizzato per il segno, mentre gli altri 15 bits danno il valore del numero; quindi in questa maniera si possono avere numeri n compresi fra -(215-1) e 215-1,
|n|< 215-1=32 767 » 3 105;
- tipo I4, lunghi 4 bytes; in questo caso
|n|< 231-1=2 147 483 647 » 2 109.
In pratica, questo intervallo di valori finito con cui si deve lavorare può provocare dei problemi. Se per esempio, con un operazione si "esce" dall'intervallo consentito, il risultato finale sarà non corretto.
2.2 numeri reali
Per considerare numeri che possano avere virgola mobile ("floating point") e un intervallo di valori più esteso, si scompone il numero in una frazione compresa fra 0 ed 1 (mantissa), moltiplicata per una potenza di 10 elevata ad un certo esponente (in pratica, il computer usa 2 come base della potenza). Si possono avere 3 tipi di numeri reali:
- tipo R4 (anche chiamati a precisione singola, SINGLE PRECISION), lunghi 4 bytes. Il primo byte viene utilizzato per l'esponente, quindi avendo a disposizione 8 bits, si possono avere al massimo 28 = 256 diversi esponenti; di questi la metà viene utilizzata per gli esponenti positivi (0®127) e l'altra metà per gli esponenti negativi (-1® -128), per cui la massima e la minima grandezza dei numeri che si potrà rappresentare sono, rispettivamente, 2127»1038 e 2-128»10-39.
Per quanto riguarda la mantissa, si hanno a disposizione 23 bits (1 si usa per il segno), quindi 223-1= 8389607 numeri; dunque, i numeri a precisione singola possono avere al massimo 7 cifre decimali (in qualche caso 6). Per esempio,
1/3 ® 0.333 333 3 100 ,
2/3 ® 0.666 666 7 100 .
Ricapitolando ,
R4: precisione 7 cifre decimali, numero max: 1038, numero min: 10-39.
- tipo R8 (anche chiamati a precisione doppia, DOUBLE PRECISION), lunghi 8 bytes. Il computer utilizza 11 bits per l'esponente, quindi si hanno a disposizione 211 = 2048 valori, di cui circa la metà viene utilizzata per gli esponenti positivi ( 21024»10308) e l?altra metà per quelli negativi (2-1024»10-308). Per la mantissa, si hanno 53 bits che permettono di avere 15 cifre significative. Ricapitolando:
R8: precisione 15 cifre decimali, numero max: 10308, numero min: 10-308.
Da notare che questi dati possono essere diversi da computer a computer.
- tipo R16 (anche chiamati a precisione quadrupla, QUADRUPLE PRECISION), lunghi 16 bytes. Tutti i nuovi bits si usano per la mantissa, quindi, si ha:
R16: precisione 32 cifre decimali, numero max: 10308, numero min: 10-308.
Questa rappresentazione ha diverse conseguenze nei calcoli; i più comuni inconvenienti sono:
errore di troncamento:
si creano in quanto il computer lavora con numeri decimali a precisione
finita, per cui un'operazione eseguita con numeri troncati può dare
un risultato diverso da quello "giusto".
Esempio 1:
esatto: 4/3 + 1/3 = 5/3 Þ0.166 666 7 101
con numeri R4: 0.133
333 3 101 + 0.333 333 3 100
= 0.166 666 6 101
Esempio 2:
esatto: 0.4 + .4 10-7 + .4 10-7 =
0.400 000 0 + 0.000 000 04 + 0.000 000 04 =
0.400 000 08 Þ 0.400 000 1
con numeri R4:
(0.400 000 0 + 0.000 000 04) + 0.000 000 04 =
(0.400 000 04 Þ 0.400 000 0) + 0.000 000 04 =
0.400 000 0 + 0.000 000 04 =
0.400 000 04 Þ 0.400 000 0 .
é chiaro che dopo un grande numero di operazioni l'errore di arrotondamento crescerà e il numero di cifre significative si ridurrà pericolosamente. In questo caso conviene usare numeri con un maggior numero di decimali, cioè usare la rappresentazione R8 o R16. Da notare che il computer in genere non avverte che si stanno perdendo cifre significative.
overflow: questo termine sta a significare che in una operazione si è prodotto un numero che è più grande (in valore assoluto) del limite consentito, quindi maggiore di 1038 (10308) per i numeri in precisione singola (doppia e quadrupla).
underflow: questo termine sta a significare che in una operazione si è prodotto un numero che è più piccolo (in valore assoluto) del limite consentito, quindi minore di 10-39 (10-308) per i numeri in precisione singola (doppia e quadrupla). Generalmente, viene assunto come risultato il valore zero.
In questi due ultimi casi, ma questo dipende dal particolare tipo di computer, durante il "run" del programma viene segnalato l'avvenuto overflow ed underflow. In alcuni computers (es. IBM) questa segnalazione non è automatica, ma si ottiene dopo avere "caricato" alcuni sottoprogrammi specifici.
Nel fortran è possibile usare anche i numeri complessi in maniera completamente automatica. Per esempio, quando il programma elabora il prodotto di due numeri complessi z1 e z2 trova come risultato il numero complesso la cui parte reale è data dal prodotto delle parti reali meno il prodotto delle parti immaginarie di z1 e z2, ecc. Si possono avere numeri complessi con 8, 16 e 32 bytes, ognuno composto dall'insieme di due numeri reali con 4, 8 e 16 bytes, rispettivamente. Naturalmente, questi due numeri reali rappresentano la parte reale ed immaginaria del numero complesso. Parleremo dunque di numeri complessi di tipo C8 a precisione semplice, C16 a precisione doppia e C32 a precisione quadrupla.
Qualunque numero che compare nelle operazioni di un programma viene detto costante numerica. Nelle costanti intere non compaiono ne' il punto ne' la virgola. Esempi di costanti intere: 1 , 12 , -100 .
In una costante reale R4 deve comparire il punto decimale (non la virgola); si può usare anche la notazione esponenziale, tipo 0.xxx E± yy dove E± yy sta per 10 elevato a ± yy, con il segno - obbligatorio ed il segno + facoltativo. Il numero yy deve essere intero e può essere ad una cifra. Esempi di costanti reali R4: 1. , 1.2 , -10.E-3 .
Per una costante in doppia (quadrupla) precisione si deve usare la notazione esponenziale sostituendo pero' al posto della lettera E, la lettera D (Q). Esempi di costanti reali R8: 1.D0 , 1.2D0 , -10.D-3 .
Le costanti complesse si indicano invece mettendo la parte reale e la parte immaginaria fra due parentesi tonde, separate da un virgola. La parte reale ed immaginaria, essendo numeri reali, seguono la stessa notazione dei numeri reali R4, R8 ecc.
Esempi di costanti complesse C8: (1.,3.) , (1.E-2,4.E3) .
Esempi di costanti complesse C16: (1.D0,3.D0) , (1.D-2,4.D3) .
Una variabile specifica la locazione di memoria dove viene immagazzinato un numero. Una variabile è indicata da un insieme di lettere e numeri; unica limitazione è che il primo carattere deve essere necessariamente una lettera.
Esempi validi di variabili: VAR, A12, B1B.
Esempi non validi di variabili: 1VAR, 0A12, ]1B, >AA.
Inizialmente il programma usa la convenzione:
le variabili con lettera compresa fra I,J,K,L,M,N sono intere di tipo I4;
tutte le variabili che iniziano con le altre lettere (A-H,O-Z) sono intese essere di reali di tipo R4 (reali a precisione semplice).
Per dare un dato valore ad una variabile si usa il comando si assegnazione "=". Vediamo alcuni esempi:
1) I variabile intera (I2 o I4): I=4
2) A variabile reale R4 : A=100. oppure A=1.E2 ecc.
3) B variabile reale R8: B=1.D2
4) C variabile reale R16: C=1.Q2
5) D variabile complessa C8: D=(20.,3.) oppure D=(2.E1,30.E-1) ecc.
Il comando "=" sta a significare di prendere il valore dell'espressione a destra del segno "=" e immagazzinarlo nella locazione di memoria contrassegnata dal nome della variabile a sinistra. Naturalmente, se la variabile è di un certo tipo di lunghezza "n" bytes, nella memoria sarà giusto assegnato uno spazio di "n" bytes; è quindi necessario definire bene (e ricordarsi) di che tipo è la variabile.
Se si assegna un numero di un certo tipo ad una variabile di un altro tipo, il computer si arrangia come può; ecco alcuni esempi:
1) I variabile intera:
3) B variabile reale R8:
4) C variabile complessa C8 C=2.
: C assume il valore (2.,0.)
L'errore più grave viene commesso nel caso 1) in quanto si perde completamente la parte decimale. Nel caso 3) assegnare ad una variabile in doppia precisione un numero in precisione semplice (si è usato la notazione con il punto decimale), porta ad un valore dove le cifre decimali dall'ottava alla sedicesima non sono definite e possono assumere valori arbitrari (rappresentati dalle "x").
Le operazioni di somma, sottrazione,
prodotto, divisione ed elevamento a potenza si ottengono semplicemente
usando i seguenti operatori:
+ somma | / divisione |
- sottrazione | ** elevamento a potenza |
* prodotto |
Questi operatori devono stare necessariamente tra due valori numerici (costanti o variabili). I simboli + e - possono essere usati anche davanti ad un solo numero, per esempio -G rappresenta il numero immagazzinato in G cambiato di segno. Vediamo alcuni esempi:
A=A+1. Þ prendi il valore di A, sommaci uno e rimettilo in A;
A=B*C Þ prendi i valore di B e C, moltiplicali e mettili in A;
A=B**2 Þ prendi il valore di B, elevalo alla potenza 2 e mettilo in A.
Si
possono usare espressioni più complicate, dove compaiono più
operazioni aritmetiche nella stessa linea; in questo caso diventa importante
la priorità delle operazioni, specificata dalla seguente tabella:
operazione | priorità |
** | alta |
* / | media |
+ - | bassa |
Esempi:
A=B+C*DÞA=B+(C D)
A=B/C*DÞA=(B D)/C
A=B*C**DÞA=B CD
A=B**C*DÞA=BC D
Nelle espressioni aritmetiche si possono introdurre le parentesi, in modo da eseguire le operazioni nell' ordine desiderato: come regola, ogni espressione racchiusa da parentesi viene eseguita prima di tutte le altre.
Esempi:
A=B**(C+D)ÞA= BC+D
A=B/(C+D)ÞA= B/(C+D)
A=(B+C*(D+E))*G ÞA= B G + C D E + C E G
Valgono le seguenti regole:
Â1) operazioni con la stessa priorità vengono eseguite seguendo l'ordine da sinistra a destra.
Â2) il risultato di una operazione risulta essere dello stesso tipo (intero, reale, ecc.) dei due operandi.
6.1 Nota sulla divisione fra interi
Gli operatori +,-,* e ** possono essere usati senza nessuna difficoltà anche tra interi, mentre l'operatore / (divisione) comporta alcuni problemi. Per esempio, cosa succede nel calcolo di 4/M se M ha il valore 3? L'espressione si riduce a 4/3= 1.333..., ma siccome per la regola Â2 4/M deve essere intero, il risultato finale sarà 1. Così se M=5, il risultato finale è 0.
Con i numeri interi è quindi importante l'ordine delle operazioni: per esempio, se M=5, otteniamo nei vari casi
I=4*M/5+1Þ 5 ,
I=4/5*M+1Þ 1 (qualunque sia il valore di M).
6.2 Nota sulla assegnazione di variabili intere
Abbiamo già detto che, nell'assegnare ad una variabile intera un valore reale, si tronca (e non si arrotonda) la parte decimale. Questo fatto può dare risultati non corretti; per esempio, consideriamo questa espressione:
N=P*Q/R , (per convenzione N è intera e P,Q,R reali) .
Cosa succede se P=2. , Q=3. , e R=6. ? L?espressione si riduce a 6./6. e quindi N dovrebbe assumere il valore 1. Eppure, il calcolo potrebbe portare (per errori di troncamento) ad il valore di 0.9999999, che è 1 in pratica, ma condurrebbe ad attribuire ad N il valore 0. Per evitare questo tipo di incertezze è utile modificare l'espressione in
N=P*Q/R +0.001
cioè aggiungere una quantità piccola prima del troncamento.
6.3 Nota sulle operazioni contenenti operandi di tipo differente
Se in una espressione compaiono costanti e/o variabili di tipo differente (interi, reali, complessi), valgono le seguenti regole:
Â3) interi e reali: il risultato di una operazione è intero se tutti gli operandi sono interi ed è reale se almeno uno degli operandi è reale;
Â4) reali R4 e reali R8: il risultato di una operazione è reale R4 se tutti gli operandi sono reali R4 ed è reale R8 se almeno uno degli operandi è reale R8;
Â5) reali e complessi: il risultato di una operazione è reale se tutti gli operandi sono reali ed è complesso se almeno uno degli operandi è complesso;
per esempio, nell'operazione A*B, con A reale R4 e B reale R8, prima si trasforma A in variabile R8 (si aggiungono cifre decimali) e poi si esegue l'operazione che darà come risultato un numero reale R8. Non è difficile trovare ulteriori regole per operazioni fra interi e complessi, reali R4 e reali R16, complessi C8 e complessi C16 ecc. ecc.
Esempio 1: M=I+4*J/K-A con I=3, J=5 e K=6 interi; A=4.1 reale.
L' espressione è calcolata nei seguenti passi:
1) calcolo 4*JÞ20 (è operazione di priorità alta più a sinistra);
2) calcolo 20/KÞ3 (troncamento cifre decimali);
3) I+3Þ6 ;
4) 6-AÞ1.9 (il risultato è reale);
7) M assume il valore finale di 1.
Esempio 2: M=I**A**J con I=2, J=4 interi e A=0.5 reale.
1) calcolo 2**0.5Þ1.414213 (reale per la regola Â3);
2) calcolo 1.414213**4Þ3.999994 (ancora reale);
3) M assume il valore finale 3, anche se "normalmente" doveva venire 4.
Da notare in questo esempio che se la prima operazione fosse stata A**J il risultato finale sarebbe stato 1.
Esercizi
(sia X=1., Y=6.,
Z=3.).
Calcolare: | soluzione | |
1 | I=45/4*4 | 44 |
2 | A=4-6+18.0/3/3 | 0.0 |
3 | I=(X+Y/Z)**2 | 8 o 9 |
4 | I=((X+Y)/Z)**2 | 5 |
5 | I=(X+Y)/Z**2 | 0 |
6 | I=X+(Y/Z)**2 | 4 o 5 |
Le istruzioni nel Fortran devono essere scritte con un formato preciso, come può essere visto nell? esempio seguente, dove, per comodità, è riportata anche la numerazione delle righe e delle colonne:
colonne:
1 2
3 4
1234567890123456789012345678901234567890.....
PROGRAM PROVA
C
programma per calcolare il peso di un oggetto
REAL MASSA
DATA GRAV/9.8/
10 WRITE(6,*) ' DAI LA MASSA IN
KILOGRAMMI '
READ(5,*) MASSA
PESO=MASSA*GRAV
WRITE(6,*) ' IL PESO
VIENE= ',PESO,' NEWTON '
WRITE(6,*) ' 1 PER
CONTINUARE '
READ(5,*) IC
IF(IC.EQ.1) GOTO 10
STOP
END
La lunghezza di ogni riga (o "record") può essere al massimo di 80 caratteri (o colonne), ma è vietato scrivere nelle ultime 8 posizioni (colonne 73-80) in quanto venivano usate per numerare le schede. Inoltre, le prime sei colonne servono per uso speciale e quindi le istruzioni FORTRAN devono sempre partire dalla colonna 7. Alcuni degli usi speciali sono mostrati nel programma di prova: se nella prima colonna compare il segno C o * (asterisco) allora l'intera linea è considerata come di "commento" ed è ignorata dal computer; un numero che compare nelle colonne 1-5 serve per "etichettare" la particolare riga e si utilizza come riferimento per comandi successivi (così l'etichetta 10 nella riga 5 è usata dal comando GOTO della riga 11). Queste etichette posso stare nell'intervallo da 1 a 99999. Importante: non usare la colonna 6! Infatti, se la colonna 6 di una linea contiene un qualsiasi carattere (lettera o numero), allora i comandi di quella linea sono considerati come la continuazione dei comandi della riga precedente; si possono aggiungere fino a 19 continuazioni seguenti una prima linea. Da notare infine che non è importante se i comandi sono scritti in maiuscolo o minuscolo.
Esaminiamo ora brevemente il programma dell'esempio 7.1. La prima riga contiene il nome del programma definito con il comando PROGRAM (questa istruzione si può anche omettere), poi c'è una riga di commento per spiegare a cosa serve il programma. Quindi, con l'istruzione REAL si definisce reale la variabile MASSA, poi si assegna il valore di g (accelerazione della gravità) memorizzandolo nella variabile GRAV. Il programma a questo punto si arresta e aspetta di leggere (READ) al terminale la massa dell'oggetto che viene poi memorizzata nella variabile MASSA; si calcola PESO=MASSA*GRAV; infine si scrive il valore della variabile PESO. Ora il programma chiede se continuare il calcolo per altri oggetti: se si assegna alla variabile IC il valore 1 il programma ritorna (GOTO) alla lettura del nuovo valore di MASSA; altrimenti l'esecuzione finisce (STOP).
In questo programma si trova un primo esempio di una costante di tipo "carattere" o stringa: è la sequenza di caratteri racchiusa da due apostrofi consecutivi nell'istruzione WRITE; il computer tratta le stringhe semplicemente come un insieme di caratteri e non come variabili numeriche. Quindi, l'istruzione della riga 8 produrrà sullo schermo del terminale una riga del tipo
IL PESO VIENE = 0.9800000E+1 NEWTON
avendo assegnato alla variabile MASSA il valore 1.
8 La definizione delle variabili
Come abbiamo visto nel paragrafo precedente, all' inizio di un programma FORTRAN, di solito, ci sono alcune istruzioni che servono per "personalizzare" il programma. Più precisamente, se vogliamo usare una data variabile nel programma, all'inizio dobbiamo specificare:
- il tipo della variabile (se lo vogliamo diverso da quello "standard");
- se a quella particolare variabile è associato un solo valore numerico oppure un vettore o una matrice di numeri;
- il valore che la variabile deve assumere all'inizio.
Qui di seguito mostreremo questi punti uno a uno.
8.1 La dichiarazione di tipo
Le prime istruzioniche devono apparire nel programma (subito dopo l'istruzione iniziale PROGRAM) riguardano la dichiarazione del tipo delle variabili che si vogliono usare nel seguito. Nel FORTRAN esiste la possibiltà di fare una dichiarazione "esplicita" che associa ad ogni particolare variabile il suo tipo, come, per esempio,
REAL*8 VAR1,VAR2,VAR3
INTEGER*4 VAR4,VAR5
COMPLEX*8 VAR6
Con questa serie di istruzioni si specifica che le variabili VAR1,VAR2,VAR3siano di tipo R8 (reali a doppia precisione), VAR4,VAR5intere I4 e VAR6 una variabile complessa C8. Il numero che moltiplica le istruzioni REAL, INTEGER e COMPLEX corrisponde esattamente al numero di bytes che deve essere associato alla variabile; per esempio per definire variabili R4, R8 o R16, il numero che moltiplica l'istruzione REALdeve essere 4, 8 o 16, rispettivamente. Inoltre, nei casi "standard" REAL*4,INTEGER*4,COMPLEX*8si può omettere il numero e scrivere semplicementeREAL,INTEGER,COMPLEX,rispettivamente. Da notare ancora che si può usare il comando DOUBLE PRECISIONal posto del comando REAL*8, quindi le istruzioni
REAL*8 VAR1,VAR2
e
DOUBLE PRECISION VAR1,VAR2
sono completamente equivalenti.
Un altro metodo per dichiarare il tipo delle variabili viene fatto con una dichiarazione "implicita", cioè che associa il tipo della variabile alla lettera iniziale del suo nome; per esempio, per definire complesse C8 tutte le variabili che iniziano per C,D,E si scrive
IMPLICIT COMPLEX*8 (C,D,E)
o equivalentemente,
IMPLICIT COMPLEX*8 (C-E)
Una istruzione molto utile, per definire che tutte le variabili che iniziano con una lettera che non sia I,J,K,L,M,N (le lettere riservate agli interi) siano reali a doppia precisione è
IMPLICIT REAL*8 (A-H,O-Z)
Ricordiamo che, all'inizio,
il programma assume che le variabili che iniziano per I,J,K,L,M,N sono
intere I4 e tutte le altre R4 (reali a singola precisione), e quindi, le
istruzioni REAL,INTEGER,COMPLEXsono
da usare solo se si vuole modificare questa convenzione iniziale. Nell'
esempio 7.1, si è usata l' istruzione REALper
definire la variabile MASSA come
reale a precisione singola, altrimenti MASSA
sarebbe
stata una variabile intera.
8.2 Vettori e Matrici
Ad una variabile si può associare non solo un singolo valore numerico ma tutta una serie di valori, per esempio un "vettore" o una "matrice" di numeri. Gli elementi di un vettore A in FORTRAN sono individuati dal nome della variabile-vettore A seguita da un numero intero (indice) racchiuso tra parentesi: A(1), A(2),.... Nello stesso modo, un elemento di una variabile-matrice B sono scritti in questa maniera B(1,1), B(1,2), . . . Naturalmente si possono definire matrici a tre e più dimensioni, come C(1,1,1) ecc.
Quando una variabile viene definita per la prima volta, il computer deve conoscere quanto spazio riservargli nella memoria. Se la variabile è di tipo R4 e non è un vettore od una matrice, lo spazio riservato sarà di 4 bytes. Quando la variabile contiene un vettore o una matrice di numeri bisogna far conoscere al computer del numero degli elementi di cui sarà composta, in modo che possa assegnare tutta una serie di bytes contigui. Per esempio, se A è un vettore reale R4 di N elementi, lo spazio riservato ad A nella memoria dovrà essere di 4N bytes. Questa dichiarazione di "dimensione" si può fare usando le stesse istruzioni REAL,INTEGER,COMPLEX aggiungendo (tra parentesi) al nome della variabile il minimo ed il massimo valore che potrà assumere l'indice, separate da un segno ":" (due punti); per esempio, le istruzioni
REAL*8 VAR1(0:10),VAR2(100)
COMPLEX VAR3(10,0:2)
specificano che la variabile VAR1 sia un vettore composto da 11 numeri (in doppia precisione) il cui indice varia fra 0 e 10; VAR2invece sia un vettore composto da 100 numeri il cui indice varia fra 1 e 100 (se il limite inferiore è omesso si assume che sia 1); infine VAR3 sia una matrice di 300 numeri complessi, con il primo indice che varia tra 1 e 10, mentre il secondo varia tra 0 e 2.
C'è un altro modo per dichiarare vettori/matrici, usando l'istruzione DIMENSION, che però specifica solo il numero di elementi associati a delle variabili ma non il loro tipo; quindi, per definire gli stessi vettori come nell'esempio precedente si potrebbero usare le seguenti istruzioni
REAL*8 VAR1,VAR2
COMPLEX VAR3
DIMENSION VAR1(0:10),VAR2(100),VAR3(10,0:2)
8.3 La definizione dei valori iniziali
Nella maggior parte dei computers il valore iniziale di tutte le variabili è fissato essere zero; nei programmi FORTRAN è possibile dare delle istruzioni in modo che, quando parte il programma, certe variabili siano inizializzate con valori dati. Questo è effettuato tramite l'istruzione DATA, che prende la forma
DATAlista variabili/lista valori/,lista variabili/lista valori/,...
per esempio, per assegnare alla variabili reali A e B i valori iniziali di 1.0 e 2.0,e alla variabile intera I il valore 1, si possono usare equivalentemente una delle due istruzioni seguenti
DATA A,B,I/1.0,2.0,1/
o
DATA A/1.0/,B/2.0/,I/1/
Se a più variabili si deve assegnare lo stesso valore iniziale, si può usare questa scorciatoia:
DATA A,B,C/3*1.0/
che equivale a
DATA A,B,C/1.0,1.0,1.0/
Si può usare l'istruzione DATA anche per inizializzare i valori di un vettore o di una matrice, come per esempio
DIMENSION A(3),B(10,10)
DATA A/1.0,2.0,3.0/
DATA B/100*1.0/
Da notare che l'assegnazione del valore alla variabile nell'istruzione DATAavviene quando inizia l'esecuzione del programma e non dipende dalla particolare posizione in cui è situata l'istruzione stessa (può anche essere a metà del programma). Naturalmente, l'istruzione DATA deve trovarsi dopo le istruzioni di definizione e dimensione delle variabili, cioè il computer deve già sapere di che tipo è la variabile e se è un vettore, una matrice, ecc.
Una situazione che può dare adito ad errori è l'assegnazione di valori ad una matrice a due dimensioni. In questo caso basta ricordarsi che i valori devono essere scritti colonna per colonna. Per esempio, le istruzioni
DIMENSION B(2,2)
DATA B/1.0,2.0,11.0,20.0/
portano ad avere B(1,1)=1.0 , B(2,1)=2.0 , B(1,2)=11.0,ecc.
Con l'istruzione DATAsi intende assegnare particolari valori iniziali alle variabili, che nel corso del programma potranno assumere altri valori. Se si vuole dare ad una variabile un particolare valore (come per esempio assegnare alla variabile PIil valore di pi greco = 3.141 592 653 ...) che rimarrà sempre lo stesso, si può usare l'istruzione PARAMETERche prende la forma
PARAMETER(variabile=valore,variabile=valore,...)
Perciò il modo migliore di dare il valore a PIè
PARAMETER (PI=3.1415926536)
Con l'istruzione PARAMETERsi possono usare anche espressioni aritmetiche, per esempio
PARAMETER (T=1.0/3.0,T49=4.*T/3.0)
da notare che l'assegnazione avviene con le regole usuali (e così pure se ci sono operazioni tra numeri di tipo diverso, il computer seguirà le regole mostrate nel paragrafo 6). Nell' esempio sopra mostrato si nota anche che è possibile usare variabili nell'espressioni che compaiono nel PARAMETER, basta che siano già definite attraverso un PARAMETERprecedente.
L'istruzione PARAMETERdeve essere situata sempre dopo che il tipo della variabile sia stato definito. In un programma si possono inserire un qualsiasi numero di istruzioni DATAe PARAMETER.
8.4 Posizione delle istruzioni sopra elencate
Ricapitolando, il tipico ordine con cui appaiono queste istruzioni di inizializzazione e definizione di variabili è il seguente:
PROGRAM ....
REAL , INTEGER , COMPLEX ...
DIMENSION ...
DATA , PARAMETER ....
9 Le istruzioni di scrittura e lettura
Durante l'esecuzione di un programma è importante "comunicare" con il computer; come si è visto nell'esempio 7.1, all'inizio del programma ci saranno alcune istruzioni di lettura (READ) per assegnare i valori iniziali di alcune variabili, e alla fine sarà necessario fare scrivere (WRITEe PRINT) dal computer i risultati. Per esempio, per eseguire l'istruzione WRITEil computer ha bisogno di conoscere i) dove prendere i valori da scrivere (cioè, il nome delle variabili che contengono i numeri che vogliamo conoscere) ii) dove scriverli (sul terminale, sul disco magnetico, ecc.) e, infine, iii) con che formato vogliamo che i dati siano scritti. Questo ultimo punto è molto importante, in quanto se, per esempio, vogliamo scrivere una matrice di numeri ci piacerebbe scriverla sotto forma di tabella bidimensionale, ecc. Il FORTRAN non si occupa di queste cose, quindi si rende necessario usare una istruzione che si chiama FORMAT dove si può specificare come vogliamo che appaiano i dati scritti.
9.1 Le istruzioni WRITE e PRINT
La forma più generale dell'istruzione WRITE è la seguente
WRITE(UNIT=u,FMT=f,ERR=e) lista di variabili
Il numero intero u ha il compito di specificare dove vogliamo che il computer scriva i numeri (UNIT sta per unità di uscita = output); in pratica ci sono due posti dove far scrivere, sul terminale (UNIT=6) e sul disco magnetico; in questo ultimo caso i dati saranno posti nel file "fort.u", a meno che, con l'istruzione OPEN (si veda la sezione 9.4), non si definisca espressamente il nome di uno specifico file. Con FMT=f, dove "f" è un numero intero, si istruisce il computer di scrivere i dati con il formato specificato dall?istruzione FORMATsituata nella riga la cui etichetta è appunto il numero "f". Da notare che si può omettere sia "UNIT=" che "FMT=", quindi si può scrivere semplicemente WRITE(u,f,...), ricordando però che il numero che si riferisce all'unità di uscita deve precedere l'etichetta per il FORMAT. Se "f" è uguale a un "*" (asterisco), allora il computer capisce che si vuole usare un formato "libero", cioè quello predefinito nel FORTRAN (in questo caso, i valori delle variabili verranno stampati in una sola riga, con 7 decimali se sono variabili R4, 15 se sono R8, ecc.). Chiaramente in questo caso non è necessaria l'istruzione FORMAT.
La rimanente specifica (ERR=e) è opzionale e serve per controllare che il processo di scrittura abbia avuto successo. Se questa specifica non è presente e si verifica una condizione di errore (come, per esempio, quando si tenta di scrivere in un file e il disco è pieno) il computer fermerà l'esecuzione. Se invece si è specificato ERR=e, dove "e" è un altro numero di etichetta, quando si verifica una condizione di errore durante la scrittura il computer continuerà l'esecuzione del programma dalla riga etichettata con "e".
L'istruzione PRINT è una versione semplificata di WRITE, ed il suo formato è
PRINT f,lista di variabili .
dove "f" ha lo stesso significato descritto precedentemente di etichetta per l'istruzione FORMAT.
9.2 L'istruzione READ
Il formato dell'istruzione READè simile a quello di WRITE, infatti si scrive
READ(UNIT=u,FMT=f,ERR=e,END=d)lista di variabili .'
Il significato di "u,f" è lo stesso e anche qui si può omettere di scrivere sia "UNIT=" che "FMT="; in questo caso la lettura avviene dal terminale se "u=5", e dal file "fort.u", se "u" è un altro numero (¹6, comunque). Le specifiche ERR=eeEND=dsono opzionali; anche ora la specifica ERR=e ha la funzione di far continuare l'esecuzione dalla riga etichettata con "e" se avviene un errore in lettura. Nella istruzione READc'è anche un' altra possibilità di errore, che capita quando, leggendo nel disco magnetico, si arriva alla fine del file di lettura; se si prova a leggere altri dati, il computer, non sapendo dove prenderli, sospenderà l'esecuzione. Se invece si specifica END=d il computer continuerà l'esecuzione dalla riga etichettata con "d".
9.3 L'istruzione FORMAT
Nelle istruzioni sopra descritte, la specifica del formato con cui i dati saranno presentati viene fatta con l?istruzione FMT=f, dove "f" era una etichetta di riga che si riferisce all'istruzione FORMAT. Questa si presenta nella forma seguente
f FORMAT(df1,df2,....,dfn)
dove "df1,df2,....,dfn" sono descrittori di formato. Nella seguente tabella ci sono i descrittori di formato usati per l?istruzione READ.
Tabella
9.1 descrittori di formato per l?istruzione READ:
Iw | leggi i successivi "w" caratteri come un intero |
Fw.d | leggi i successivi "w" caratteri come un reale R4 con "d"decimali (se il punto decimale non è presente) |
Ew.d | stesso significato; |
Dw.d | stesso significato ma per reali R8; |
nX | ignora i successivi "n"caratteri. |
Vediamo alcuni esempi, in ognuno dei quali si leggeranno alcuni numeri dal file "fort.1", che supponiamo contenga le seguenti due righe:
123456789 123.1234
0.123456789
Il computer inizia a leggere dalla prima riga e dalla prima colonna (a sinistra) del file . Inoltre, come al solito, le variabili I,J sono intese essere intere, mentre le variabili A,Breali R4.
Esempio 9.1. Le istruzioni,
READ(UNIT=1,FMT=100)
I,J
100 FORMAT(I3,I4)
READ(1,101) A,B
101 FORMAT(F4.1,F4.2)
hanno come risultato di far assumere alla variabile Iil valore 123, alla variabile Jil valore 4567, ad Ail valore 0.12 ed a Bil valore 34.56 . Infatti, il FORMAT alla riga etichettata con 100, specifica che la prima variabile è intera e va presa nei primi 3 caratteri; dopo avere letto variabile I, il punto di lettura del computer è posizionato sulla quarta colonna della prima riga e quindi la seconda variabile è letta nei successivi 4 caratteri. A questo punto il FORMAT 100 è completato e il punto di lettura passerà alla riga successiva (tutti i caratteri che rimangono nella prima riga saranno ignorati). Ora il computer legge il valore della variabile A usando il FORMAT 101; A deve essere di 4 caratteri quindi viene 0.12 (il punto è considerato un carattere); inoltre c?è il punto nel numero letto e quindi non si preoccupa di quanto vale il numero "d" (vedi tabella 9.1). Ora il punto di lettura è il quinto carattere della riga due; il numero successivo deve essere di 4 caratteri, perciò viene letto 3456; non avendo punto decimale diventa importante il valore di "d" che è 2, quindi "B=34.56".
Esempio 9.2 Le istruzioni,
READ(UNIT=1,FMT=100)
I,J,A,B
100 FORMAT(2I6,2F3.3)
danno come risultato:
I=123456
J=78912
A=3.1
B=0234
Nel leggere J è stato trovato uno spazio vuoto che è stato ignorato (789 12 ®78912); i successivi tre caratteri (3.1) sono stati assegnati ad A, e i tre successivi (234) a B; qui non c'è la virgola e quindi B=0.234 . Da notare che 2I6 equivale a I6,I6. Così nI6equivale in generale a I6,I6,I6,... ripetuto n volte; lo stesso vale per gli altri descrittori.
Nel caso di lettura con formato libero, per esempio READ(1,*)I,J,A,B , la lettura avviene in maniera più semplice, in quanto il computer considera i numeri come separati da spazi bianchi, senza contare il numero dei caratteri. Per esempio, per dare alle variabili I,J,A,Bil valore assegnato nell'esempio 9.2, si poteva semplicemente scrivere nel file "fort.1"
123456 78912 3.1 0.234
Ora vediamo i descrittori per l'istruzione WRITE.
Iw | scrivi un intero nei successivi "w" caratteri |
Fw.d | scrivi un reale R4 nei successivi "w"caratteri con "d" decimali |
Ew.d | stesso significato di Fw.d ma con notazione esponenziale |
Dw.d | stesso significato di Ew.d ma per reali R8 |
nX | scrivi i successivi "n" caratteri come "spazi bianchi" |
'ab...z' | scrivi la stringa di caratteri ab...z |
Vediamo alcuni esempi:
I=23
J=-12
A=3.14159
B=13.1
C=20.23
WRITE(6,100) I,J,A,B
100 FORMAT(2I5,2F10.3,E10.3)
producono la seguente linea di uscita (sul terminale poichè UNIT=6):
¬¬¬23¬¬-12¬¬¬¬¬3.142¬¬¬¬13.100¬¬.202E+02
dove il simbolo "¬"è stato usato per mostrare gli "spazi bianchi". Vediamo qui che i numeri interi sono stati scritti usando 5 caratteri (descrittore I5), mentre per i tre numeri reali sono stati usati 10 caratteri, con tre cifre decimali. Da notare che i numeri conosciuti con più cifre decimali vengono arrotondati delle cifre in eccesso; se il numero di cifre decimali conosciuto è minore, vengono aggiunti degli zero. L'ultimo numero è stato scritto con notazione esponenziale, vale a dire mantissa + esponente.
Nella scrittura è necessario assicurarsi di avere abbastanza "spazio" per potere scrivere tutte le cifre dei numeri, se no il computer genera una sequenza di "*". Nell' esempio 9.3, se Ivalesse 100000 o -33333, 5 caratteri non sarebbero più sufficienti; lo stesso se il numero reale Adiventasse (in valore assoluto) molto grande, per esempio A=3333333.14159. Con il descrittore "Fw.d"bisogna ricordarsi che si possono scrivere al massimo numeri con w-d-2 cifre prima del punto decimale (bisogna tenere conto della possibilità del segno negativo). Con il descrittore "Ew.d" non ci sono questi problemi, perchè un numero molto grande viene sempre riportato ad avere mantissa tra 0 ed 1; però bisogna ricordarsi di mettere w>d+6, in quanto "w" deve contenere, oltre alla mantissa di "d" caratteri, un eventuale segno negativo, il punto decimale e 4 caratteri per l'esponente.
Nell' esempio 9.3 abbiamo visto che le variabili sono state scritte tutte sulla stessa riga (ricordiamo che nei normali terminali una riga non può superare 80 caratteri). Si può dire al computer di cambiare riga usando il descrittore "/". Per esempio, se il formato dell'istruzione di scrittura dell'esempio 9.3 fosse stato,
100 FORMAT(2I5/2F10.3,E10.3)
il risultato sarebbe stato il seguente
¬¬¬23¬¬-12
¬¬¬¬¬3.142¬¬¬¬13.100¬¬.202E+02
Dopo che i primi due numeri sono stati scritti su una riga, il descrittore "/" ha fatto scrivere le successive tre variabili nella riga seguente.
Se un descrittore deve essere ripetuto "n" volte si puo scrivere "nFw.d", ecc., come nel caso della lettura. Inoltre, se si fanno scrivere un numero di variabili maggiore del numero di descrittori all'interno del FORMAT, essosarà ripetuto da capo. Aggiungendo delle parentesi all'interno del FORMAT, si può variare il numero di descrittori che saranno ripetuti.
WRITE(6,100) A1,A2,A3,A4,A5,A6,A7,A8,A9,A0
100 FORMAT(4E10.3)
porta alla scrittura di 3 righe, di cui le prime due contengono quattro numeri e la terza due numeri in formato "E10.3". Se il formato fosse stato:
100 FORMAT(3E10.3,(E10.3))
¶
si avrebbero quattro numeri nella prima riga e poi sei righe con un numero solo, poiché il FORMAT verrebbe ripetuto solo a partire dal segno ¶. Le parentesi possono essere usate anche nella seguente maniera:
100 FORMAT(E10.3,9(2X,F4.2))
Questo FORMATproduce una riga in cui il primo numero è scritto con formato "E10.3", e i nove successivi con formato "F4.2" con due spazi bianchi fra ogni numero (lunghezza totale di riga scritta = 64 caratteri).
Uno degli aspetti più importanti del calcolo elettronico risiede nella possibilità di salvare i dati su un mezzo magnetico (dentro un "file"), per poterli riutilizzare in un secondo momento, sia dallo stesso programma e sia da altri programmi. Abbiamo visto che con le istruzioni WRITE(u,...) e READ(u,...) possiamo eseguire delle letture e scritture su dei files che hanno un nome standard (sui computer UNIX sono i files di nome "fort.u"); in questo caso diremo che l'unità di numero "u" è connessa al file di nome fort.u .Però é possibile ridefinire il file connesso ad una data unità tramite l'istruzione OPEN; in questo modo si possono evitare interferenze tra programmi diversi, interferenze che possono nascere quando entrambi i programmi per esempio scrivono sulla stessa unità. L'istruzione OPEN prende la forma,
OPEN(UNIT=u,FILE='fn',STATUS='st',FORM='fm')
dove "fn" è il nome del file che deve essere connesso all'unità "u". Dopo che il computer ha trovato questa istruzione nel programma, le eventuali operazioni di scrittura e lettura associate all'unità "u" sono effettuate sul file "fn" e non più sul file fort.u. La specifica STATUS può essere OLD, NEW, UNKNOWN, SCRATCH e serve per informare il computer se il file già esiste o no. Se "st" è OLDsignifica che il file deve già esistere (questa informazione è utile quando il computer deve leggere dati da quel file), mentre se è NEWsignifica che il file non deve già esistere (utile quando si deve scrivere dati su quel file); quando non siamo sicuri di come verrà utilizzato il file (un po' per scrittura e un po' per lettura) possiamo usare lo status UNKNOWN. Se "st" è SCRATCH allora il programma crea uno speciale file che verrà cancellato quando il programma cesserà l'esecuzione.
La specifica FORM='FORMATTED' indica che il file conterrà gli usuali caratteri (lettere, numeri, segni di punteggiatura, ecc.) in modo che il programmatore (o un altro tipo di computer) possa leggere il file; questa è anche il modo standard di scrivere su un file. Eppure, il computer deve fare una certa quantità di lavoro per convertire i dati dalla rappresentazione interna binaria a quella "umana". Se i dati che vengono scritti nel file sono molti e servono solo per essere riletti dal computer non è necessario convertirli nei caratteri usuali; il computer può quindi scriverli nel file usando la propria rappresentazione; questo si può fare usando la specifica FORM='UNFORMATTED'. Per leggere da un file "unformatted" si usano i comandi WRITE e READ senza la specifica di formato. Per esempio,
OPEN(UNIT=1,FILE='myfile',FORM='UNFORMATTED',
>
STATUS='SCRATCH')
WRITE(1) A1,A2,A3,A4
.....
READ(1) A1,A2,A3,A4
Qualche volta è utile disconnettere un file aperto con l'istruzione OPEN e per questo si usa l'istruzione CLOSE:
CLOSE(UNIT=u,STATUS='st')
Questa istruzione può essere utile perchè se il programma si interrompe in maniera anomala, ad esempio usando l'interruzione forzata "Ctrl-C", spesso i files aperti vengono perduti. Invece, l'istruzione CLOSE chiude la connessione tra programma e file che quindi sarà scritto in maniera definitiva sul disco, evitando così i pericoli da interruzione accidentale.
Ci sono varie istruzioni che regolano la riga precisa da dove il computer inizierà la scrittura o lettura. Quando si apre un file il punto di srittura è la prima riga e dopo che sono state lette o scritte n righe, il punto di posizionamento è la riga n+1. L 'istruzione
REWIND u
causa il riposizionamento sulla prima riga, mentre
BACKSPACE u
porta il computer a riposizionarsi una riga indietro (quindi il computer può per esempio rileggere la stessa riga una altra volta). Un altra istruzione importante è
ENDFILE u
Questa istruzione scrive una riga speciale chiamata "ultima riga"; se si riposiziona la riga di lettura all'inizio con REWIND e poi si comincia a leggere di nuovo con READ(u,f,END='d'), arrivati alla "ultima riga" si verifica una condizione di errore che attiva la specifica END='d' dell'istruzione READ (se END='d' è omesso il computer blocca l'esecuzione). Si può quindi usare ENDFILE per avvertire che il file è finito o per aggiungere righe alla fine di un file; per esempio, supponendo di avere scritto nell' unità=8 una "ultima riga", si possono usare le istruzioni seguenti:
C leggi il file fino alla "ultima riga"
20 READ(8,END=21)
GOTO 20
21 BACKSPACE 8
C ora aggiungi nuova informazione
WRITE(8) ....
.....
C terminare con un ENDFILE per la prossima scrittura
ENDFILE 8
9.5 L' istruzione NAMELIST
Questa istruzione è usata quando si devono leggere, per esempio dal terminale, i valori di molte variabili; in questo caso l' impiego dell'istruzione READ comporterebbe la battitura di una lunga serie di numeri alla tastiera; invece, con il READassociato all' istruzione NAMELIST è possibile assegnare solo una parte di dati, mentre l' altra parte mantiene i valori inalterati (magari assegnati con istruzioni DATA). In pratica, l' istruzione NAMELIST definisce la lista di variabili che dovranno essere "lette":
NAMELIST /nn/ A,B,I,J,....
dove "nn" è una sequenza di uno o più caratteri definita dal programmatore, vale a dire un nome, che specifica la lista A,B,I,J,.., ecc.. Si possono avere più liste specificate con istruzioni NAMELIST nello stesso programma (con nome diverso); queste istruzioni vanno poste dopo avere definito i tipi di variabile e dimensionato i vettori. Quindi, per leggere i valori alle variabili definite nel NAMELIST di nome "nn" si scrive:
READ(5,nn)
Quando il computer si arresta e aspetta di leggere i dati, bisogna usare il seguente formato (il simbolo "¬" raffigura uno "spazio bianco"),
¬&nn¬A=1.,I=2,B=0.¬/
Come si vede dal precedente
esempio, si possono assegnare i valori in un ordine arbitrario e non è
necessario assegnare valori a tutte le variabili. Come si è detto
precedentemente, in questo caso, le variabili non specificate conservano
il valore che avevano prima dell'istruzione READ(5,nn).
L'istruzioneDO serve per ripetere una parte di programma un certo numero di volte, creando un cosiddetto "loop" (ciclo). Un loop può contenere qualsiasi altra istruzione FORTRAN, anche un altro loop; in questo caso si parla di loops "annidati" (nested). L'istruzione DO prende la forma,
DO ll VAR=V1,V2,V3
dove "ll" è una etichetta di riga che identifica l'ultima riga di istruzioni compresa nel gruppo di righe da ripetere; "VAR" è il nome di una variabile (usualmente una variabile intera), mentre "V1,V2,V3" rappresentano rispettivamente il valore iniziale, il valore finale e di quanto "VAR" deve aumentare ogni volta che il loop è ripetuto; VAR,V1,V2,V3 devono essere variabili o costanti numeriche dello stesso tipo. Inoltre, se "V3" ha valore 1, si può anche omettere. Il loop viene ripetuto un numero Nrip di volte pari a
Nrip =MAX(0,INT[(V2-V1+V3)/V3])
Da notare che l'ultimo valore che assumerà la variabile "VAR" è
VARmax= V1+V3*Nrip
che può non coincidere necessariamente con V2 (comunque risulta sempre VARmax£V2). Per esempio il comando DO 10 I=1,10,2genera un loop ripetuto (10-1+2)/2=5 volte, quindi la variabile I assumerà in sequenza i valori 1,3,5,7,9. Il comando DO 10 I=10,9,2 invece porta a Nrip=0, ed in questo caso, l'intero gruppo di istruzioni contenuto nel loop sarà completamente ignorato.
Il
computer segue le seguenti regole quando deve eseguire un loop:
1 | calcola Nrip |
2 | pone VAR=V1 |
3 | se Nrip=0 esce dal loop (salta al punto 7) |
4 | esegue le istruzioni comprese tra la riga con il DO e la riga successiva a quella con etichetta ll |
5 | somma V3 a VAR |
6 | sottrae 1 da Nrip e salta al punto 3 |
7 | passa all'esecuzione dei comandi che risiedono nelle righe successive alla riga etichettata con ll |
Si può discutere sul valore che assume la variabile VAR all'uscita del loop. Se il loop è completato normalmente, dalle regole precedenti segue che VAR ha il valore VARmax+V3. Invece, se nelle righe contenute nel loop c'è una istruzione di salto (GOTO) che porta al punto 7 prima che il loop sia terminato (vale a dire prima che Nripraggiunga il valore zero), il valore di VAR all'uscita del loop è l'ultimo assegnato al punto 5, e quindi dipenderà da caso a caso.
Come ultima riga del loop, spesso si usa l' istruzione
ll CONTINUE
(dove ll è l'etichetta di riga definita nel comando DO) che non esegue nessuna operazione.
Come abbiamo detto,
si possono avere loops annidati; in questo caso bisogna considerare che
il loop più interno dovrà essere stato completato quando
si arriva all'ultima istruzione del loop esterno. Vediamo alcuni esempi
nella tabella 10.1.
Tabella 10.1 Annidamenti validi e non validi.
|
|
|
DO 1 I=1,10 | DO 1 I=1,10 | DO 1 I=1,10 |
DO 2 J=1,10 | DO 1 J=1,10 | DO 2 J=1,10 |
....... | ...... | ...... |
2 CONTINUE | 1 CONTINUE | 1 CONTINUE |
1 CONTINUE | 2 CONTINUE |
Nel terzo caso, l'etichetta del loop più esterno si trova prima dell'etichetta che specifica la fine del loop più interno e quindi questa serie di istruzioni non è valida.
Nell'istruzione DO si può anche omettere il numero di etichetta; in questo caso, per specificare il gruppo di istruzioni che saranno ripetute bisogna inserire una riga con il comando END DO (cioè, il loop comprenderà le istruzioni comprese tra la riga con DO e quella con END DO).
Per concludere la
discussione dell'istruzione DO,
si deve avvertire che non è permesso, nelle istruzioni dentro un
loop, cercare di modificare il valore delle variabili VAR, V1,
V2 eV3.
Insieme alle espressioni aritmetiche, il FORTRAN contiene espressioni logiche o criteri, che possono assumere il valore di vero e falso. Le espressioni logiche possono essere usate con l'istruzione IF per dare al programma la possibilità di prendere delle decisioni e quindi eseguire differenti parti di programma a seconda del risultato di quelle decisioni. Il formato dell'istruzione IF è il seguente,
IF(criterio 1) THEN
gruppo di istruzioni 1
ELSE IF(criterio 2) THEN
gruppo di istruzioni 2
......
ELSE IF(criterio N) THEN
gruppo di istruzioni N
ELSE
gruppo di istruzioni N+1
END IF
Il significato dei comandi IF ("se"), THEN ("allora") e ELSE ("altrimenti") è identico a quello delle corrispondenti parole inglesi. Nell? esempio precedente si ha: se il criterio 1 risulta "vero" allora si esegue il gruppo di istruzioni 1; se il criterio 1 è "falso" ma il criterio 2 è "vero", allora si esegue il gruppo di istruzioni 2; se i criteri 1,. . .,I-1 sono "falsi" e il criterio I è "vero" allora si esegue il gruppo di istruzioni I, ecc. Infine, se tutti i criteri sono "falsi" allora si esegue il gruppo di istruzioni N+1. In tutti i casi, dopo avere eseguito un singolo gruppo di istruzioni, il controllo passa alle istruzioni seguenti la riga con il comando END IF. Si possono anche avere istruzioni più semplici come
IF(criterio) THEN
gruppo di istruzioni a
ELSE
gruppo di istruzioni b
END IF
o
IF(criterio) THEN
gruppo di istruzioni
END IF
o
IF(criterio) istruzione
Quando non si specifica ELSE come negli ultimi due esempi, e il criterio risulta "falso", il computer passa direttamente all?esecuzione delle istruzioni successive.
Esaminiamo ora la
forma dei criteri più semplici, detti anche "espressioni logiche
elementari", che sono elencate nella tabella 11.1.
________________________________________________________________
Tabella 11.1 Le espressioni logiche elementari (A e B sono due variabili).
A.EQ.B vero se A=B e falso se A¹B.
A.NE.B vero se A¹B e falso se A¹B.
A.GT.B vero se A>B e falso se A£B.
A.GE.B vero se A³B e falso se A<B.
A.LT.B vero se A<B e falso se A³B.
A.EQ.B vero se A£B e falso se A>B.
________________________________________________________________
Da notare che nei criteri si possono usare anche espressioni aritmetiche complesse al posto delle variabili A e B , come nell? esempio seguente:
IF(A+10./D1 .EQ. B**2.) THEN ....
In questi bisogna tenere conto che prima vengono eseguite tutte le operazioni numeriche e successivamente viene valutata l?espressione logica.
È possibile combinare le espressioni logiche elementari presentate nella tabella 11.1 in criteri più complessi, usando gli operatori .AND., .OR. e .NOT. . Questi operatori non agiscono tra espressioni numeriche ma tra espressioni logiche come è specificato nella tabella 11.2.
________________________________________________________________
Tabella 11.2 Gli operatori logici .AND.,.OR. e .NOT. (c1,c2 sono criteri, quindi hanno il valore di vero o falso).
c1.AND.c2 vero se c1 e c2 sono ambedue veri, mentre falso se almeno uno è falso.
c1.OR.c2 vero se dei criteri c1,c2 almeno uno è vero, mentre falso se ambedue sono falsi.
.NOT.c1 vero se c1 è falso, mentre falso se c1 è vero
(riversa il valore di c1).
________________________________________________________________
Esempio 11.1. Sia I=1, J=1 e K=2;
criterio:risultato:
I.EQ.J.AND.I.EQ.K falso
I.EQ.J.OR.I.EQ.K vero
I.EQ.J.AND..NOT.I.EQ.K vero
Un singolo criterio può contenere un numero arbitrario di espressioni logiche semplici, tutte unite con gli operatori .AND., .OR. e .NOT. . In questo caso, è necessario conoscere la priorità delle varie operazioni, come specificato nella tabella 11.3.
________________________________________________________________
Tabella 11.3 La priorità delle varie operazioni nella valutazione di un criterio.
PrioritàOperazione
alta espressione numerica
. . . espressione logica semplice
. . . .AND.
. . . .OR.
bassa .NOT.
________________________________________________________________
Quindi nella valutazione
di un criterio, prima vengono eseguite tutte le espressione numeriche,
poi le espressioni logiche semplici (quelle specificate nella tabella 11.1).
Quindi si valutano gli operatori
.AND.,
.OR.,
e .NOT.,
nell?ordine. é possibile, inserendo coppie di parentesi tra espressioni
logiche semplici, variare l?ordine delle operazioni; come al solito, le
operazioni fra parentesi vengono eseguite prima.
Esempio 11.2.
Se c1,
c2
e c3
sono tre criteri, avremo i seguenti casi (v sta per vero ed f per falso):
c1 c2 c3 c1.AND.c2.OR.c3 c1.AND.(c2.OR.c3)
v v v v v
v v f v v
v f v v v
v f f f f
f v v v f
f v f f f
f f v v f
f f f f f
____________________________________________________
Nel criterio
c1.AND.c2.OR.c3
dell? esempio precedente si esegue prima l?operazione c1.AND.c2,
e quindi l? .OR. ,
mentre nell?altro caso, l?ordine è esattamente il contrario e, come
si vede, il risultato può essere diverso. In generale, volendo scrivere
un criterio "complicato", è opportuno costruirsi una tabella simile
a quella precedente e considerare tutti i casi possibili.
11.1 L? IF aritmetico
E? possibile usare l?istruzione IF in un altro modo (IF "aritmetico"); questa procedura permette la scelta tra tre possibilita? a secondo del valore di una variabile. L? IF aritmetico prende la forma
IF(X) l1,l2,l3
e causa la prosecuzione dell?esecuzione dalla riga di etichetta l1 se il valore della variabile X e? minore di zero, dalla riga etichettata l2 se X e? zero e dalla riga etichettata l3 se X e? maggiore di zero. Si può usare anche una espressione numerica al posto della variabile dentro la parentesi: la scelta di dove continuare l?esecuzione e? allora causata dal valore negativo, zero o positivo dell?intera espressione. Da notare che questo uso di IF è obsoleto e dovrebbe essere evitato perché comporta un notevole uso di "salti" e quindi una notevole complicazione del programma.
11.2 Espressioni logiche con numeri reali
L?uso dei criteri con i numeri reali richiede una certa attenzione, come si può capire dall?esempio seguente. Se A=2., B=3. e C=6. e il computer deve eseguire una istruzione del tipo
IF(A*B.EQ.C) GOTO 100
il criterio dovrebbe essere "vero" e il computer dovrebbe saltare alla riga etichettata con il numero 100. Eppure, a causa degli errori di arrotondamento, la moltiplicazione potrebbe dare valore 6.0000001 o 5.9999999 o altro, (supponendo di lavorare con variabili in precisione singola) e il criterio risulterebbe falso. Questi errori non sono prevedibili (quindi non saremmo sicuri se il computer eseguirebbe l?istruzione GOTO 100) ed è opportuno prendere alcuni accorgimenti. Per esempio, si può scrivere al posto dell? IF precedente, la nuova istruzione
IF(ABS(A*B-C).LT.1.E-5) GOTO 100
dove ABS(X) è una funzione intrinseca, vale a dire un programma precostituito nel FORTRAN che dà il valore assoluto del suo argomento X. Vedremo più avanti altri esempi di funzioni intrinseche. Nell?esempio precedente, il criterio è stato scritto tenendo conto della possibilità che A*B possa differire da C di una quantità pari a 0.000001 (in più e in meno); in questo modo, poiché l?errore di arrotondamento dovrebbe essere più piccolo di questa quantità, siamo sicuri che il criterio non dipenderà più da eventuali errori di arrotondamento.
[HOME]
12 L'istruzione
GOTO e i loop "condizionali"
Abbiamo già
incontrato l?istruzione GOTO in
istruzioni del tipo
GOTO ll
dove ll è una etichetta di riga posta da qualche parte nel programma (può essere sia prima che dopo la riga con GOTO). Questa istruzione ha come conseguenza un "salto", vale a dire, l?esecuzione viene continuata dalla riga di etichetta ll e non con l?istruzione della riga successiva al GOTO. Questa istruzione è un "GOTO incondizionato", poiché il salto viene eseguito sempre e quindi va usato con molta cautela. Se il GOTO è unito con un IF si parla di "GOTO condizionato"; questa istruzione è usata frequentemente per formare loops senza ricorrere all?istruzione DO. In questo caso, se il salto viene eseguito ad una istruzione di una riga precedente, si corre il rischio di creare un loop infinito, cioè un loop senza vie di uscita. Vediamo esplicitamente uno di questi casi illustrato nell? esempio seguente:
l1 IF(criterio 1) GOTO l2
blocco di istruzioni A
GOTO l1
l2 istruzioniB
In questo caso, il blocco di istruzioni A è ripetuto finche ` il criterio 1 rimane falso, mentre quando diventa vero si passa al blocco di istruzioni B. Risulta chiaro quindi che se per qualche motivo il criterio 1 rimanesse sempre falso, l?esecuzione rimarrà perennemente bloccata dal GOTO l1. Un altro caso simile è il seguente
l1 blocco di istruzioni A
IF(criterio 2) GOTO l1
istruzioni B
La principale differenza fra i due casi mostrati è che nel secondo caso il blocco di istruzioni A viene eseguito almeno una volta, contrariamente al primo esempio, dove se il criterio 1 fosse vero all?inizio, il blocco A verrebbe saltato completamente.
Un altra istruzione che può essere utile è il cosiddetto "GOTO calcolato", che ha la seguente forma
GOTO(l1,l2,...,ln),espressione numerica intera
e causa un trasferimento di controllo alla riga etichettata l1 se l?espressione numerica intera valesse 1, alla riga etichettata l2 se l?espressione valesse 2, e così via. Se l?espressione numerica è negativa, zero o maggiore di n, il GOTO calcolato viene semplicemente saltato.
Parte C. La struttura dei programmi FORTRAN.
13 Il programma principale e i sottoprogrammi
Un programma FORTRAN completo comprende un programma "principale" (MAIN PROGRAM) e un numero variabile di sottoprogrammi (anche nessuni), che possono essere di due tipi: SUBROUTINE e FUNCTION. Per esempio, si supponga di avere un programma dove si esegue la stessa operazione, diciamo la derivazione, sopra diverse funzioni. In questo caso, conviene mettere la serie di istruzioni che calcola la derivata su una funzione generica dentro un sottoprogramma; a questo punto, se si devono derivare "n" funzioni, basta "chiamare", vale a dire eseguire, quel sottoprogramma "n" volte.
La serie di istruzioni facenti parte di un sottoprogramma deve iniziare con una delle istruzione seguenti (intestazione):
PROGRAM nome programma principale (facoltativa)
FUNCTION nome(lista) sottoprogramma di tipo FUNCTION
SUBROUTINE nome(lista) sottoprogramma di tipo SUBROUTINE
dove "lista" rappresenta una lista di variabili dette gli "argomenti" dei sottoprogrammi. La fine di ogni unità di programma (programma principale o sottoprogramma) è specificata dall' istruzione END. Per accedere alle istruzioni in un sottoprogramma di tipo function e, per esempio, di nome "AAA", basta usare questo nome come una qualsiasi variabile e inserirla in una espressione numerica (bisogna però aggiungere la lista degli argomenti); per chiamare una subroutine, per esempio di nome "BBB", invece si usa il comando CALL, come mostrato qui di seguito
CALL BBB(lista)
Un sottoprogramma può chiamare altri sottoprogrammi e così via; il computer ritorna ad eseguire le istruzioni del programma "chiamante" dopo che il sottoprogramma chiamato arriva all' istruzione RETURN.
Esempio 13.1. Esaminiamo il programma seguente:
PROGRAM main
.....
A=FINT(X1,X2,X3)
......
CALL DERE(X4,X5,X6)
......
STOP
END
FUNCTION FINT(Y1,Y2,Y3)
blocco di istruzioni a
FINT= ...
RETURN
END
SUBROUTINE DERE(Y4,Y5,Y6)
blocco di istruzioni b
RETURN
END
Il "flusso" di istruzioni eseguite è il seguente: l?esecuzione comincia dal programma principale, che in questo caso è stato chiamato "main", e procede normalmente fino ad arrivare all? istruzione dove compare FINT(X1,X2,X3); il computer controlla se esiste una matrice tridimensionale di nome FINT e non trovandola cerca una function di quel nome (le variabili X1,X2,X3 sono gli argomenti di questa function); essendoci nel nostro caso un tale sottoprogramma, il computer trasferisce il controllo al gruppo di istruzioni "a" (che sono comprese tra l? istruzione FUNCTION FINT(Y1,Y2,Y3) e l?istruzione END) e comincerà` ad eseguirle fino ad arrivare all? istruzione RETURN. A questo punto il controllo torna al programma principale, che riprende l? esecuzione da dove era rimasta, cioè all? assegnazione della variabile A. L? esecuzione continua fino alla chiamata delle subroutine DERE; il controllo ora passa a questa subroutine che esegue il gruppo di istruzioni "b" fino ad arrivare all? istruzione RETURN, che fa ritornare di nuovo alle istruzioni del programma principale seguenti la riga con CALL DERE. L? esecuzione continuerà fino al comando STOP.
I sottoprogrammi e il programma principale formano delle unità ben separate; per esempio, non si può con un comando GOTO saltare, partendo da un sottoprogramma, ad una istruzione di un sottoprogramma diverso. In particolare, ogni unità ha i suoi numeri di etichetta e in due o più unità può comparire la stessa etichetta.
Anche le variabili usate da un sottoprogramma sono specifiche a quel sottoprogramma, vale a dire che il computer assegna diverse parti della memoria ad ogni unità di programma e da una unità non si può accedere (a meno di usare il comando COMMON, si veda la sezione 14) alle variabili di un altra unità. In particolare, una variabile di una unità può avere lo stesso nome di variabili definite in altre unità, senza pericolo di interferenze, in quanto come detto, ogni unità mette le variabili in una sua zona particolare di memoria. Una altra cosa da notare è che le istruzioni che specificano il tipo delle variabili e/o il dimensionamento di vettori e matrici poste all? inizio di una unità riguardano solamente le variabili definite in quella particolare unità, e non hanno effetto sulle variabili definite in altre unità. Quindi il programmatore deve avere cura di inserire all? inizio di ogni sottoprogramma tutte le opportune istruzioni descritte nella sezione 8.
La via di comunicazione tra una unità A e un sottoprogramma B è costituita dagli argomenti che compaiono, tra parentesi, accanto al nome della unità B. Come abbiamo visto dall? esempio 13.1 non è importante che gli argomenti che compaiono nella chiamata del sottoprogramma nell? unità A abbiano lo stesso nome di quelli che compaiono nell? intestazione del sottoprogramma B. é necessario tuttavia che siano dello stesso tipo (reale, intero, in doppia precisione, ecc.) e che contengono lo stesso numero di elementi, cioè che abbiano la stessa dimensione (stesso numero di bytes). Se, nell? esempio 13.1, la variabile X1 del programma principale fosse un vettore di 100 elementi, anche Y1 (definito nella function FINT) dovrebbe essere un vettore di tale dimensione. Vediamo con un esempio come avviene questo passaggio di informazioni tra l?unità chiamante Ae per esempio una subroutine di nome BBB. In qualche punto dell? unità Asi troverà una istruzione del tipo
CALL BBB(X1,X2,X3)
Supponiamo inoltre che l?intestazione della subroutine sia
SUBROUTINE BBB(Y1,Y2,Y3)
Quando il controllo passa dall? unità Aalla subroutine, alle variabili Y1,Y2,Y3, poste nella parte di memoria assegnata alla subroutine BBB viene associato il valore delle variabili X1,X2,X3, rispettivamente, valori che sono posti nella parte di memoria del programma chiamante A. Quando la subroutine restituisce il controllo all? unità A, avviene il processo inverso. Quindi se per esempio, nella subroutine BBB la variabile Y1 assumesse un nuovo valore, anche la variabile X1 del programma chiamante assumerà quel valore, quando la subroutine restituisce il controllo. Inoltre, le variabili usate nella subroutine BBB, a parte naturalmente Y1,Y2,Y3, non sono accessibili dall? unità A; bisogna notare però che, se l? unità A richiamasse la stessa subroutine B altre volte, le variabili definite in B avrebbero conservato il valore che avevano al momento dell? ultimo ritorno all? unità chiamante A (in alcuni computer questo non è automatico ma viene fatto se si usa l? istruzione SAVE).
13.1 External functions
Esaminiamo ora alcune proprietà dei sottoprogrammi di tipo FUNCTION; questi sottoprogrammi, scritti dal programmatore, si chiamano "esterni" (external) per distinguerli da quelli "interni" forniti direttamente dal FORTRAN (si veda la sezione 13.2). Come abbiamo già detto la prima istruzione di una function deve essere (FNMAME è un nome generico):
FUNCTION FUNAME(VAR1,VAR2,...)
dove VAR1,VAR2,... è una lista di variabili che conterrà i dati di entrata necessari per fare i calcoli. Con le function possiamo solo far calcolare un solo risultato che in uscita sarà associato proprio al nome "FUMAME" della function. Quindi, dentro la function stessa si dovrà trovare una istruzione del tipo (di solito giusto prima dell? istruzione RETURN):
FUNAME=espressione numerica
Quindi FUNAME è a tutti gli effetti una variabile, sia nel sottoprogramma che nel programma chiamante e dovrà seguire tutte le regole che seguono le variabili, in particolare a riguardo del tipo. Se non si specifica niente, si assume che una function il cui nome incomincia con le lettere I, J, K, L, M, N sia intera e negli altri casi reale R4. Naturalmente, è possibile definire il nome di una function come vogliamo usando i comandi REAL, COMPLEX, e INTEGER, come negli esempi seguenti
REAL FUNCTION FUNAME(....) reale R4;
REAL*4 FUNCTION FUNAME(....) reale R4;
REAL*8 FUNCTION FUNAME(....) reale R8;
DOUBLE PRECISION FUNCTION FUNAME(....) reale R8;
INTEGER FUNCTION FUNAME(....) intera I4, ecc.
Anche nel programma chiamante il nome della function è usato come una variabile e può entrare dentro espressioni numeriche, come per esempio:
A=B*FUNAME(VAR1,VAR2,...)+ ....
é importante fare in modo che anche nel programma chiamante il nome della function sia dello stesso tipo di quello definito nella function stessa. Se per esempio, la function cominciasse con il comando DOUBLE PRECISION FUNCTION FUNAME, all? inizio del programma chiamante è necessario definire FUNAME come variabile reale a doppia precisione tramite il comando REAL*8.
13.2 Intrinsic o internal functions
Come abbiamo già detto, esiste già una serie di functions precostituite e che servono per il calcolo delle funzioni matematiche più comuni, come le funzioni trigonometriche, ecc. Per ogni caso, ne esistono diverse versioni a seconda del tipo dell? argomento e della precisione con cui vogliamo conoscere il risultato. Per esempio, le funzioni adibite al calcolo del seno di un angolo X (in radianti) sono le seguenti
SIN(X) X reale R4 risultato reale R4
DSIN(X) X reale R8 risultato reale R8
QSIN(X) X reale R16 risultato reale R16
CSIN(X) X complesso C8 risultato complesso C8
Come regola quasi
generale si ha che la function che esegue il calcolo in doppia precisione
si ottiene anteponendo al nome "normale" la lettera D,
per la precisione quadrupla la lettera Q,
per avere il risultato con i complessi la lettera C,
ecc. Una lista (parziale) di intrinsic functions, in precisione singola,
usate per calcolare funzioni matematiche può essere trovata nella
tabella 13.1.
Tabella
13.1. Alcune intrinsic functions in precisione semplice usate per calcolare
le funzioni matematiche più comuni.
ACOS(X) | arcocoseno di X |
ALOG(X) | logaritmo naturale di X |
ALOG10(X) | logaritmo in base 10 di X |
ASIN(X) | arcoseno di X |
ATAN(X) | arcotangente di X |
COS(X) | coseno di X |
COSH(X) | coseno iperbolico di X |
ERF(X) | funzione errore calcolata in X |
EXP(X) | esponenziale = eX |
GAMMA(X) | funzione gamma calcolata in X |
SIN(X) | seno di X |
SINH(X) | seno iperbolico di X |
SQRT(X) | radice quadrata di X |
TAN(X) | tangente di X |
TANH(X) | tangente iperbolica di X |
__________________________________________________________________
C?è un? altra
serie di functions che eseguono operazioni utili di vario tipo, come elencato
nella tabella 13.2.
__________________________________________________________________
Tabella 13.2. Alcune intrinsic functions per operazioni varie.
FunctionArgomenti Risultato Definizione
AIMAG(X) complessorealeparte immaginaria di X;
REAL(X) complessorealeparte reale di X;
CONJG(X) complessocomplessocomplesso cognugato di X;
FLOAT(X) interoreale R4trasformazione di X da I4 a R4;
DFLOAT(X) interoreale R8trasformazione di X da I4 a R8;
DBLE(X) reale R4 reale R8 trasformazione di X da R4 a R8;
INT(X) reale R4 interotrasformazione di X da R4 a I4;
IDINT(X) reale R8 interotrasformazione di X da R8 a I4;
ABS(X) reale R4 reale R4 valore assoluto di X;
DABS(X) reale R8 reale R8 valore assoluto di X;
IABS(X) intero I4 intero I4 valore assoluto di X;
MAX0(X1,.,XN) interi I4 intero I4 massimo tra X1,..,XN;
AMAX1(X1,.,XN) reali R4 reale R4 massimo tra X1,..,XN;
DMAX1(X1,.,XN) reali R8 reali R8 massimo tra X1,..,XN;
MIN0(X1,.,XN) interi I4 intero I4 minimo tra X1,..,XN;
AMIN1(X1,.,XN) reali R4 reale R4 minimo tra X1,..,XN;
DMIN1(X1,.,XN) reali R8 reali R8 minimo tra X1,..,XN;
__________________________________________________________________
13.3 Statement functions
Quando le istruzioni che compongono una function possono essere ridotte ad una sola, è possibile definire quella function direttamente nel sottoprogramma che la deve usare. Questo modo di definire la funzione (chiamata "statement function", cioè funzione dichiarata) deve essere piazzata senza alternative subito dopo le istruzioni che specificano il tipo e la dimensione delle variabili e subito prima di ogni istruzione di assegnazione. La forma dell? istruzione è, per esempio,
AAA(X1,.,XN)=espressione aritmetica
L? espressione aritmetica può contenere: costanti; le variabili X1,.,XN; altre variabili definite nella stessa unità di programma; altre statement functions, basta che siano definite precedentemente; intrinsic functions; e altre funzioni esterne (basta che non modifichino il valore di X1,.,XN. L?uso di queste functions è identico a quello descritto precedente: si possono inserire direttamente nelle espressioni aritmetiche che definiscono qualche variabile. Per esempio, la statement function AAA definita precedentemente può comparire in una istruzione del tipo
A=2.*(AAA(Y1,.,YN)+4.)
Come già discusso, al momento dell? esecuzione dell? istruzione A=..., il programma valuta l? espressione aritmetica associata alla statement function AAA, prendendo come valore delle variabili X1,.,XN i valori che sono associati alle variabili Y1,.,YN. I vantaggi maggiori di questo tipo di function stanno nella maggiore velocità di esecuzione; infatti, il computer non deve eseguire un trasferimento di controllo ad un altra unità di sottoprogramma, che richiede sempre un certo tempo, ma è come se l? espressione aritmetica associato alla statement function fosse inserito direttamente nelle istruzioni dell? unità. In effetti, la statement function può essere considerata semplicemente come un modo per semplificare la scrittura di identiche e ripetute espressioni numeriche. Da notare infine, che questo tipo di function è strettamente locale, cioè può essere usata solamente nell? unità di programma dove è dichiarata.
13.4 Subroutines
Una subroutine è la forma migliore per spezzare il programma in parti più piccole, ognuna dedicata ad un compito specifico. Il programma chiamante accede alla subroutine tramite l? istruzione CALL:
CALL SUNAME(ARG1,...,ARGN)
che causa l? esecuzione del gruppo di istruzioni che iniziano con
SUBROUTINE SUNAME(VAR1,...,VARN)
e finiscono con l? istruzione END. Il controllo ritorna al programma chiamante quando la subroutine termina l? esecuzione con l? istruzione RETURN. Da notare che "SUNAME" è semplicemente un nome usato per chiamare la subroutine e non una variabile. I risultati del calcolo sono passati al programma chiamante attraverso gli argomenti ARG1,...,ARGN della subroutine (un metodo alternativo è quello di usare l? istruzione COMMON, come spiegato nella sezione successiva).
Come detto nella sezione precedente, ogni sottoprogramma ha una parte di memoria riservata dove immagazzinare i valori associati alle proprie variabili (che sono dette variabili locali). Nel FORTRAN è prevista la possibilità di rendere alcune variabili accessibili a due o più sottoprogrammi; queste variabili così definite sono ora chiamate variabili globali. Per ottenere che alcune variabili diventino condivisibili tra le diverse unità del programma si usa l? istruzione COMMON, che ha il seguente formato
COMMON /name/ VAR1,VAR2,....
dove VAR1,VAR2,... rappresenta la lista delle variabili che devono diventare globali. Per esempio,
COMMON /EX1/ A,B,V(100),I,J
definisce una lista di variabili, avente EX1 come nome di COMMON, comprendente le variabili A,B,I,J e il vettore V(100). Da notare che con questa istruzione si può dimensionare direttamente eventuali vettori e/o matrici. Infatti nell? esempio precedente non è necessario specificare le dimensioni di V in una istruzione DIMENSION. Alternativamente si può scrivere
COMMON /EX1/ A,B,V,I,J
DIMENSION V(100)
Poichè, questa istruzione è una istruzione che specifica una proprietà delle variabili, va posta all? inizio del programma, prima delle istruzioni DATA e prima di ogni riga contenente espressioni numeriche.
Se si inserisce in due o più sottoprogrammi un dato COMMON, le variabili della lista saranno disponibili in tutte queste unità. Questo è quindi un altro modo per passare dati tra il programma chiamante e il sottoprogramma e viceversa. Le variabili "nel COMMON" di un dato "nome" sono messe in un area di memoria a comune di quei sottoprogrammi dove compare l? istruzione COMMON/nome/. Da notare che il passaggio dei dati attraverso gli argomenti ad un sottoprogramma necessita un trasferimento di valori tra le aree di memoria delle due unità (chiamante e sottoprogramma); nel caso di trasferimento di una grossa quantità di dati questa procedura può richiedere un certo tempo e quindi può risultare più conveniente mettere le variabili da trasferire in una area comune.
Consideriamo uno stesso COMMON ripetutoin due (o piu?) unità differenti: i nomi delle variabili del COMMON che compaiono nelle varie unità possono essere diversi e anche il numero di variabili può essere diverso; comunque è importante che il numero dei bytes occupati dalle variabili sia lo stesso in tutti i casi; per fare un esempio, supponiamo che nell? unità A compaia
COMMON /EX2/ A1,A2,B(10)
ed in un'altra unità B
COMMON /EX2/ Z(12)
Allora nell? unità B Z(1) assumerà il valore di A1, Z(2) assumerà il valore di A2, Z(3) assumerà il valore di B(1), ecc. In questo modo si può risparmiare sull? impiego di memoria, facendo occupare da più sottoprogrammi solo una zona in memoria invece di molte sezioni separate, pur avendo la libertà di usare variabili di nomi diversi per ogni unità di programma.
14.1 Inizializzare le variabili globali
Le variabili globali (cioè quelle inserite in un COMMON) non possono essere inizializzate con un comando DATA come spiegato nella sezione 8. Infatti, essendo una variabile globale "presente" in due o più sottoprogrammi, si potrebbe per sbaglio assegnarle valori iniziali differenti da più parti, creando così una situazione conflittuale. Per evitare questi problemi, le variabili globali si possono inizializzare solamente in particolari sottoprogrammi chiamati BLOCK DATA. Questi sottoprogrammi contengono esclusivamente istruzioni di specifica di tipo e di dimensione delle varie variabili, istruzioni COMMON e istruzioni DATA. Non possono contenere invece istruzioni eseguibili, come espressioni numeriche, letture, scritture e richiami ad altri sottoprogrammi; inoltre, devono terminare con l? istruzione END, senza però contenere nessun comando STOP o RETURN. La forma generale è la seguente:
BLOCK DATA name
specifiche di tipo e dimensione
COMMON/CM1/ VAR1,VAR2,....
COMMON/CM2/ VAR3,VAR4,....
.....
DATA VAR1,VAR2,.../lista di valori/
DATA VAR3,VAR4,.../lista di valori/
.....
END
In un programma si possono inserire un numero arbitrario di BLOCK DATA.