Les sous-programmes

Transcription

Les sous-programmes
S. Laporte
sous-programmes
DAIGL TS1
Les sous-programmes
I. Présentation et terminologie
Lorsqu'un programme est long, il est irréaliste d'écrire son code d'un seul tenant. En fait, on
décompose le programme en plusieurs parties plus petites qu'on assemble pour former
l'application finale.
Les sous-programmes (procédures et fonctions) permettent de donc découper un gros
programme en morceaux plus petits et donc plus simples à coder et à comprendre, et de plus
permet d'éviter de répéter plusieurs morceaux de code identiques.
Comme un programme, un sous-programme possède un nom, des variables, des instructions,
un début et une fin. Mais contrairement à un programme, un sous-programme ne peut pas
s'exécuter indépendamment d'un autre programme.
En effet, l'exécution d'un sous-programme est demandée par le programme principal (ou un
autre sous-programme) grâce à une instruction spécifique qu'on nomme APPEL. Le
programme principal est l'ensemble d'instruction qui s'exécute au lancement de l'application
(depuis l'OS ou l'environnement de développement). C'est le programme principal qui se
charge de demander l'exécution des instructions se trouvant des les sous-programmes.
application
programme principal
lancement de
l'application
Début
instruction1
instruction2
… .
sous-prog1
instructionX
…
sous-prog2
instructionZ
…
Fin
sous-prog1
Début
… .
Fin
sous-prog2
Début
…
Fin
Remarque importante :
Vous avez déjà utilisé des sous-programmes en C# : les procédures événementielles. Ce sont
des sous-programmes particulier, exécutés à la suite d'un événement (par exemple clic sur un
bouton, chargement d'une fenêtre, … ).
1
S. Laporte
sous-programmes
DAIGL TS1
Les sous-programmes que nous allons étudier sont d'un autre type : ces sous-programmes
(non événementiel) ne sont pas exécutés suite à un événement, mais sont lancés par une
instruction dans un autre sous-programme ou par le programme principal.
L'instruction qui permet d'exécuter un sous-programme est nommée APPEL.
On dit qu'un sous-programme est appelé par un programme appelant (qui peut être le
programme principal ou un autre sous-programme).
Certains sous-programmes sont déjà écrits, regroupés dans des bibliothèques et sont
utilisables dans l'environnement de développement. On les appelle les sous-programmes
prédéfinis.
Mais le développeur peut être amené à coder ses propres sous-programmes pour mieux
organiser son code (le découper pour le rendre plus lisible et plus facile à débogguer) et
permettre de réutiliser du code qui revient souvent.
Les instructions d'un sous-programme est écrit en dehors du programme appelant: c'est la
DEFINITION du sous-programme. Si un sous-programme écrit n'est jamais appelé, il ne sera
jamais exécuté. Mais on peut vous demander d'écrire un sous-programme sans savoir quand et
comment il sera appelé.
Seul le programme principal est lancé au moment où on exécute l'application. Les sousprogrammes ne sont exécutés seulement quand ils sont appelés.
Lorsque l'exécution d'un sous-programme est terminée, il y a un RETOUR à l'instruction qui
suit l'appel dans le programme appelant.
Il existe deux types de sous-programmes : les procédures et les fonctions. Les procédures ne
représentent qu'une suite d'instructions, alors que les fonctions renvoient en plus une valeur
résultat. Pour l'instant nous n'étudierons que des exemples avec des procédures. Nous verrons
en détail la différence entre les deux dans la 3° partie.
Exemple très simple:
Procédure DessinerLigne( )
Var i :entier
Début
Pour i de 1 à 10 Faire
Afficher "*"
FinPour
Afficher "\n"
//passage à la ligne
FinProc
Programme (principal)
Var n : entier
Début
Afficher "Voici une belle figure géométrique"
Pour n de 1 à 5 Faire
DessinerLigne( )
FinPour
Afficher "Alors, ça vous plait?"
FinProg
2
Cette procédure dessine une
ligne de 10 étoiles et passe à
la ligne
S. Laporte
sous-programmes
DAIGL TS1
programme principal
DessinerLigne
n
Début
…
Pour n de 1 à 5 Faire
DessinerLigne( )
FinPour
i
Début
…
Fin
Fin
La procédure DessinerLigne est appelée 5 fois (une fois par passage dans la boucle pour),
donc le programme dessine un rectangle
Un sous-programme peut lui-même appeler un autre sous programme.
A chaque fin de sous-programme, on revient à l'exécution de l'instruction suivante dans le
programme appelant.
Ordre d'exécution des instructions
programme principal
1
2
3appel de A
11
12
13appel de B
16
ssprog A
4
5
6appel de C
10
ssprog C
7
8
9
14
15
ssprog B
Communication entre programme appelant et sous-programme appelé
Les données et les résultats d'un sous-programme proviennent d'une communication entre le
programme appelant et le sous-programme.
3 modes de communication sont possibles :
- l'utilisation de variables communes à l'appelant et à l'appelé : ce sont les variables globales.
- le passage de paramètres, pour les procédures et les fonctions
- la valeur de retour pour les fonctions
II. Variables locales et variables globales
L'endroit où est déclaré une variable est très important car il détermine dans quel(s) sousprogramme(s) elle va pouvoir être utilisée.
3
S. Laporte
sous-programmes
DAIGL TS1
Une variable locale est déclarée à l'intérieur d'un sous-programme et elle n'est utilisable que dans le
sous-programme où elle a été déclarée. Ceci est aussi valable pour le programme principal : une variable
déclarée dans le programme principal n'est utilisable que dans le programme principal et pas dans les sousprogramme.
Une variable globale est déclarée à l'extérieur du programme principal et des sous-programmes : elle
commune à l'ensemble des sous-programmes1 et du programme principal, Elle est utilisable partout
(surtout il ne faut pas redéclarer la variable dans les sous-programmes utilisateurs)
Suite de l'exemple :
Imaginons qu'on veuille que l'utilisateur puisse choisir le nombre d'étoiles par lignes et le nombre de
lignes dans le rectangle. On pourrait déclarer ces deux données en variables globales
Var globales :
nbe : entier //nombre d'étoiles par ligne
nbl : entier //nombre de lignes du rectangle
nbe
Procédure DessinerLigne( )
Var i :entier
Début
Pour i de 1 à nbe Faire
Afficher "*"
FinPour
Afficher "\n" //passage à la ligne
FinProc
nbl
Procédure DessinerLigne
Programme (principal)
Var i : entier
Début
Afficher "Combien de lignes voulez-vous à votre rectangle ?"
Saisir nbl
Afficher "Combien d'étoiles par lignes voulez-vous?"
Saisir nbe
Pour i de 1 à nbl Faire
DessinerLigne( )
FinPour
Afficher "Alors, ça vous plait?"
FinProg
i
programme principal
i
Variables de même nom
Le même nom de variable peut désigner plusieurs variables locales différentes. En effet, rien
n'empêche que plusieurs sous-programmes utilisent le même nom de variable en local. La valeur de i
dans un sous-programme n'aura pas d'influence sur la valeur de i d'un autre sous-programme, car i
correspond à deux emplacements de la mémoire distincts, et propre à un sous-programme.
En revanche, une variable globale n'existe qu'en un seul exemplaire pour tous les sous-programmes,
donc chaque fois qu'un sous-programme modifie une variable globale, alors sa modification sera
visible par tous les autres sous-programmes. C'est utilisé seulement en cas de nécessité. Dans notre
exemple, on ne pourrait donc pas utiliser i comme variable globale, car i a une valeur différente dans
le programme principal et dans la procédure DessinerLigne.
Mais Attention à ne jamais appeler une variable locale par le même nom qu'une variable globale.
1
plus exactement l'ensemble des sous-programmes de la classe (dans les langages objet tels que C#) ou dans
l'ensemble des sous-programmes du fichier dans les autres types de langage
4
S. Laporte
sous-programmes
DAIGL TS1
III. Fonctions et procédures
Dans les exemples précédents, nous avons vu les procédures, qui ne sont pas les seuls types
de sous-programmes. Les procédures ne représentent qu'une suite d'instructions exécutées à
l'appel dans le programme appelant.
Les fonctions réalisent aussi une suite d'instructions mais en plus elle renvoie une valeur
résultat au programme appelant. Cette valeur doit être utilisée par le programme appelant.
En effet; l'appel d'une fonction est remplacé dans le programme appelant par la valeur
renvoyée. Cette valeur doit donc être incluse dans une instruction qui l'utilise.
A la définition d'une fonction, on doit indiquer le type de la valeur retournée
Exemple :
Variable globale
montantHT : réel
Fonction ttc ( ) : réel
Var montantTTC
Début
montantTTC ? montantHT*1.196
retourne montantTTC
Fin
Programme totalfacture
Début
Répéter
"MontantHT du produit numéro" + i
Saisir montantHT
total ? total + ttc( )
"Autre produit? (tapez N pour non ou tout autre touche pour continuer)"
Saisir rep
Jusqu'à rep = 'N'
Afficher "Le total de la commande est de " + total
Fin
En résumé :
l'appel d'une procédure constitue une instruction en lui-même
l'appel d'une fonction en revanche représente une valeur (tout comme une variable
représente une valeur) qui doit être utilisée à l'intérieur d'une instruction. On ne peut pas
trouver l'appel d'une fonction tout seul, sur une ligne à part. Il se trouve forcément dans un
calcul, une affectation, un affichage, un test, ect.
Application : programme différence dates et procédure Quantième
5
S. Laporte
sous-programmes
DAIGL TS1
IV. Les paramètres
La communication entre sous-programme et programme appelant se fait par l'intermédiaire de
variables locales qu'on appelle paramètres (ou parfois arguments).
Dans notre exemple, au lieu d'utiliser des variables globales, on peut passer le nombre
d'étoiles par ligne en paramètre de la procédure.
Procédure DessinerLigne(E nbetoile :entier)
Var i :entier
Début
Pour i de 1 à nbetoile Faire
Afficher "*"
FinPour
Afficher "\n"
//passage à la ligne
FinProc
Programme (principal)
Var i , nbe, nbl : entier
Début
Afficher "Combien de lignes voulez-vous à votre rectangle ?"
Saisir nbl
Afficher "Combien d'étoiles par lignes voulez-vous?"
Saisir nbe
Pour i de 1 à nbl Faire
DessinerLigne( nbe )
FinPour
Afficher "Alors, ça vous plait?"
FinProg
Dans cet exemple, à l'appel, la valeur de nbe est transmise à la variable paramètre nbétoile de
la procédure, qui va donc s'exécuter avec cette valeur. La valeur est donc une donnée pour la
procédure, on dit que le paramètre est en entrée (d'où E).
La communication avec les paramètres peut se faire dans deux sens:
-
-
du programme appelant au programme appelé => du point de vue du sous-programme
appelé, c'est une entrée; une valeur de paramètre est donnée lors de l'appel par le
programme appelant au sous-programme
du programme appelé vers le programme appelant => du point de vue du programme
appelé, c'est une sortie; une variable du programme principal est valorisée par le sousprogramme.
Mais la communication peut aussi être à la fois en entrée et en sortie.
Dans ce cas, la valeur initiale donnée par le programme appelant est utilisée puis modifiée par
le sous-programme appelé.
Lors de la définition, les paramètres sont déclarés dans l'en-tête, avec leur type (entier,
chaîne,… ) et leur nature (donnée, résultat ou donnée/résultat). Ce sont les paramètres formels.
Les paramètres sont utilisés comme les autres variables dans le code du sous-programme
Les paramètres "donnée" (en entrée) ont une valeur fournie à l'exécution par l'instruction
d'appel du programme appelant.
6
S. Laporte
sous-programmes
DAIGL TS1
C'est une erreur de les saisir ou de leur attribuer une valeur en début de traitement. Le fait de
mettre une variable en paramètre donnée remplace sa saisie dans le sous-programme. Celle-ci
a été faite au préalable, avant l'exécution du sous-programme.
Les paramètres "résultat" vont répercuter leur valeur aux variables correspondantes de l'appel
dans le programme appelant. Il est donc inutile de les afficher. C'est au programme appelant
de décider de ce qu'il va faire de ces résultats.
Nous allons étudier ces autres types de paramètre dans l'exemple suivant.
V. Exemple d'application
2 traitements complémentaires:
traitement 1 : trouver le nombre de secondes contenu dans une durée exprimée en heures,
minutes et secondes
heures, minutes, secondes => nombre de secondes
traitement 2 : l'inverse, à savoir calculer la durée en heures, minutes et secondes
correspondant à un nombre de secondes
nombre de secondes
=> heures, minutes, secondes
Voir la réalisation de ces traitements sans sous programme
Données mémorisées dans des variables et valorisées par saisies au clavier (ou lues depuis un fichier
ou une base de donnée, ou calculées à partir d'autres variables). Résultats affichés (ou écrit et/ou utilisé
dans la suite du programme)
? Données du traitement en paramètres et résultat affiché
traitement 1
Les données ne sont pas saisies mais leur valeur provient des paramètres de l'appel :
paramètres données (en entrée)
Procédure afficheNbsec(E heures : entier, minutes : entier, secondes : entier)
Var
nbsec : entier
Début
nbsec ? heures* 3600 + minutes * 60 + secondes
Afficher nbsec
FinProcédure
Utilisation de la procédure pour afficher le nombre de secondes écoulées en 2h 15m 10s
=> on appelle le sous-programme avec les valeurs de paramètres correspondantes
afficheNbsec(2, 15, 10)
Exemple de programme appelant
permettant de saisir une durée en heures, minutes et secondes puis d'afficher le nombre de
secondes écoulées pendant cette durée
Programme appelant
Var
h, m, s : entier
Début
Aff "Tapez la durée dans le format hh mm ss"
Saisir h, m, s
7
S. Laporte
sous-programmes
DAIGL TS1
Aff "Voilà le nombre de secondes écoulées pendant cette durée "
afficheNbsec(h, m, s)
Fin
traitement 2
Procédure afficheHMS(E nbsec :entier)
Var
heur, min, sec : entier
Début
heur ? nbsec DIV 3600
reste ? nbsec MOD 3600
min ? reste DIV 60
sec ? nbsec MOD 60
Afficher heur, "h", min, "m", sec, "s"
FinProcédure
? Résultat communiqué au programme appelant au lieu d'être affiché
=> permet la réutilisation du résultat dans le programme appelant
2 façons de communiquer le résultat d'un sous-programme à l'appelant :
- retour d'une fonction (à utiliser si le résultat correspond à une seule valeur)
- paramètre en sortie
(à utiliser si plusieurs variables forment le résultat)
traitement1 => 1 seul résultat => FONCTION
Par défaut, les paramètres d'une fonction sont en entrée.
Fonction nbsec(heure : entier, minute : entier, seconde : entier) : entier
Var
nbsec : entier
Début
nbsec ? heure* 3600 + minute * 60 + seconde
Retourne nbsec
FinFonction
appel :
L'appel d'une fonction est remplacé à l'exécution par la valeur retournée par celle-ci.
L'appel d'une fonction est donc utilisé comme une expression, à l'intérieur d'une instruction.
Les paramètres de l'appel peuvent être des valeurs littérales ou des valeurs de variables
avec comme paramètres des valeurs littérales
duréesec ? nbsec(2, 15, 10)
avec comme paramètres des variables
duréesec ? nbsec(h, m, s)
On peut aussi trouver toute expression comme valeur de paramètre à l'appel (résultat d'un calcul, résultat d'une
autre fonction). Pourvu que le type de l'expression corresponde bien au type du paramètre.
traitement 2 => résultat formé de plusieurs variables
=> procédure avec paramètres en sortie
Procédure converthms(E nbsec :entier
S heur, min, sec : entier)
Var
reste : entier
8
S. Laporte
sous-programmes
DAIGL TS1
Début
heur ? nbsec DIV 3600
reste ? nbsec MOD 3600
min ? reste DIV 60
sec ? nbsec MOD 60
FinProcédure
appel
converthms(10000, h, m, s)
Les paramètres de l'appel qui correspondent à des paramètres en sortie (ou entrée/sortie)
doivent obligatoirement être des variables (car elles vont être valorisées).
L'appel d'une procédure représente une instruction à part entière
Utilisation de ces sous-programmes dans un programme qui calcule la différence entre deux
heures de la journée.
Programme différenceHeures
Var
h1, m1, s1 : entier
//pour exprimer la première heure
h2, m2, s2 : entier
//pour exprimer la seconde heure
sec1, sec2, sec : entier
//pour le calcul du nombre de secondes d'écart
dh, dm, ds : entier
//heure, minute et seconde de la durée résultat
Début
Afficher "Première heure sous forme hh mm ss"
Saisir h1, m1, s1
Afficher "Deuxième heure sous forme hh mm ss"
Saisir h2, m2, s2
sec1 ? nbsec(h1, m1, s1)
sec2 ? nbsec(h2, m2, s2)
sec ? sec2 – sec1
converthms(sec, dh, dm, ds)
Afficher "la durée séparant ces deux heures est de ", dh, "h ", dm, "m ", ds, "s"
Fin
D) Utilisation d'un sous-programme dans un autre sous-programme
Ecrire une fonction qui retourne la durée écoulée entre deux heures.
Fonction durée (h1 : hms, h2: hms) : hms
Var
d : hms
sec : entier
Début
sec ? nbsec(h2) – nbsec(h1)
d ? converthms(sec)
retourne converthms(nbsec(h2) – nbsec(h1))
retourne d
FinFonction
E) tableau passé en paramètre
On peut passer en paramètre un tableau. Il faut que le type et les dimensions du tableau
transmis à l'appel et correspondent au type et aux dimensions du tableau déclaré en paramètre
dans la définition.
9
S. Laporte
sous-programmes
DAIGL TS1
Lors de l'appel, un tableau passé en paramètre ne comporte pas de crochets. On indique juste
son nom.
ex: Fonction qui renvoie le maximum d'un tableau.
Quand le tableau est surdimensionné et qu'on utilise une variable contenant son nombre
d'éléments utilisés, il faut passer cette variable en paramètre avec le tableau.
Fonction maximum(tab : tableau[1..Max] de réels, nbe : entier) : réel
Var
max : réel
i : entier
Début
max ? 0 // valable si tous les éléments sont positifs
Pour i de 1 jà nbe Faire
Si tab[i] > max
Alors max ? tab[i]
FinSi
FinPour
retourne max
FinFonction
appel
Var
tabnote : tableau[1..MAX] de réels
nbeleve : entier
…
Début
…
//saisie du tableau de note (par exemple avec une procédure) et du nombre d'élèves du tableau
Afficher "la meilleure note est", maximum(tabnote, nbeleve)
Fin
VI. paramètres en entrée-sortie (donnée-résultat)
Un paramètre peut être à la fois une donnée et un résultat pour le sous-programme. C'est-àdire que le sous-programme utilise la valeur en entrée du sous-programme puis modifie cette
valeur.
Procédure qui insère une nouvelle valeur dans un tableau
Le tableau va être modifié par la procédure, ainsi que le nombre d'élements. Ce sont donc des
paramètres en entrée/sortie
Procédure ajout(E valeur : entier,
E/S tab: tableau[1..MAX] d'entier,
E/S nbelem : entier)
Début
Si nbelem < MAX //on vérifie que le tableau n'est pas déjà plein
Alors tab[ nbelem+1 ] ? valeur
// on ajoute dans la case suivante
nbelem ? nbelem +1
// on met à jour le nombre d'éléments
Sinon aff "tableau plein"
10
S. Laporte
sous-programmes
FinSi
FinProc
11
DAIGL TS1

Documents pareils