Array
Gli array Fortran corrispondono a insiemi di variabili identificate da
un nome unico e
da uno o più indici interi. Il numero
di indici viene indicato come il rango (rank) dell' array.
Oltre al numero di indici, un array viene caratterizzato dalla sua misura o taglia (size) e cioè il numero totale di componenti dell'array;
dall' estensione su ciascun indice (ovvero
il numero di valori diversi di quell'indice
); dai valori minimi e massimi dei valori su
ciascun indice (lower e upper bound) e dalla sua forma
(shape) ovvero la sequenza delle estensioni per ciascun indice.
Si possono dichiarare array in corrispondenza di ciascuno dei 5 tipi
dati primitivi. Per farlo, occorre aggiungere l' attributo dimension
alle dichiarazioni.
Ad esempio
integer, dimension(10) :: a,b
real, dimension(-5:5) :: x
real, dimension(0:15) :: y
complex,dimension(12) :: z
logical,dimension(-10:10) :: lg
character(len=80),dimension(20) :: string
Le precedenti dichiarazioni corrispondono tutte ad array con un solo
indice (array di rango 1). La prima corrisponde a due array di interi
di estensione 10, taglia 10, di indici minimo e massimo pari a 1
e 10 e di forma 10; la seconda e la terza a due array di real di
estensione 11 e 16 rispettivamente, pari alla taglia, di indice
inferiore -5 il primo e 0 il secondo e superiore 5 il primo e 15 il
secondo; poi c'è un array di
variabili logiche (estensione 21) ed infine un array di 20 componenti
character, ciascuna delle quali può immagazzinare il valore di
un character lungo 80 caratteri.
Per array di rango superiore (il numero di indici massimo possibile in
Fortran è 15 nello standard 2008, 7 in quelli precedenti, a partire dal 77), si
separano gli intervalli di variabilità
dei diversi indici mediante una virgola (,). Es:
real,dimension(10 , 0:9 , -10:10) :: A
dichiara A come variabile a 3 indici (array di rango 3) , il primo
variabile tra 1 e 10, il
secondo tra 0 e 9 ed il terzo tra -10 e 10. La forma dell' array e':
[10,10,21]
Questo meccanismo di dichiarazione, corrisponde anche alla allocazione
delle corrispondenti locazioni di memoria da parte del compilatore, al
momento della compilazione.
Tuttavia, potrebbe essere più utile rimandare l' allocazione
della memoria al tempo in cui il programma viene eseguito. Soprattutto
se le necessità di memoria possono variare da un' esecuzione
all' altra.
Tale meccanismo (allocazione dinamica) comporta in primo luogo di
modificare la dichiarazione dell' array:
1. al posto dell' estensioni o dei limiti inferiori e
superiori di ciascun indice dell' array, si mettono dei "due punti"
(:), separati da virgole, tanti quanti sono gli indici.
2. Si aggiunge l' attributo allocatable alla dichiarazione
di tipo dati.
Es:
integer,dimension(:,:), allocatable :: a
integer,dimension(:) , allocatable :: b
Quando, nel programma occorrerà allocare memoria, l' istruzione
sarà, p.es.:
allocate(a(N,M),b(-N:N))
Se non fosse disponibile la memoria richiesta, nel momento in cui si cercasse di accedervi si verificherebbe una condizione di errore che farebbe arrestare l'esecuzione del processo.
Se la memoria allocata non servisse più, potrebbe essere
rilasciata esplicitamente mediante il comando
deallocate(a,b)
Va tuttavia tenuto presente che un programma che termina rilascia comunque tutta la memoria al sistema
operativo, anche in assenza di un deallocate
esplicito. Pertanto, di norma la deallocazione esplicita di array viene eseguita solo quando occorre riutilizzare gli stessi nomi di array ma con estensionii e/o limiti diversi.
Con lo standard 2003 è stata introdotta la possibilità di allocazione
e riallocazione automatica:
ogni volta che ad un array allocabile viene assegnata un'espresssione a valori array, se l'array non era allocato o se era allocato con estensiopne diversa, viene (ri0allocato con estensione pari a quella dell' espressione a destra dell'assegnazione.
La memorizzazione di array di rango maggiore di uno avviene "per
colonne". Ovvero l' indice dell' array che varia più velocemente
è quello più a sinistra.
In tal modo, p.es., una matrice viene immagazzinata per colonne. Una
matrice 2x3 avrà dunque gli elementi memorizzati nel seguente
ordine:
a11,a21,a12,a22,a13,a23.
Tenendo presente l' ordine di memorizazione, le istruzioni di
input/output (I/O) possono essere basate sul nome dell' array o usare
le singole componenti o sezioni dello stesso.
Costruttori di array
Se ad un array si intende assegnare lo stesso valore per tutte le
componenti è sufficiente assegnare il valore al nome dell'array.
Es:
x = 1.0
Se invece si intende cambiare i valori si può usare il
cosiddetto costruttore di array: si elencano i valori delle varie
componenti dell'array, separati da virgole e tra (/ e /) oppure
parentesi quadre [ ]. Es:
a = (/ 1, 2, 3, 4, 5 /)
oppure
a = [1,2,3,4,5]
All'interno di un costruttore di array si possono usare cicli do
impliciti:
a= [(i, i =1,10)]
nel' ipotesi che a abbia 10 componenti, o, ancora meglio:
a= [(i, i =1,size(a))]
riempie l'array a con i primi interi consecutivi a partire da 1.
Per array di rango maggiore di uno occorre combinare il costruttore di
array di rango 1 con la funzione intrinseca RESHAPE che riassembla un
array di rango 1 secondo la shape data come secondo argomento. L'
ordine dei dati è quello di default in mancanza di ulteriori
argomenti. Può tuttavia essere presente l' argomento opzionale
order cui va associato un array che contiene l' ordine di
riempimento
delle varie dimensioni dell' array. P.es il seguente programma
inizializza una matrice riempiendo la matrice "per righe" e verificando
che, scritta "per righe" riproduca la matrice originaria.
program ac
implicit none
integer :: i
integer,dimension(3,2) :: x = reshape( &
[ 1, 2 &
, 3, 4 &
, 5, 6 ] , shape(x), order= [2,1] ) ! order vincola il secondo
indice a variare piu' in fretta del primo
! e quindi a riempire prima le righe e poi le colonne
do i=1,size(x,1)
print*,x(i,:)
end do
end program ac
La maggior parte delle funzioni di libreria di tipo numerico, se
chiamate con argomento array ritornano al programma chiamante un array
della stessa forma e le cui componenti sono i valori della funzione nei
corrispondenti punti.
Funzioni intrinseche per manipolazione di array
Esistono di diverse funzioni intrinseche per la manipolazione di array. Diamo qui un elenco delle più utilizzate rinviando alla documentazione del linguaggio per elenchi completi e maggiori dettagli.
-
dot_product(a,b) i due argomenti sono array di rango 1 con la stessa estensione. IL risultato è uno scalare uguale al prodotto scalare dell' algebra lineare (se a e b sono complessi la somma dei prodotti delle componenti coniugate di a per quelle di b).
-
matmul(a,b) moltiplicazione matriciale "righe per colonne". Le forme di a e b devono essere tali che l'estensione della seconda dimensione di a sia pari a quella della prima di b (se a è una matrice di forma [m,k] e b [k,n] il risultato saà una matrice di forma [m,n]). Se a è un array di rango 1 ed estensione m e b una matrice di forma [m,n] o se b è un array di rango 1 ed estensione m e a una matrice di forma [n,m] matmul ritorna rispttivamente il prodotto "a sinistra" vattore per matrice o il prodotto"a destra" matrice per vettore.
- maxval(array) ritorna il massimo valore di un array di interi o real.
- minval(array) ritorna il minimo valore di un array di interi o real.
-
product(array) ritorna il prodotto di tutti gli elementi.
-
sum(array) ritorna la somma di tutti gli elementi.
-
allocated(array) true se l'array risulta allocato, false altrimenti.
-
shape(array) ritorna un array di rango 1 in cui ciascuna componente &egrav; l'estensione di array lungo l'indice corrispondente.
-
lbound(array [,dim]) con un argomento (array) ritorna i valori minimi per ciascun indice. Se appare l'argomento opzionale dim ritorna il solo valore minimo dell' indice sulla corrispondente dimensione.
-
ubound(array [,dim]) con un argomento (array) ritorna i valori massimi per ciascun indice. Se appare l'argomento opzionale dim ritorna il solo valore massimo dell' indice sulla corrispondente dimensione.
-
size(array [,dim]) ritorna la taglia dell'array (il numero di componenti dell'array) se un argomento oppure l'estensione lungo la dimnsione dim se du argomenti.
-
reshape(array, shape [,order=[i1,...,iN]) ritorna un array di forma shape a partire dall' argomento array che deve essere un array di rango 1. shape è un array di rango 1 con l'estensione su ciascuna dimensione del risultato. Se presente, order=[i1,...,iN] è un array di integer che specifica l'ordine di riempimento delle varie dimensioni.
-
spread(array, dim, ncopies) "clona" l'array primo argomento ncopies volte al vaariare dell' indice lungo al dimensione dim.
-
transponse(matrix) ritorna la matrice trasposta (matrix deve essere un array di rango 2).
-
maxloc(array [,dim]) con un argomento ritorna un array di rango 1 di integer in numero pari al rango di array. Corrisponde ai valori degli indici in corrispondenza dei quali c'è l'elemento massimo dell' array. Se c'è più di un massimo la posizione del primo elemento nell' ordine di riempimento dell'array.
-
minloc(array [,dim]) con un argomento ritorna un array di rango 1 di integer in numero pari al rango di array. Corrisponde ai valori degli indici in corrispondenza dei quali c'è l'elemento minimo dell' array. Se c'è più di un minimo la posizione del primo elemento nell' ordine di riempimento dell'array.