• Aucun résultat trouvé

Fonctions et sous-programmes

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

Rien n’oblige `a ´ecrire tous les calculs que l’on veut faire dans un mˆeme programme : c’est mˆeme assez d´econseill´e ! Pour des raisons, ne serait-ce que de lisibilit´e, on a tr`es souvent int´erˆet `a d´ecouper un gros probl`eme en plusieurs petits pour lesquels les possibilit´es de se tromper sont plus faibles31. De plus, si le mˆeme type de calcul doit ˆetre r´ep´et´e `a plusieurs endroits du programme (par exemple un calcul d’int´egrale), ce n’est pas la peine de le repro-grammer plusieurs fois. Si, enfin, on a d´ej`a ´ecrit (et test´e) un programme qui sait faire un certain type de calcul (par exemple, calculer une int´egrale num´erique ou r´esoudre un syst`eme lin´eaire d’´equations), il est utile de pouvoir le r´ecup´erer et l’ins´erer dans le programme que l’on est en train de faire sans avoir `a le re´ecrire. Il existe en outre, et l’on verra dans ce cours un certain nombre d’exemples de leur utilisation, des biblioth`eques de sous-programmes et de fonctions extrˆemement riches, telleslinpack,lapack, nag,imsl. . ., dans lesquelles on peut puiser sans vergogne et ainsi ´eviter de r´einventer la roue.

Fonctions et sous-programmes sont donc des ingr´edients essentiels !

2.9.1 Les function.

2.9.1.1 D´efinir unefunction.

On peut ´ecrire ses propres fonctions en plus des fonc-tions intrins`eques. On les utilise de la mˆeme fa¸con que les fonctions intrins`eques mais ´evidemment, il faut aussi les d´efinir, alors que les fonctions intrins`eques sont bien sˆur pr´ed´efinies. Pour cela, on place les instructions corre-spondantes, par exemple dans le mˆeme fichier-source que le programme lui-mˆeme, mais en dehors de celui-ci (avant program ou apr`es end. Par exemple, supposons que l’on veuille calculer le barycentre, `a une dimension, d’un en-semble de nombres lus dans un fichier :

program calcul ! debut du programme principal implicit none

integer, parameter :: n = 100 real, dimension (1:n) :: x

real :: bsom

open (1, file=’machin’) read(1,*) x

close(1)

write(*,*) ’Le barycentre des elements de X est : ’ write(*,*) bsom(x,n) ! appel de bsom end ! fin du programme principal real function bsom(z,m) ! debut de la function bsom implicit none

integer :: i, m

real, dimension(1:m) :: z

31. c’est ce que l’on appelle avec quelque p´edanterie l’analyse descendante, c’est-`a-dire que l’on divise un probl`emeP a priori compliqu´e en plusieurs sous-probl`emesP1, P2, . . ., puis chaque sous-probl`eme est redivis´e enP1,1, P1,2, . . . , P2,1, . . ., etc. On arrˆete quand tous les sous-sous-sous-. . .-probl`emes sont tellement ´el´ementaires qu’il n’y a plus de probl`eme !

real :: s

s = 0.

do i = 1, m s = s + i*z(i) enddo

bsom = s/sum(z)

end ! fin de la function bsom

La premi`ere partie est ce qu’on appelle leprogramme principal, la seconde, la fonction32. L’appel de la fonc-tion dans le programme principal est fait normalement, ici dans une instruction

write(*,*) bsom(x,n)

mais cela pourrait ˆetre dans une expression comme r = sqrt(2.0)*bsom(x,n) - k

l’usage d’une fonction est donc tr`es similaire `a celle des fonctions intrins`eques dufortran, commesin,exp, etc.

Dans la d´efinition de la fonction, on doit ´ecrire explicite-ment l’expression donnant la valeur de la fonction : si l’on oublie l’instructionbsom = s/sum(z), il n’y aura pas d’erreur d´etect´ee, le calcul sera fait normalement, mais le r´esultat ne sera pas transmis au programme appelant. La fonction doit se terminer par l’instructionendqui en mar-que la fin, comme le programme principal.

2.9.1.2 Les arguments et les variables d’une function.

Dans cet exemple, la fonction r´eelle bsom calcule le barycentre des ´el´ements de z. Le tableau z est un argu-ment de la fonction (function bsom(z,m)). Dans l’appel debsom, le premier argument est le tableauxqui doit avoir lemˆeme typeet lemˆeme nombre d’´el´ements quezdans la d´efinition de la fonction : la correspondance entre les vari-ables du programme principal et celles de la fonction est donn´ee par l’ordre dans lequel elles apparaissent dans l’ap-pel et la d´efinition de la fonction. En effetxest la premi`ere variable qui apparaˆıt dans l’appel de bsom(x,n) dans le programme principal etzest la premi`ere dans la d´efinition real function bsom(z,m). Ainsi, x dans le programme principal etzdans la fonction d´esignent la mˆeme variable ; de mˆeme pournetm.

1er, 2e argument

write(*,*) bsom( x, n)appel, dans p. princ.

l l

correspondance real function bsom( z, m)d´efinition

Attention ! Ce n’est pas parce qu’une variable a le mˆeme nom dans une fonction qu’une autre dans le programme principal qu’il s’agit de la mˆeme chose ; au contraire, ces deux variablesmˆeme si elles ont le mˆeme nom n’ont au-cun rapport ! En revanche, une variable du programme principal repr´esente strictement la mˆeme chose qu’une autre, mˆeme de nom diff´erent, dans la fonction `a condi-tion d’avoir ´et´e transmise comme argument dans l’appel et la d´efinition de la fonction comme dans l’exemple ci-dessus33.

Ainsi, la variable x du programme principal est iden-tique `a la variablez de la fonction, mais la variable sde

32. ou plus pr´ecis`ement la d´efinition de lafunction.

33. La seule chose qui soit transmise est l’adresse du premier

´el´ement du tableaux. C’est en fait unpointeur masqu´e !

la fonctionbsomest compl`etement ind´ependante de ce qui peut ˆetre d´eclar´e dans le programme principal : il pourrait tr`es bien y avoir une autre variablesdans le programme principal sans qu’il y ait la moindre interaction entre les deux.

Lorsqu’on apprend `a programmer, c’est une source d’er-reur classique et cela paraˆıt inutilement compliqu´e, mais

`a l’usage c’est un grand avantage car ainsi la fonction bsom, une fois ´ecrite et test´ee, est ind´ependante du cadre dans lequel elle est utilis´ee, on peut donc s’en servir dans un grand nombre de circonstances sans se pr´eoccuper du d´etail de la fa¸con dont elle est ´ecrite. Par exemple, on peut tr`es bien avoir oubli´e qu’il y a des variables s et z dans bsom, et faire dans le programme principal un appel du type :

z = bsom(s,m)

sans cons´equence dramatique, `a condition que, dans le pro-gramme principal,ssoit un tableau dem´el´ements etzun scalaire r´eel.

Il faut donc se rappeler qu’en fortran, il n’y a pas de variables globales, toutes les variables doivent ˆetre ex-plicitement transmises de programme appelant `a fonction ou sous-programme.

2.9.2 Et les subroutine.

Une fonction souffre de la limitation qu’elle ne peut pro-duire qu’une seule valeur comme r´esultat : ainsi, si l’on veut calculer plusieurs choses, il faut faire un sous-pro-gramme (subroutine). Par exemple, si l’on veut calculer, dans l’espace `a trois dimensions, le barycentre d’un en-semble de masses (donc trois nombres) ainsi que la masse totale (un quatri`eme nombre), on peut proc´eder comme suit :

program cmasse implicit none

integer, parameter :: n = 100

real, dimension(1:n) :: masse, rx, ry, rz

real :: ogx, ogy, ogz, mtot

integer :: i

open(1, file=’masses’) do i = 1, n

read(1,*) rx(i), ry(i), rz(i), masse(i) enddo

close(1)

call baryc (ogx,ogy,ogz,mtot,rx,ry,rz,masse,n) write(*,*) ’Masse totale : ’, mtot

write(*,*) ’Coordonnees du centre de masse : ’ write(*,*) ogx, ogy, ogz

end

subroutine baryc( gx, gy, gz, mt, x, y, z, m, n ) implicit none

integer :: n

real :: gx, gy, gz, mt

real, dimension(1:n) :: m, x, y, z mt = sum(m)

gx = dot_product(x,m)/mt gy = dot_product(y,m)/mt gz = dot_product(z,m)/mt

end

Le sous-programmebarycutilise les donn´eesrx,ry,rz, etmasse, ainsi que le nombre de masses impliqu´ees, pour rendre les coordonn´ees du centre de masse et la masse totale.

Noter la diff´erence avec une fonction : on peut ins´erer l’appel d’une fonction dans une expression,

´ecrire : write(*,*) bsom(x,n)/pi ou bien : xtot = 3.*bsom(x,n) + w, mais un sous-programme doit ˆetre appel´e par un call. Par ailleurs, une fonction doit ˆetre d´eclar´ees car elle a un type : entier, r´eel, double pr´ecision, logique. . . en revanche, un sous-programme ne doit pas ˆetre d´eclar´e mais bien sˆur, ses arguments, eux, doivent l’ˆetre, car un sous-programme ne prend pas de valeur, mais se borne `a modifier certains de ses arguments.

Les autres remarques concernant l’ind´ependance des variables d’une fonction vis-`a-vis de celles du programme principal et le dimensionnement des tableaux transmis comme argument sont aussi valables dans le cas d’un sous-programme.

2.9.3 L’intention

On voit, dans l’exemple ci-dessus par exemple, que cer-tains arguments sont des donn´ees que lasubroutinedoit utiliser pour faire ses calculs (m, x,y, z, n), d’autres (mt, gx,gy,gz) des r´esultats qu’elle doit calculer : les premiers sont doncen entr´ee, les autresen sortie, sans que rien dans la syntaxe n’indique cette diff´erence. C’est ici qu’intervient l’id´ee d’intention ouintent en anglais.

On sp´ecifie, dans les d´eclaration du sous-programme, si un argument est en entr´ee ou en sortie, ainsi :

subroutine baryc( gx, gy, gz, mt, x, y, z, m, n ) implicit none

integer, intent(in) :: n

real,intent(out) :: gx, gy, gz, mt real, dimension(1:n), intent(in) :: m, x, y, z mt = sum(m)

gx = dot_product(x,m)/mt gy = dot_product(y,m)/mt gz = dot_product(z,m)/mt end

Cela a deux cons´equences : 1oc’est plus clair pour le pro-grammeur, 2ole compilateur d´etectera une erreur si, par exemple, on essaie de modifier un argument d´eclar´e in.

L’intent(inout) existe aussi, pour les arguments dont on doit utiliser la valeur en entr´ee, mais que l’on doit

´egalement modifier.

2.9.4 La mise en commun de variables.

On a vu que fonctions et sous-programmes ´etaient `a peu pr`es ´etanches vis-`a-vis du monde ext´erieur, `a l’excep-tion, ´evidemment, des variables transmises comme argu-ments : c’est un avantage, mais, c’est parfois un peu rigide.

On peut alors mettre en commun des variables entre pro-gramme et sous-propro-gramme ou entre sous-propro-grammes ou fonctions diff´erents, `a l’aide de modules. Imaginons, par exemple, un code comprenant un programme principal et des sous-programmes qui utilisent tous la grandeur π et les conversions entre degr´es et radians. Plutˆot que red´efinir

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

ces grandeurs dans chaque programme et sous-programme, on peut commencer par faire un module, plac´e au d´ebut du fichier source :

module trig_consts

! definition de constantes trigonometriques implicit none

real, parameter :: pi = acos(-1.0) real, parameter :: deg_rad = pi/180.

real, parameter :: rad_deg = 180./pi end

il s’agit simplement de d´eclarations (ici de param`etres, mais ce n’est pas obligatoire) et, le cas ´ech´eant, d’affecta-tions de valeurs. Dans un programme ou sous-programme dans lequel on veut utiliser ces variables, il faut sp´ecifier au d´ebut des d´eclarations

use trig_consts

et les variablespi,deg radetrad degsont utilisables sans autre forme de proc`es ; ainsi, sitheta est un angle donn´e en degr´es :

sin_theta = sin(deg_rad*theta)

est correct mˆeme si deg rad n’a pas ´et´e d´eclar´e dans le programme. Quand on fait cela, il faut faire un peu atten-tion, puisque si l’on a une autre variable pi dans un des programmes qui utilisent ce module, il y aura un conflit d´etect´e par le compilateur. Cependant, comme il faut faire appel explicitement par use aux modules dont on a be-soin dans chaque sous-programme qui l’utilise, les d´egats potentiels restent limit´es34 : on peut tr`es bien imaginer des structures comme :

module blabla

! acceleration de la pesanteur terrestre real, parameter :: g=9.81

end

program truc

use blabla ! rappel du module blabla implicit none

real :: p, m = 75 p = m*g

write(*,*)’Poids =’,p call machin(p) end

subroutine machin(poids)

implicit none ! ici pas de module blabla real, intent(in) :: poids

real :: g= 2.

write(*,*) ’Poids a’,g ,’ g =’, poids*g end

La variablegdemachinn’a rien `a voir avec celle du mod-ule et du programme principal, puisque machin n’utilise pas le module blabla(il n’y a pasuse blabla).

Outre la d´efinition de constantes, un des usages les plus courants des modules est quand un programme principal appelle un sous-programme A qui lui-mˆeme en appelle un autre B. A priori, les seules variables connues du sous-programme B sont celles que lui a transmises A, or il se peut tr`es bien qu’il ait besoin d’autres grandeurs utilis´ees

34. De ce point de vue, on reste assez loin des variables globales duCdont les auteurs du langage disent eux-mˆemes que ce n’est pas ce qu’ils ont fait de mieux ! (B. W. Kernighan, D. M. Ritchie, Le langage C, Masson (1997), p. 33.)

par le programme principal. Si l’on ne veut pas modifier A (si c’est un programme compliqu´e surtout si on ne l’a pas

´ecrit soi-mˆeme, c’est toujours un peu dangereux), il suffit de faire un module pour mettre ces variables en commun entre le programme principal et B : on saute, pour ainsi dire, par-dessus A !

2.9.5 Mettre un nom de sous-programme comme argument.

Admettons que l’on ait ´ecrit une fonction somdef qui sache calculer une int´egrale d´efinie : il faut lui transmet-tre comme argument, outransmet-tre les bornes xmin et xmax de l’int´egrale et la pr´ecisionepssouhait´ee, le nom de la fonc-tion `a int´egrer. Par exemple :

xmin = 0. ; xmax = acos(-1.0)

write(*,*) somdef( xmin, xmax, eps, sin )

si l’on veut calculer l’int´egrale de 0 `aπ de sinx. Cepen-dant, si l’on proc`ede sans pr´ecaution, le compilateur di-agnostiquera que la variable sin n’est pas d´eclar´ee, et dira quelque chose qui peut ressembler `a :Error: Symbol

’sin’ at (1) has no IMPLICIT type, ce qui est ab-surde puisqu’il s’agit d’une fonction intrins`eque. . . C’est que dans ce contexte l`a, il ne reconnait pas sin comme une fonction : il faut donc le pr´eciser dans les d´eclarations avecintrinsic.

real :: xmin, xmax, eps, somdef intrinsic :: sin

...

xmin = 0. ; xmax = acos(-1.0)

write(*,*) somdef( xmin, xmax, eps, sin )

De mˆeme, s’il s’agit d’une fonctionmafctque l’on a ´ecrite soi-mˆeme, il faut la d´eclarer parexternal

real :: xmin, xmax, eps, somdef external :: mafct

...

xmin = .. ; xmax = ..

write(*,*) somdef( xmin, xmax, eps, mafct )

Les mˆemes r`egles s’appliquent pour les sous-programmes35.

Ce sont l`a les seuls cas d’utilisation de intrinsic et external: quand on ne transmet pas comme argument le nom d’un sous-programme ou d’une fonction `a un autre sous-programme ou fonction, il est compl`etement inutile d’utiliser ces d´eclarations36.

2.9.6 Les biblioth` eques.

2.9.6.1 Pour quoi faire ?

Imaginons que l’on ait ´ecrit une s´erie de sous-programmes, par exemple des calculs d’int´egrale, dont le code-source, c’est-`a-dire ´ecrit enFortran, est plac´e pour chaque sous-programme dans un fichier dont le nom se ter-mine par.f90. Pour r´eutiliser ces sous-programmes dans d’autres programmes, on peut bien sˆur simplement inclure

35. Certains compilateurs plus anciens n’acceptent que l’ancienne syntaxeexternal mafct(sans les ::).

36. Cette affirmation est en fait un peu p´eremptoire. . ., on peut imaginer des situations o`u cela peut ˆetre n´ecessaire, par exemple si l’on a ´ecrit soi-mˆeme un sous-programmedot productqui risque d’entrer en conflit avec la fonction intrins`eque de mˆeme nom ; la eclarationexternalesout alors le probl`eme.

les fichiers correspondants `a l’aide de directives include

’nom de fichier.f90’, c’est d’ailleurs ce que l’on fait souvent.

Toutefois, il arrive que ces sous-programmes aient eux-mˆeme besoin d’autres sous-programmes pour fonction-ner. Par exemple, les programmes de calcul de tran-form´ee de Fourier vont tous utiliser le mˆeme algorithme, mais vont diff´erer selon que l’on veut une transform´ee directe ou inverse : ainsi le programme que l’on ap-pelle ne fait qu’appeler un autre programme en modifi-ant ´eventuellement un signe ; il faudrait alors inclure ex-plicitement les deux sous-programmes, ce qui suppose que l’on connaisse sp´ecifiquement comment tout cela est orga-nis´e : autant dire que c’est rarement le cas quelques ann´ees apr`es l’´ecriture initiale. Une bonne solution est alors de constituer une biblioth`eque (library en anglais37).

Une biblioth`eque est un fichier dans lequel sont re-group´es un ensemble se sous-programmes d´ej`a compil´es et dans lequel le compilateur peut aller pˆecher ce dont il a besoin, par exemple un sous-programme de calcul de trans-form´ee de Fourier et tous les sous-programmes auxquels celui-ci fait appel : dans le programme principal, il suffira de faire un calldu sous-programme voulu.

2.9.6.2 Cr´eer une biblioth`eque personnelle.

On a donc ´ecrit un certain nombre de sous-programmes et de fonction (dˆument test´es, bien sˆur. . .) plac´es dans des fichiers machin 01.f90, machin 02.f90, truc real.f90, truc dbl prec.f90, etc. Si on compile tout cela par g95

*.f90, le compilateur refusera de la faire parce qu’il n’y a pas de programme principal : il faut donc inhiber l’´editeur de lien38avec l’option-cet pendant qu’on y est, on peut demander une optimisation du code avec l’option -O3.

Cela donne : g95 -c -O3 *.f90

on obtiendra alors une s´erie de fichiers machin 01.o, machin 02.o, truc real.o, truc dbl prec.o, etc. Ces fichiers doivent alors ˆetre inclus dans un fichier d’archive : ar rv libmabib.a *.o

Le fichier libmabib.acontient la biblioth`equemabib. On peut alors supprimer tous les fichiers interm´ediaires : rm -f *.o

Pour utiliser cela, il suffit de compiler normalement son programme principal, dans lequel il y a des call machin 01(arg1, arg2), avec la commande :

g95 big prog.f90 -Lrepertoire de mabib -lmabib -o big prog

o`u repertoire de mabib est le r´epertoire o`u se trouve le fichier libmabib.a. Si l’on peut placer le fichier libmabib.a dans le r´epertoire /usr/local/lib39, l’option-Ln’est plus n´ecessaire.

2.9.6.3 Utiliser une biblioth`eque existante.

Il est toutefois assez rare que l’on ait `a cr´eer une biblioth`eque de toutes pi`eces : l’essentiel des algorithmes courants a d´ej`a ´et´e programm´e, compil´e, test´e, etc., il est inutile -voire nocif- de les refaire ! Pour l’alg`ebre

37. attention aux faux amis : en anglais, library signifie bib-lioth`eque (l’endroit o`u l’on emprunte des livres) alors quelibrairie (l’endroit o`u l’on ach`ete des livres) se ditbookstore. . .

38. c’est ce qui fait le lien entre programmes et sous-programmes.

39. il faut pour cela les droits de super-utilisateur.

lin´eaire, il y a par exemplelapack qu’en g´en´eral, il suffit d’invoquer `a la compilation par :

g95 mon big prog.f90 -llapack -o mon big prog Cela suppose ´evidemment que l’on ait la documentation qui va avec, mais c’est g´en´eralement facile `a obtenir via Internet. C’est tr`es souvent ainsi que l’on travaille dans

le monde r´eel de la simulation : pour r´esoudre un probl`eme, on identifie une m´ethode de r´esolution dont le noyau r´eside dans un algorithme connu (inversion de matrice, transform´ee de Fourier, valeurs propres, . . .) et l’on trouve le programme de biblioth`eque convenable, on l’appelle et on compile l’ensemble avec l’invocaton de la biblioth`eque idoine : un appr´eciable gain de temps !

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