• Aucun résultat trouvé

Tableaux

Dans le document Licence de physique L3 PHYTEM (Page 26-30)

a chaque fois que l’on tape un nombre n´egatif, l’on obtien-dra la requˆete d’un nombre positif sans que le calcul de la racine ne soit fait ; en revanche, si l’on entre un nombre positif, on en obtiendra la racine, et enfin si ce nombre est nul (strictement : on verra un peu plus loin ce que cela signifie) on sort de la boucle.

Autre exemple : v´erifier qu’un fichier existe d´ej`a avant de tenter de l’ouvrir en lecture,

! declaration d’une chaine de 40 caracteres

! qui devra contenir le nom du fichier character (len=40) :: n_fic ! nom de fichier logical :: ok ! variable logique do

write(*,’(‘‘Nom du fichier de donnees : ‘‘,$)’) read(*,*) n_fic ! lire au clavier le nom du fichier

! inquire est une commande FORTRAN permettant de

! verifier l’existence du fichier

inquire(file=n_fic,exist=ok) ! existe: ok = .TRUE.

if ( ok ) exit ! et on n’insiste pas

! sinon, il faut recommencer

write(*,’(‘‘Fichier non trouve’’)’) enddo

open(10,file=n_fic) ! ouverture du fichier ...

Les boucles infinies, assorties deexitetcycle, tendent

`a remplacer le do while, souvent consid´er´e comme ob-sol`ete. Le untildupascaln’existe pas enfortran: de fait, exit et cycle sont beaucoup plus riches, puisqu’ils peuvent ˆetre plac´es n’importe o`u dans la boucle, et non seulement au d´ebut comme while ou `a la fin comme until23.

Si les boucles ont un nom, exit et cycle en tiennent compte :

l1 : do ...

l2 : do ...

if ( age_du_capitaine < 0 ) exit l1 ...

if ( ok ) cycle l2 ...

enddo l2 ...

enddo l1

Leexit l1fait sortir de la bouclel1, la plus externe, alors que l’instruction se trouve dans la boucle la plus interne :

¸ca permet de sauter par dessus un niveau d’imbrication.

23. L’int´erˆet de ces structures est r´eel, toutefois pour relativiser, notons qu’enfortranIV, on pouvait tr`es bien fabriquer des logiques similaires `a ce que permettentexitetcycle, l’esprit ´etait simple-ment moins orient´eprogrammation par blocet il fallait s’as-treindre `a un minimum de discipline, par exemple :

do 100 i = 1, 1000000 ...

c ici l’equivalent d’un ‘‘cycle’’

if ( x .gt. y ) go to 100 ...

c ici l’equivalent d’un ‘‘exit’’

if ( z .le. 0. ) go to 200 ...

100 continue 200 ...

Il ´etait prudent d’´eviter de placer l’´etiquette 200 `a l’autre bout du programme, du moins si l’on avait l’ambition d’´ecrire un code

maintenable. . .

2.7.3 Boucles implicites.

Plutˆot qu’´ecrire : do i = 1, n

write(1,*) x(i) enddo

on peut ´ecrire :

write(1,*) ( x(i), i = 1, n )

La diff´erence est que dans le deuxi`eme cas tous les ´el´ements du tableau24 sont ´ecrits sur la mˆeme ligne, sauf si on le pr´ecise par format. Plus concis encore, sixest un tableau : write(1,*) x

´ecrit tout le tableau.

On peut faire la mˆeme chose avecread.

2.8 Tableaux.

Les tableaux de nombres sont l’´equivalent informatique des vecteurs et des matrices : on peut ainsi d´esigner plusieurs nombres par une seule variable ; le vecteur V de dimension n d´esigne l’ensemble de ses n ´el´ements vi, i ∈ [1, n], la matrice A de dimension (n, m) d´esigne tous lesn×m´el´ementsaij, le tenseurTde rang 3 d´esigne tous les ´el´ementstijk, et ainsi de suite. Comme dans ces exemples, un ou plusieurs indices entiers servent `a indiquer l’´el´ement voulu du tableau. Avec les tableaux, on acquiert la capacit´e `a traiter, comme s’il s’agissait d’une seule en-tit´e, un grand nombre de donn´ees, pour ainsi dired’un coup!

2.8.1 D´ eclaration

La d´eclaration d’un tableau est faite en indiquant les valeurs extrˆemes des indices de chacune des dimensions du tableau, par exemple :

real, dimension(1:100) :: x

d´eclare un tableau `a un seul indice qui va de 1 `a 100,

c’est-`

a-dire un tableau unidimensionnel de r´eels `a 100 ´el´ements ou un vecteur `a 100 coordonn´ees si l’on pr´ef`ere. Le i-`eme

´el´ement est d´esign´e parx(i);iest bien entendu un entier.

Par exemple :

real, dimension(0:100) :: x real :: pi = acos(-1.0) integer :: i

do i = 0, 100

x(i) = cos(i*pi/100.) enddo

remplit un tableau de 101 ´el´ements avec les valeurs prises par cosθavecθ∈[0, π] par pas deπ/100.

On peut faire des d´eclarations du genre : real, dimension(-100:100) :: x

ce qui donne dans ce cas un tableau `a 201 ´el´ements dont l’indice varie de -100 `a 100, et alors, on peut acc´eder `a des

´el´ements comme par exemple x(-20).

24. voir les tableaux dans la section suivante.

La d´eclaration real, dimension(100) :: x est

´equivalente `a real, dimension(1:100) :: x25.

Pour les tableaux `a plusieurs dimensions, il faut sp´ecifier les valeurs extrˆemes de chacun des indices :

integer, dimension(1:25,-2:2) :: a

d´eclare un tableau `a deux dimensions d’entiers de 25×5.

Le premier indice va de 1 `a 25, le deuxi`eme de -2 `a 2.

Un ´el´ement est donc d´esign´e, par exemple, para(i,j)ou a(12,-1).

L’exemple suivant :

integer :: i

complex, dimension(200) :: s

complex :: im = (0.,1.)

real :: omega, t

omega = 2*acos(-1.0) do i = 1, 200

t = (i-1)*0.005 ; s(i) = exp(im*omega*t) enddo

d´eclare un tableau complexe de 200 ´el´ements et le remplit.

Il peut ˆetre commode de remplacer une dimen-sion explicitement fix´ee `a une certaine valeur, du type dimension(100), par dimension(n) o`u nest un nombre entier. Il faut alors d´eclarer ncomme un param`etre,

c’est-`

a-dire un nombre dont on ne peut pas changer la valeur en cours d’ex´ecution du programme. Ainsi :

integer, parameter :: l = 20, m = 100, n = 30 real, dimension(l,m) :: x

real, dimension(m,n) :: y real, dimension(l,n) :: z

integer :: i, j, k

open(10,file=’fichier.x’) ! lecture x do j = 1, m

do i = 1, l

read(10,*) x(i,j) enddo

enddo close(10)

open(10,file=’fichier.y’) ! lecture y do j = 1, n

do i = 1, m

read(10,*) y(i,j) enddo

enddo close(10)

do j = 1, n ! produit matriciel do i = 1, l

z(i,j) = 0.

do k = 1, m

z(i,j) = z(i,j) + x(i,k)*y(k,j) enddo

enddo enddo

d´eclare 3 tableaux de 20×100, 100×30 et 20×30, lit les valeurs des ´el´ements de xet y dans des fichiers et fait le produit des deux matrices dansz(attention `a la diff´erence entre le chiffre 1et la lettrel).

Malgr´e son apparente complexit´e, l’avantage de cette fa¸con de d´eclarer les tableaux est que si l’on veut, dans une nouvelle version du programme, changer une dimension d’un tableau, il suffit de changer la valeur du param`etre

25. `a la diff´erence du langageCpour lequel les tableaux commen-cent avec l’´el´ement0.

correspondant et toutes les boucles s’adaptent automa-tiquement.

Finalement, il est possible d’initialiser un tableau dans la d´eclaration, comme un scalaire :

real, dimension(-2:2) :: x=(/-4,-2,0,2,4/)

ici, x(-2) = -4, x(-1) = -2, etc. Une boucle implicite peut aussi ˆetre utilis´ee pour le mˆeme r´esultat :

integer :: i

real, dimension(-2:2) :: x=(/ (2*i, i=-2,2) /)

2.8.2 Manipulation globale de tableaux

2.8.2.1 nd’un coup ! (mieux que le petit tailleur) On peut faire des calculs en manipulant les ´el´ements de tableaux un `a un dans des boucles comme dans les exem-ples ci-dessus ; c’est d’ailleurs ce que l’on faisait jusqu’`a l’apparition de fortran9026. La manipulation globale de tableaux est sans doute l’apport le plus important de fortran90 par rapport aux langages qui l’ont pr´ec´ed´e ; c’est aussi un ingr´edient qui facilite beaucoup les calculs num´eriques d`es qu’ils deviennent un peu lourds.

Admettons que les ´el´ements du tableauc doivent ˆetre la somme des ´el´ements correspondants des tableaux aet b:

integer, parameter :: n = 1000

integer :: i

real, dimension(n) :: a, b, c

!....

! il faudrait lire les elements de a et b

! dans un fichier par exemple.

do i = 1, n

c(i) = a(i) + b(i) enddo

En fortran90, la boucle peut se r´esumer en une seule instruction :

c = a + b ! ici on fait n additions.

le compilateur se chargeant de v´erifier que tous les tableaux concern´es ont bien la mˆeme dimension. . . On peut donner explicitement les bornes :

c(1:n) = a(1:n) + b(1:n)

ce qui a le mˆeme r´esultat que ci-dessus : ¸ca n’a au-cun int´erˆet dans cet exemple, mais imaginons que l’on veuille additionner la premi`ere moiti´e du tableauaavec la deuxi`eme moiti´e du tableaubet inversement, la deuxi`eme moiti´e deaavec la premi`ere deb:

c(1:n/2) = a(1:n/2) + b(n/2+1:n) c(n/2+1:n) = a(n/2+1:n) + b(1:n/2)

ou bien que l’on ne s’int´eresse qu’aux ´el´ements impairs : c = 0. ! initialisation de c

c(1:n:2) = a(1:n:2) + b(1:n:2)

le dernier indice ´etant le pas, comme pour une boucledo27.

26. Plus pr´ecis´ement, certains constructeurs d’ordinateurs `a ca-pacit´e vectorielle avaient commenc´e `a produire dans les ann´ees 1980 des compilateurs fortran8X sp´ecifiques `a leurs machines : for-tran90est l’h´eritier de ces innovations.

27. Il faut toutefois faire un peu attention car la norme ne garan-tit pas que les op´erations effectu´ees dans un traitement global de tableau se fassent dans l’ordre des indices de sorte que si un calcul epend d’un r´esultat obtenu pr´ec´edemment sur un ´el´ement d’indice inf´erieur, il faut ´ecrire explicitement une boucle. En revanche, cer-tains ordinateurs sont optimis´es pour ce genre de calcul et un traite-ment global, quand il est possible, peut se traduire par un gain de temps sensible.

28 Licence de physique L3 : PHYTEM, Universit´e Pierre et Marie Curie Paris-6 & ENS-Cachan

2.8.2.2 where

Finalement, les conditions s’expriment `a l’aide dewhere avec une syntaxe tr`es proche du if, sauf qu’´evidemment il s’agit maintenant de tableaux pris globalement : integer, parameter :: n = 5000

real, dimension(n) :: x, y

! ....

where ( x >= 0.) y = sqrt(x) else where

y = 0.

end where

on calcule donc les racines carr´ees de tous les ´el´ements positifs du tableau x, et l’on annule y pour les ´el´ements n´egatifs de x: c’est l’´equivalent de la boucle :

integer, parameter :: n = 5000 real, dimension(n) :: x, y

integer :: i

! ....

do i = 1, n

if ( x(i) >= 0.) then y(i) = sqrt(x(i)) else

y(i) = 0.

endif enddo

2.8.2.3 forall

Imaginons toutefois que l’on veuille, par exemple, cr´eer la matrice identit´e d’ordren:

I=









1 0 0 . . . 0 0 0 1 0 . . . 0 0 0 0 1 . . . 0 0 ... ... ... . .. ... ...

0 0 0 . . . 1 0 0 0 0 . . . 0 1









il faut pouvoir acc´eder aux termes diagonaux de la ma-trice : une boucle do peut faire l’affaire, mais celle-ci impose que les op´erations demand´ees soient ef-fectu´ees s´equentiellement dans l’ordre indiqu´e par l’indice ; sur certains ordinateurs cela peut sensiblement ralentir l’ex´ecution. L’instruction forall est tr`es similaire `a do mais l`eve cette contrainte, ainsi :

real, dimension(n,n) :: id ! matrice identite ...

id = 0. ! mise a zero des n*n termes forall ( i = 1:n ) id(i,i) = 1.0 ! n termes diagonaux Noter que la syntaxe est un peu diff´erente que celle dedo.

On a aussi : forall ( i = 1:n )

...

end forall

2.8.3 Fonctions intrins` eques et tableaux

Les fonctions intrins`eques du fortrans’accommodent tr`es bien de tableaux, par exemple :

integer, parameter :: n = 5000 real, dimension(n) :: x, y

! ....

y = sin(x)

c’est, encore une fois, l’´equivalent d’une boucle carxet y ne sont pas des scalaires mais des tableaux de nombres et, en une ligne, est concentr´e un calcul de 5000 sinus. . .

fortran90 offre en outre une panoplie de fonctions intrins`eques sp´ecialement destin´ees aux op´erations sur les tableaux, par exemple, pour obtenir la somme des

´el´ements d’un tableau :

integer, parameter :: n = 2048 real, dimension(n) :: x

real :: y

! ....

y = sum(x)

ou de fa¸con plus sophistiqu´ee, la somme des ´el´ements posi-tifs d’un tableau :

integer, parameter :: n = 6723 real, dimension(n) :: x

real :: y

! ....

y = sum(x, mask = x >= 0.)

ce qui suitmaskdevant ˆetre un tableau logique de la bonne dimension. La moyenne des racines carr´ees donnerait : y = sum(sqrt(x), mask = x >= 0.)/count(x >= 0.) La fonction intrins`eque count compte le nombre de fois o`u la condition est vraie.

Si le tableau est multidimensionnel, on sp´ecifie la di-mension sur laquelle la somme doit ˆetre faite :

integer, parameter :: n = 237, m = 53 real, dimension(n,m) :: x

real, dimension(m) :: y

! ....

y = sum(x, dim = 1, mask = x >= 0.)

donne : yj = Xn

i= 1 xi,j0

xi,j, ∀j ∈ [1, m], le r´esultat est donc

´evidemment un tableau. On peut ´ecrire de fa¸con plus suc-cinte :

y = sum(x, 1, x >= 0.)

La fonctionproduct, d’usage similaire, fait le produit des ´el´ements d’un tableau.

Le produit scalaire z = Xn i=1

xiyi s’obtient avec dot product:

integer, parameter :: n = 8000 real, dimension(n) :: x, y

real :: z

! ....

z = dot_product(x,y)

et le produit de deux matrices ci,j = Xm k=1

ai,kbk,j avec matmul:

integer, parameter :: n = 100, m = 50, p = 32 real, dimension(n,m) :: a ! attention au real, dimension(m,p) :: b ! dimensionnement real, dimension(n,p) :: c ! des tableaux

! ....

c = matmul(a,b)

Nom Type Description Fonctions math´ematiques

count(l,mask,dim) entier nombre de fois o`u le tableau logiquelest vrai, selon la dimensiondimquandmaskest vrai

dot product(a,b) num´erique, mˆeme type queaetb produit scalaire des tableaux unidimensionnelsaetb matmul(a,b) num´erique, mˆeme type queaetb produit des deux matricesaetb

maxloc(a,dim,mask) entier position du plus grand ´el´ement du tableauaselon la dimensiondimquandmaskest vrai.

maxval(a,dim,mask) entier ou r´eel valeur du plus grand ´el´ement du tableauaselon la dimensiondimquandmaskest vrai.

minloc(a,dim,mask) entier position du plus petit ´el´ement du tableauaselon la dimensiondimquandmaskest vrai.

minval(a,dim,mask) entier ou r´eel valeur du plus petit ´el´ement du tableauaselon la dimensiondimquandmaskest vrai.

product(a,dim,mask) num´erique, du mˆeme type quea produit des ´el´ements du tableauaselon la dimensiondim quandmaskest vrai

sum(a,dim,mask) num´erique, du mˆeme type quea somme des ´el´ements du tableauaselon la dimensiondim quandmaskest vrai

Transformations de tableaux

cshift(a,shift,dim) indiff´erent permutation circulaire des ´el´ements du tableauaselon la dimensiondim. Sishiftest positif, d´eplacement de shiftpositions `a gauche, sinon `a droite

eoshift(a,shift, indiff´erent d´ecalage des ´el´ements du tableauaselon la dimensiondim.

boundary,dim) Sishiftest positif, d´eplacement deshiftpositions `a gauche, sinon `a droite. Les ´el´ements manquants `a l’extr´emit´e sont remplac´es par ceux deboundary, ou z´ero siboundaryabsent.

pack(a,mask, indiff´erent transforme le tableau multidimensionnelaen un tableau vector) unidimensionnel quandmaskest vrai, les ´el´ements filtr´es

parmask´etant remplac´es par ceux du vecteurvector unpack(a,mask, indiff´erent transforme le tableau unidimensionnelaen un tableau missing) multidimensionnel quandmaskest vrai, les ´el´ements filtr´es

parmask´etant remplac´es par ceux demissing. La forme du tableau r´esultant est celle demask

Table 2.8 – Quelques fonctions de manipulation globale de tableaux.

Noter que le r´esultat fourni par cette fonction est lui-mˆeme une matrice.

Un r´esum´e (non exhaustif) de quelques fonctions que l’on est amen´e `a utiliser assez souvent est donn´e dans la table 2.828.

2.8.4 Allocation dynamique de m´ emoire

Une d´eclaration est, comme on l’a d´ej`a vu, une r´eservation d’espace dans la m´emoire vive de l’ordinateur.

Ainsi, real :: x d´eclare une variable r´eelle x et, de ce fait, r´eserve 4 octets (ou 32 bits) en m´emoire, parce que c’est la place qu’occupe un r´eel en simple pr´ecision29: ces 4 octets sont donc affect´es `a cette variable et ne peuvent pas ˆetre utilis´es `a autre chose. De mˆeme, lorsqu’on d´eclare un tableau, par exemple real, dimension(n) :: x, on r´eserve n fois 4 octets. On comprend donc que cette r´eservation doive ˆetre faite avant l’ex´ecution du pro-gramme : si l’on veut changer la taille d’un tableau, cela revient `a modifier la taille de l’espace qu’il occupe en m´emoire ; il faut arrˆeter l’ex´ecution du programme, mod-ifier le code source, recompiler et relancer l’ex´ecution30.

Il existe cependant des quantit´es de situations dans

28. NE PAS apprendre ce tableau par cœur !

29. pour la plupart des ordinateurs courants. L’usage de mots de 64 bits commence toutefois `a se r´epandre assez rapidement.

30. ou alors, enfortran77, en l’absence d’allocation dynamique de m´emoire, on pr´evoyait large : on r´eservait par exemple 1 000 000 d’emplacements pour n’en utiliser finalement que quelques uns.

lesquelles on aimerait pouvoir modifier la taille d’un tableau en cours d’ex´ecution : par exemple, si l’on a

´ecrit un programme qui calcule la moyenne des ´el´ements d’un tableau de nombres de longueur quelconque, il serait agr´eable de pouvoir entrer la taille du tableau, puis le di-mensionner, puis faire le calcul sans avoir `a recompiler le programme `a chaque fois que le nombre de nombres dont on veut la moyenne a chang´e !

L’allocation dynamique de m´emoire permet de d´eclarer un tableau sans en donner la taille, puis de lui donner une taille en cours d’ex´ecution, de le supprimer puis de lui donner une autre taille, etc. Par exemple :

! declaration d’un tableau unidimensionnel

! sans taille fixe

real, dimension(:), allocatable :: x integer :: n

!...

write(*,’(‘‘Entrer le nombre d’elements ‘‘,$)’) read(*,*) n

! affectation d’une taille donnee au tableau allocate(x(n))

!... ici on fait des calculs deallocate(x) ! desaffectation

! on recommence avec un tableau plus petit

! qui commence en zero au lieu de un allocate(x(0:n/2-1))

!...

deallocate(x)

On g`ere ainsi la m´emoire occup´ee pour ainsi dire au

30 Licence de physique L3 : PHYTEM, Universit´e Pierre et Marie Curie Paris-6 & ENS-Cachan

vol , ou plutˆot, dynamiquement dans le jargon con-sacr´e. . .

2.9 Fonctions et

Dans le document Licence de physique L3 PHYTEM (Page 26-30)