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