Fortran 9[/95] M. Bianco ARRAY Un array è una sequenza di variabili di uno stesso tipo identificate da un nome Queste variabili vengono memorizzare in posizioni contigue di memoria Si può accedere a una di queste variabili in modo diretto tramite un indice (o una n-upla di indici) a() a(2) a(3) a(4) a(5) a(6) a(7) Esempio (arraybase.f9) INTEGER, DIMENSION() :: a INTEGER :: i DO i, a(i)2i-!i primi numeri dispari DO i2, a(i)a(i)a(i-)! Somma primi i dispari WRITE (, '(X,"a(",I3,") ",I5)') i, a(i) ARRAY: dichiarazione TIPO, DIMENSION(<const>) :: nome, nome2 <const> deve essere un intero costante o parameter Esempi: integer, dimension() :: a integer, parameter :: max55 real, dimension(max) :: r logical, dimension(4) :: logic complex, dimension(max) :: comp character(6),dimension(8) :: nomi ARRAY: inizializzazione do i, a(i)! Iniz. uno alla volta r.! Tutto r diventa nullo logic(/.true.,.false., &.true.,.false. /) ARRAY: inizializzazione Per gli elementi degli array valgono le stesse regole delle variabili integer, dimension() :: a do i, write (,) a(i)! Valori "casuali" Ogni elemento di un array è una variabile!
ARRAY: rango Gli array possono avere più indici (dimensioni) integer, dimension(,4,5) :: a Il rango di un array è il numero di dimensioni dell array stesso do i, do j,4 do k,5 write (,) a(i,j,k)! Rango 3 ARRAY Gli indici degli array possono avere diversi valori di minimo e massino integer, dimension(3:,-4:5) :: a do i3,, do j-4,5, write (,) a(i,j)! Rango 2 Gli array in memoria Gli array in memoria akind(<tipo>) a() akind(<tipo>) a(2) a2kind(<tipo>) a(3)...... <tipo>, dimension(n)::a Indichiamo con a l indirizzo del primo elemento del array a Indichiamo con KIND(<tipo>) il numero di byte occupati da un elemento di a L i-esimo elemento di a si a(n-2)kind(<tipo>) a(n-) trova nell indirizzo a(i-)kind(tipo) a(n-)kind(<tipo>) a(n) a(,) a(2,) a(3,) a(4,) a(,2) a(2,2) a(3,2) a(4,2) a(,3) a(2,3) a(3,3) a(4,3) <tipo>, dimension(n,m)::a Nell esempio n4 e m3 L elemento in riga i e colonna j (a(i,j)) di a ha indirizzo a((j-)ni-)kind(tipo) Esempio: sia l indirizzo di a(,) sia. Calcoliamo l indirizzo di a(3,2): (42)424 Gli array in memoria Esempio: crivello di Eratostene 4 8 2 6 2 24 28 32 36 4 44 <tipo>, dimension(n,m)::a Nell esempio n4 e m3 L elemento in riga i e colonna j (a(i,j)) di a ha indirizzo a((j-)ni-)kind(tipo) Esempio: sia l indirizzo di a(,) sia. Calcoliamo l indirizzo di a(3,2): (42)424 Metodo per trovare i numeri primi tra 2 e n Osservazione: i multipli di un numero non possono essere primi Se etichetto i numeri multipli di un numero come non-primi non serve valutarne la primalità quando li incontro Se ho già considerato tutti i numeri fino a k e k non è stato marcato come non-primo, posso dire se k è primo o no? 2
Esempio: crivello di Eratostene Metodo per trovare i numeri primi tra 2 e n Osservazione: i multipli di un numero non possono essere primi Se etichetto i numeri multipli di un numero come non-primi non serve valutarne la primalità quando li incontro Se ho già considerato tutti i numeri fino a k e k non è stato marcato come non-primo allora k è primo! (Non è multiplo di nessuno dei numeri precedenti) Esempio dell esempio 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 2,3,4,5,6,7,8,9,,,2,3,4,5,6,7,8,9,2,2,22,23,24,25 Implementazione Fissato n dobbiamo poter marcare i numeri da 2 a n come primi o non-primi Dobbiamo esaminare i numeri in ordine e se incontriamo un numero primo dobbiamo marcare tutti i successivi multipli come numeri primi Se incontro un numero non-primo non faccio nulla Come deve essere la marcatura iniziale? Alla fine stampo solo i numeri marcati Implementazione Usiamo un array LOGICAL per sapere se un numero (di indice) i è primo o no Serve un limite superiore al valore di n per creare questo array integer, parameter :: MAXN22 integer :: n! input al programma:! numeri primi tra 2 e n integer :: i,j! variabili di ciclo logical, dimension(2:maxn) :: mark! mark(i) contiene.true. se i e`! Primo write (,) "Inserire il valore di n" read (,) n if (n>maxn) STOP n troppo alto (>M)" Mark.true.! Inizializzazione di Mark! Esegue il crivello do i2,n! Bastava n/2 if (mark(i)) then! se mark(i) e` vero i e`! primo! do j2i,n,i! marca tutti i multipli di! i<n come non-primi mark(j).false. end if! Ciclo di stampa do i2,n if (mark(i)) write(,)"numero", i, "primo" 3
DO implicito Inizializzare gli array integer, dimension(4) :: v& (/, 3, 56, 5 /) Cosa fare se la dimensione cresce molto? integer :: dimension() :: & v2 (/ (3, i,) /) integer :: dimension() :: & v3 (/ (2i-, i,) /) v() v(2)3 v(3)56 v(4)5 Risultato v2()3 v2(2)3 v2(3)3 v2(998)3 v2(999)3 v2()3 v3() v3(2)3 v3(3)5 v3(4)7 v3(998)995 v3(999)997 v3()999 DO impliciti annidati Si possono annidare più cicli uno nell altro v (/ ((, i,4), 5j, j,5) /) v,,,,5,,,,,,,,,,5, Cercheremo di non usarli Write con do impliciti Write (,) (2i-, i,5) Stampa: 3 5 7 9 Write (,) a Equivale a: Write (,) a(), a(2),, a() Write (,) (a(i), i,,3) Equivale a: Write (,) a(), a(4), a(7), a() Write (,) (a(i), i,,3) FORMAT (X, 4I6) Identico a: Write (,) a(), a(4), a(7), a() INDICI FUORI RANGE integer :: dimension(4) :: v& (/, 3, 56, 5 /) Quanto vale v()?! Può succedere di tutto! Alcuni compilatori fanno il controllo degli indici gfortran e g95 non lo fanno (sono ancora giovani ) Il debug può essere molto difficile!!! Consiglio Definire le dimensioni degli array usando variabili di tipo parameter, a meno che il programma non sia davvero molto semplice Evita di dover cambiare molte righe di codice nel caso in cui si debbano ridimensionare le strutture dati integer, parameter:: max55, min5 real, dimension(min:max) :: r 4
ARRAY: assegnazione Se due array hanno lo stesso rango e gli stessi indici possiamo usare una assegnazione diretta integer, dimension(3:,-4:5) :: a, b integer, dimension(3:9,-2:5) :: c ab! OK! cb! ERRORE DI COMPILAZIONE a e b hanno lo stesso shape (forma), b e c invece no! La forma non cambia se cambiano i range Esempio integer, dimension(3:9) :: a, b integer, dimension(3:8) :: c integer, dimension(:7) :: d abd! OK! dca! ERRORE DI COMPILAZIONE ab! OK! Due array hanno la stessa forma se le varie dimensioni coincidono a e d hanno lo stesso shape ma range diversi Esempio Esempio a(3) b(3) d() d() c(3) a(3) a(4) a(5) b(4) b(5) d(2) d(3) d(2) d(3) c(4) c(5) a(4) a(5) a(6) b(6) d(4) d(4) c(6) a(6) a(7) b(7) d(5) d(5) c(7) a(7) a(8) b(8) d(6) d(6) c(8) a(8) a(9) b(9) d(7) d(7) a(9) Esempio Funzioni di elemento a(3) a(4) a(5) a(6) a(7) a(8) a(9) b(3) b(4) b(5) b(6) b(7) b(8) b(9) Con funzione di elemento si intendono quelle funzioni che possono essere applicate a ogni elemento dei vettori come a valori scalari Le somme e i prodotti delle slide precedenti possono essere visti come dei casi particolari Anche sin(), cos(), abs(), etc. possono essere applicate a vettori bsin(a) calcola i seni di tutti gli elementi di a, dove b deve avere lo stesso shape di a. 5
Esempio integer :: dimension(4) :: v& (/, -3, 56, -5 /) write (,) abs(v)! Stampa 3 56 5 In appendice A del libro, le funzioni indicate come funzione di elemento possono essere applicate ad array! Sottoarray Usando una sintassi simile a molti linguaggi di tipo scientifico (e.g. MATLAB) si possono specificare porzioni di array Sia b<e a(b:e:s) è un array che contiene solo gli elementi i cui indici sono b, bs, b2s,, bks, dove bks<e e b(k)s>e Esempio a(/, 3, 6, 7, 2, 4, 3 /) Write (,) a(2:6:3) stampa 3 2 Write (,) a(2:6) stampa 3 6 7 2 4 Write (,) a(2::2) stampa 3 7 4 Write (,) a(:3) stampa 3 6 Esempio Integer :: i3, j6 a(/, 3, 6, 7, 2, 4, 3 /) Write (,) a(i:j:2) stampa 6 2 Write (,) a(2:j:i) stampa 3 2 Write (,) a(i:) stampa 6 7 2 4 3 Indici vettoriali INTEGER, dimension(5) :: vett & (/, 6, 4,, 9 /) real, dimension() :: a & (/.,., 2., 3., 4., 5.&, 6., 7., 8., 9. /) real, dimension(5) :: b ba(vett) bconterrà: [., 5., 3.,., 8.] Indici vettoriali INTEGER, dimension(5) :: vett & (/, 6, 4,, 9 /) real, dimension() :: a & (/.,., 2., 3., 4., 5.&, 6., 7., 8., 9. /) real, dimension(5) :: b a(vett)b ERRORE IN COMPILAZIONE!! 6
Mark.true.! Inizializzazione di Mark! Esegue il crivello do i2,n! Bastava n/2 if (mark(i)) mark(2i:n:i).false.! Ciclo di stampa do i2,n if (mark(i)) write(,)"numero", i, "primo" Somma dispari In quanti modi si può calcolare la somma dei primi n numeri dispari? Quello che serve in ogni caso è un ciclo che enumeri i numeri dispari per ogni possibile valore di n Se n fosse fissato a priori (non un input al problema) si potrebbe calcolare il risultato senza usare un ciclo: 357 Modo s DO i,n ss2i-! 2i- i-esimo numero dispari Modo 2 s DO i,2n-,2 ssi! i e` l i-esimo numero dispari s i DO ss2i- ii IF (i>n) EXIT Modi 3 e 4 s i DO ii ss2i- IF (in) EXIT s i DO ssi ii2 IF (i>2n-)& EXIT Modi 5 e 6 s s i i- DO ii2 ssi IF (i2n-)& EXIT 7
Modo 7 s i Alternativamente: DO IF ((i/2)2i) THEN ii IF (MOD(i,2)) THEN ssi! I e` dispari!! nn-! Quanti numeri mancano? IF (n) EXIT END IF Modo 8 s n2n-! n-esimo numero dispari DO ssn nn-2 IF (n-) EXIT Modo 9 (sommadispariancora.f9) Modo 9 integer, dimension(5) :: a& (/ (2i-, i,5) /)! (/ (i, i,9,2) /) write (,'(I4,/)') a write (,) '-----' a(2:5)a(2:5)a(:4) write (,('(I4,/)')) a 3 5 7 9 4 8 2 6 3 5 7 3 5 7 9 La somma tra due array non corrisponde a un ciclo DO eseguito elemento per elemento, ma è come se si creassero due array temporanei e se ne calcolasse la somma. Il risultato viene assegnato all array di destinazione! Modo 9 (sommadispariancora.f9) integer, dimension(5) :: a& (/ (2i-, i,5) /)! (/ (i, i,9,2) /) write (,'(I4,/)') a write (,) '-----' a(2:)a(2:)a(:4) write (,('(I4,/)')) a 3 5 7 9 Qual è il valore in r dopo l esecuzione delle seguenti istruzioni se r è REAL? r(2/3)4. 8
Qual è il valore in r dopo l esecuzione delle seguenti istruzioni se r è REAL? r(2/3.)45/3 Qual è il valore in r dopo l esecuzione delle seguenti istruzioni se r è REAL? r(2/3.)45/3 r(2./3.)4 Qual è il valore in r dopo l esecuzione delle seguenti istruzioni se r è REAL? r(2/3.)45/3 r(2./3.)4 r.666 4 Qual è il valore in r dopo l esecuzione delle seguenti istruzioni se r è REAL? r(2/3.)45/3 r(2./3.)4 r.666 4 r.666 4. r2.666. r3.666 Se a vale 6 e b vale -7, quale valore hanno le seguenti espressioni logiche? (a>b).and. (a-b<b-a) (a>b).or. (a-b<b-a) (mod(a,3).and..not.mod(b,3)) Convertire i seguenti numeri in binario 8 2 24 Convertire i seguenti numeri in decimale 9
2 4 9 8 LSB Numero Risultato: 2 5 2 LSB Numero Risultato: 3 6 2 24 LSB Numero Risultato: 52 225 22 LSB Numero Risultato: 3226 623 326 23 LSB Numero Risultato: 26 22 52 225 22 LSB Numero Risultato:2
Variazione Dato un numero b: Si consideri b come base numerica Si fornisca in output un istogramma che dica quante volte la cifra i del sistema numerico in base b appare come ultima cifra di un numero primo (tra 2 e n) Variazione Dato un numero b: Si consideri b come base numerica Si fornisca in output un istogramma che dica quante volte la cifra i del sistema numerico in base b appare come ultima cifra di un numero primo (tra 2 e n) Il valore dell ultima cifra di un numero n in base b è il resto della divisione di n per b MOD(n,b) Implementazione Dato un numero primo se ne calcola il valore dell ultima cifra in base b Il numero di primi con quella cifra finale viene incrementato si uno! Serve un array indicizzato con le cifre finali che contenga il numero di volte in cui una cifra appare come ultima cifra integer, parameter :: MAXN22 integer :: n! input al programma:! numeri primi tra 2 e n integer :: i,j! variabili di ciclo logical, dimension(2:maxn) :: mark! mark(i) contiene.true. se i e`! Primo integer, parameter :: MAXC5 integer, dimension(maxc) :: istogramma! istogramma che dice quante volte! la cifra i appare come ultima! cifra di un numero primo write (,) "Inserire il valore di n" read (,) n if (n>maxr) STOP n troppo alto (>M)" write (,) "Inserire il valore della base numerica" read (,) cifre if (cifre>maxc) STOP "b troppo alto! inizializza l'istogramma:! bastava istogramma do i,cifre istogramma(i)! Esegue il crivello do i2,n! Bastava n/2 if (mark(i)) then! se mark(i) e`! vero i e` primo! do j2i,n,i! marca tutti i! multipli di i<n! come non-primi mark(j).false. end if
! Ciclo di stampa j! Conta i numeri primi trovati do i,n if (mark(i)) then! MOD(i,cifre) valore! dell ultima! cifra in base (cifra) istogramma(mod(i,cifre))& istogramma(mod(i,cifre)) jj end if! stampa il report write (,) "Sono stati trovati", & j, "numeri primi" write (,) "Istogramma:" do i,cifre write (,) "Cifra", i, "appare", istogramma(i), "volte" 2