8. Procédures▲
8-1. Arguments▲
Très souvent, dans un programme, on a besoin d'effectuer un même traitement plusieurs fois avec des valeurs différentes. La solution est de définir ce traitement une seule fois à l'aide d'une unité de programme de type procédure (SUBROUTINE
ou FUNCTION
). Les unités de programmes désirant effectuer ce traitement feront appel à cette procédure en lui transmettant des valeurs via des variables appelées arguments d'appel (actual-arguments). La procédure appelée récupère les valeurs qu'on lui a transmises via des variables appelées arguments muets (dummy arguments).
En Fortran le passage de ces valeurs s'effectue par référence :
- les adresses des arguments d'appel sont transmises à la procédure appelée ;
- dans la procédure appelée, les arguments muets sont des alias des arguments d'appel.
8-2. Subroutines▲
L'appel d'une procédure de type SUBROUTINE
s'effectue à l'aide de l'instruction CALL
suivie du nom de la procédure à appeler avec la liste des arguments d'appels entre parenthèses.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
REAL
, DIMENSION
(100) :: tab
REAL
:: moyenne, maximum
...
CALL
SP( tab, moyenne, maximum )
PRINT
*,moyenne, maximum
END
SUBROUTINE
SP( t, moy, max
)
REAL
, DIMENSION
(100) :: t
REAL
:: moy, max
INTEGER
:: i
max
= t(1); moy = t(1)
DO
i=2,100
IF
( t(i) >
max
) max
= t(i)
moy = moy + t(i)
END
DO
moy = moy/100
END
SUBROUTINE
SP
8-3. Fonctions▲
Un autre moyen de transmettre des valeurs à une unité de programme est l'utilisation d'une procédure de type FUNCTION
.
À la différence d'une SUBROUTINE
, une FUNCTION
retourne une valeur, celle-ci est donc typée. De plus, son appel s'effectue en indiquant uniquement son nom suivi entre parenthèses de la liste des arguments d'appels.
Au sein de la fonction l'instruction return
sert à transmettre à la procédure appelante la valeur à retourner. Celle-ci n'est nécessaire que dans le cas où on désire effectuer ce retour avant la fin de la définition de la fonction.
Dans la procédure appelante, l'expression correspondant à l'appel de la fonction est remplacée par la valeur retournée.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
REAL
, DIMENSION
(100) :: tab
REAL
:: moyenne, maximum
REAL
:: maxi
...
maximum = maxi( tab, moyenne )
PRINT
*,moyenne, maximum
...
END
FUNCTION
maxi( t, moy )
REAL
, DIMENSION
(100) :: t
REAL
:: moy, maxi
INTEGER
:: i
maxi = t(1); moy = t(1)
DO
i=2,100
IF
( t(i) >
maxi ) maxi = t(i)
moy = moy + t(i)
END
DO
moy = moy/100
END
FUNCTION
maxi
8-4. Arguments de type chaîne de caractères▲
Lorsqu'une chaîne de caractères est transmise en argument, Fortran passe également sa longueur de façon implicite.
Dans la procédure appelée, celle-ci peut être récupérée à l'aide de la fonction intrinsèque LEN
.
La déclaration de la chaîne de caractères au sein de la procédure appelée est faite en spécifiant le caractère ∗ à la place de la longueur.
La procédure appelée fait alors référence à une chaîne de caractères à taille implicite (assumed-size string).
Dans l'exemple ci-dessous les fonctions ICHAR
/ACHAR permettent de mettre en relation un caractère et son rang dans la table des caractères ASCII.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
program
arg_chaine
implicit
none
character
(len
=10) :: ch
read
'(a)'
,ch
call
conv( ch )
print
*,ch
end
program
arg_chaine
subroutine
conv( chaine )
implicit
none
character
(len
=*) :: chaine
integer
i, j
do
i=1,len
(chaine)
if
( ichar
( chaine(i:i) ) <
97 .or.
&
ichar
( chaine(i:i) ) >
122 ) cycle
j = ichar
( chaine(i:i) ) - 32
chaine(i:i) = achar( j )
end
do
end
subroutine
conv
8-5. Arguments de type tableau▲
Lorsque l'on transmet un tableau en argument, il est commode de transmettre également ses dimensions afin de pouvoir déclarer l'argument muet correspondant au sein de la procédure appelée à l'aide de celles-ci ; de ce fait le tableau est ajustable.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
program
arg_tab
implicit
none
integer
, parameter
:: n = 3, m = 2
real
, dimension
(n,m) :: tab
real
:: somme
read
*,tab; print
*,somme( tab, n, m )
end
program
arg_tab
real
function
somme( t, n, m )
integer
:: n,m,i,j
real
, dimension
(n,m) :: t
somme = 0.
do
i=1,n
do
j=1,m
somme = somme + t(i,j)
end
do
end
do
end
function
somme
Lorsqu'un tableau est passé en argument, c'est l'adresse de son premier élément qui est transmise.
La procédure appelée doit posséder les informations lui permettant d'adresser les différents éléments de ce tableau. De façon générale, supposons que l'on dispose d'un tableau tab à 2 dimensions constitué de n lignes et m colonnes. L'adresse de l'élément tab(i,j) est :
@tab(i,j) = @tab(1,1) + [n*(j-1)+(i-1)]*taille(élt)
Le nombre de colonnes m n'intervient pas dans ce calcul. Souvent en Fortran, lors de l'appel d'une procédure, seule la première dimension d'un tableau à 2 dimensions est transmise.
Dans la procédure appelée, celui-ci est déclaré en indiquant le caractère ∗ à la place de la deuxième dimension. On fait alors référence à un tableau à taille implicite (assumed-size array).
Dans un tel cas, il faut faire preuve d'une certaine prudence, car dans la procédure appelée on ne maîtrise pas l'espace mémoire total occupé par le tableau.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
program
taille_implicite
implicit
none
integer
, parameter
:: n = 5, m = 6
real
, dimension
(n,m) :: tab
real
:: somme, som
read
*, tab
somme = som( tab, n )
print
*,somme
end
program
taille_implicite
real
function
som( t, lda )
implicit
none
real
, dimension
(lda,*) :: t
integer
:: lda
integer
:: i,j
som = 0.
do
i=1,lda
do
j=1,lda
som = som + t(i,j)
end
do
end
do
end
function
som
8-6. Arguments de type procédure▲
Une procédure peut être transmise à une autre procédure. Il est nécessaire de la déclarer dans la procédure appelante avec l'attribut EXTERNAL
ou INTRINSIC
si elle est intrinsèque.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
program
arg_proc
implicit
none
double
precision
b_inf, b_sup, aire
double
precision
integrale
integer
pas
double
precision
, external
:: carre
b_inf = 1.
b_sup = 6.
pas = 200000
aire = integrale( b_inf, b_sup, pas, carre )
print
'("Aire : ", f11.6)'
, aire
end
program
arg_proc
function
integrale( borne_i, borne_s, pas, f )
implicit
none
double
precision
borne_i, borne_s
double
precision
integrale
integer
pas, i
double
precision
h, f
h = (borne_s - borne_i)/pas
integrale = 0.
do
i=0, pas-1
integrale = integrale + h*f(borne_i+i*h)
end
do
end
function
integrale
function
carre( x )
implicit
none
double
precision
x
double
precision
carre
carre = x*x
end
function
carre
8-7. Procédures internes▲
En Fortran une procédure peut en contenir d'autres. Ces procédures sont appelées procédures internes. Elles ne peuvent être appelées que depuis la procédure les contenant.
Les définitions des procédures internes sont faites dans la procédure les incluant après l'instruction CONTAINS
.
Il n'y a pas d'imbrications possibles : une procédure interne ne peut pas elle-même en contenir.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
program
proc_interne
implicit
none
character
(len
=10) :: ch
read
'(a)'
,ch
call
conversion( ch )
print
*,ch
end
program
proc_interne
subroutine
conversion( chaine )
implicit
none
character
(len
=*) :: chaine
integer
i
do
i=1,len
(chaine)
if
( ichar
( chaine(i:i) ) <
97 .or.
ichar
( chaine(i:i) ) >
122 ) cycle
chaine(i:i) = car_majuscule( chaine(i:i) )
end
do
CONTAINS
function
car_majuscule( c )
character
(len
=1) :: c, car_majuscule
integer
:: i
i = ichar
( c ) - (ichar
('a'
) - ichar
( 'A'
))
car_majuscule = achar( i )
end
function
car_majuscule
end
subroutine
conversion
Dans une procédure interne, toute variable déclarée dans l'unité de programme qui la contient est accessible, à moins qu'elle n'ait fait l'objet d'une redéclaration.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
program
visibilite
implicit
none
integer
i, j
i = 10; j = 20
call
sub
print
'("i =",i3,", j =",i3)'
, i, j
contains
subroutine
sub
integer
j ! masque le "j" de l'appelant
j = 100; i = i + 1
end
subroutine
sub
end
program
visibilite
Sortie |
---|
i^=^11,^j^=^20 |
8-8. Durée de vie et visibilité des identificateurs▲
On appelle durée de vie d'un identificateur, le temps pendant lequel il existe en mémoire. Il est visible s'il existe en mémoire et est accessible, car il peut exister, mais être masqué par un autre de même nom (c.f. procédure interne).
- Par défaut, une variable a une durée de vie limitée à celle de l'unité de programme dans laquelle elle a été définie ;
- l'attribut
SAVE
permet de prolonger la durée de vie à celle de l'exécutable : on parle alors de variable permanente ou statique ; - dans une unité de programme l'instruction
SAVE
sans spécification de liste de variables indique que toutes les variables de cette unité sont permanentes ; - une compilation effectuée en mode static force la présence de l'instruction
SAVE
dans toutes les unités de programme, ce qui implique que toutes les variables sont permanentes ; -
par contre si elle est faite en mode stack, les variables permanentes sont :
- celles pour lesquelles l'attribut
SAVE
a été précisé, - celles initialisées à la déclaration (via l'instruction
DATA
ou à l'aide du signe =).
- celles pour lesquelles l'attribut
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
program
newton
double
precision
:: valeur = 50.d0
double
precision
:: tolerance
double
precision
:: racine, x
tolerance = 1.0d-1
x = racine( valeur, tolerance )
print
'("Racine de ",f5.1," = ", d16.8)'
, valeur, x
tolerance = 1.0d-6
x = racine( valeur, tolerance )
print
'("Racine de ",f5.1," = ", d16.8)'
, valeur, x
end
program
newton
function
racine( valeur, tol )
double
precision
:: valeur, tol
double
precision
:: racine
double
precision
:: x = 1.0d0, x_prev
integer
:: nb_iterations
nb_iterations = 0
do
nb_iterations = nb_iterations + 1
x_prev = x
x = 0.5 * (x_prev + valeur/x_prev)
if
( abs
(x-x_prev)/x <
tol ) exit
end
do
print
*,"Nombre d'itérations = "
, nb_iterations
racine = x
end
function
racine
8-9. Procédures intrinsèques▲
Le compilateur Fortran dispose d'une bibliothèque de procédures couvrant différents domaines : mathématique, conversion de type, manipulation de chaînes de caractères, comparaison de chaînes de caractères…
Pour une procédure donnée, le nom d'appel diffère suivant le type des arguments transmis. Un nom générique permet de s'affranchir de ces types : c'est la solution fortement conseillée, car elle facilite la portabilité.
Par exemple un appel à la procédure générique ABS
, retournant la valeur absolue de son argument, est transformé par le compilateur en un appel à la procédure :
IABS
pour un argument entier ;ABS
pour un argument réel simple précision ;DABS
pour un argument réel double précision ;CABS
pour un argument complexe.
Une liste des procédures intrinsèques est fournie en annexe BAnnexe B : procédures intrinsèques.