Aller au contenu principal

Les fonctions

Ce que ce chapitre apporte

  • Utiliser une fonction intégrée comme une boîte noire : des arguments en entrée, une valeur rendue en sortie.
  • Remplacer Random par une saisie pour qu'un programme redevienne vérifiable.
  • Écrire une fonction avec ses paramètres et sa variable de retour, sans forme Return.
  • Dérouler un appel : évaluation des arguments, réception par les paramètres, retour de la valeur.
  • Distinguer l'appel dans une expression de la forme Call, et dire laquelle convient.
  • Expliquer pourquoi une variable du programme principal n'existe pas dans une fonction.

Le chapitre précédent s'est terminé sur un défaut : la formule du taux de remplissage y était écrite deux fois, à deux lignes d'écart. Rien ne cassait, mais une correction faite d'un côté et oubliée de l'autre aurait donné deux résultats incohérents. Une fonction règle ce défaut : elle nomme une étape, la range dans son propre diagramme, et la rend appelable autant de fois que nécessaire. Ce chapitre distingue les fonctions livrées avec le logiciel de celles que le lecteur écrit, et détaille ce qui entre, ce qui sort, et ce que chaque diagramme voit du reste du programme.

Les fonctions intégrées, des boîtes noires

Size, Len, Char et ToFixed sont des fonctions intégrées. Pour s'en servir, inutile de savoir comment elles calculent : il suffit de savoir ce qu'elles reçoivent, les arguments entre parenthèses, et ce qu'elles rendent. La valeur rendue prend la place de l'appel dans l'expression.

FonctionReçoitRend
ToFixed(r, i)un réel et un nombre de décimalesle réel écrit en texte avec i décimales
Len(s), Char(s, i)un texte (et un indice)sa longueur, ou le caractère d'indice i
Size(t)un tableauson nombre de cases
Random(n)un entierun entier au hasard, de 0 à n - 1

Random sert à simuler une mesure. Random(101) rend un entier de 0 à 100, comme un niveau en pourcentage :

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
FalseTrueMainInteger niveauniveau = Random(101)Output "Niveau simulé : " & niveau & " %"niveau > 90Output "Niveau acceptable"Output "Débordement imminent"End

Un capteur simulé. Chaque exécution tire une nouvelle valeur.

Exécuter ce programme plusieurs fois : la valeur change à chaque exécution. C'est précisément ce qui le rend impossible à tester. Pour vérifier le cas 90, qui doit afficher « Niveau acceptable », il faudrait attendre que le hasard le tire, et un résultat que l'on ne peut pas reproduire ne se vérifie pas. Pour tester, remplacer l'affectation par lire: niveau : la valeur est alors choisie, et chaque cas se rejoue à volonté. Random ne revient qu'une fois le programme vérifié.

Écrire ses propres fonctions

Size, Len et ToFixed arrivent avec l'interpréteur. Les étapes propres à un programme, elles, n'existent nulle part : elles se rédigent. Flowgorithm permet d'ajouter ses propres fonctions dans le même fichier, chacune dans son diagramme, et le programme principal les appelle ensuite comme les autres.

Une fonction définie par l'utilisateur répond à trois besoins. Nommer une étape : CalculerTaux(volume, capacite) se lit mieux qu'une formule au milieu du programme. Réutiliser : la formule écrite une fois sert à chaque appel, et une correction s'applique partout. Tester à part : la fonction se vérifie seule, sur des valeurs choisies, avant d'être branchée dans le programme.

Paramètre, argument, variable de retour

Chaque fonction a son propre diagramme, qui commence par une pilule portant son nom et finit par sa propre pilule de fin. Un paramètre est une variable de la fonction, déclarée avec un type dans son en-tête. Un argument est la valeur transmise au moment de l'appel : le paramètre reçoit l'argument. Une fonction qui retourne une valeur a un type de retour et une variable de retour, déclarée dans la fonction : la fonction rend le contenu de cette variable quand elle se termine. Il n'existe pas de forme « Return ».

Cette définition écarte deux idées reçues. Une fonction n'est pas un raccourci d'écriture recopié à l'endroit de l'appel : c'est un diagramme distinct, où l'exécution entre, puis d'où elle ressort. Et la valeur rendue ne se range nulle part d'elle-même : le programme qui appelle doit la récupérer, sans quoi elle est perdue.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
MainReal puissance, duree, energieInput puissanceInput dureeenergie = CalculerEnergie(puissance, duree)AfficherBilan("Presse 2", energie)EndCalculerEnergieReal resultatresultat = puissanceKw * dureeHReturn resultatAfficherBilanOutput machine & " : " & ToFixed(kwh, 1) & " kWh"End

Le programme principal et deux fonctions, chacune dans son diagramme. CalculerEnergie retourne une valeur ; AfficherBilan affiche et ne retourne rien.

Dérouler en pas à pas avec une puissance de 7,5 kW pendant 6 h : à l'affectation, l'exécution entre dans CalculerEnergie, la fenêtre des variables montre puissanceKw, dureeH et resultat, puis revient au programme principal, où energie reçoit la valeur rendue. L'appel suivant entre dans AfficherBilan, qui affiche Presse 2 : 45.0 kWh. L'argument puissance est reçu par le paramètre puissanceKw, et l'argument duree par le paramètre dureeH : les noms n'ont pas à correspondre, et un argument peut aussi être une valeur écrite directement, comme "Presse 2".

Un appel se déroule toujours dans le même ordre, et le pas à pas le montre forme par forme.

  1. Les arguments sont évalués dans le programme principal : puissance vaut 7.5, duree vaut 6.
  2. L'exécution entre dans le diagramme de la fonction, et les paramètres reçoivent ces valeurs, dans l'ordre de l'en-tête : puissanceKw prend 7.5, dureeH prend 6. Le programme principal attend.
  3. La fonction exécute ses propres formes, avec ses propres variables.
  4. À sa pilule de fin, l'exécution revient au point d'appel, et le contenu de la variable de retour remplace l'appel dans l'expression : l'affectation devient energie ← 45.

La transmission se fait donc par position, et non par nom : le premier argument va au premier paramètre, le deuxième au deuxième. Leur nombre doit correspondre à celui des paramètres, sinon l'appel est refusé. Un argument n'est pas forcément une variable : une expression convient aussi, comme CalculerTaux(volumes[heurePic], capacite) plus loin dans ce chapitre, car c'est sa valeur qui part dans la fonction.

La fonction…Elle s'appelle…Exemple
retourne une valeurdans une expression : affectation, sortieenergie ← CalculerEnergie(puissance, duree)
ne retourne rienpar la forme CallAfficherBilan("Presse 2", energie)

La variable de retour se remplit comme n'importe quelle variable, par une affectation ordinaire. Rien ne la distingue au moment où elle est écrite : elle est spéciale par son nom, celui qui figure dans l'en-tête de la fonction. C'est sa valeur au moment où la fonction se termine qui est rendue.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
MainReal puissance, duree, energieInput puissanceInput dureeenergie = CalculerEnergie(puissance, duree)Output "Énergie : " & ToFixed(energie, 1) & " kWh"EndCalculerEnergieReal resultatReal produitproduit = puissanceKw * dureeHReturn resultat

Le calcul est juste, mais rangé dans la mauvaise variable. Exécuter : le message nomme la fonction et la variable restée vide.

Exécuter cette version : le produit est bien calculé, mais dans produit, alors que l'en-tête annonce resultat. L'exécution s'arrête sur CalculerEnergie se termine sans avoir rangé de valeur dans resultat. Aucune forme Return n'aurait signalé l'oubli, puisqu'il n'en existe pas : le seul lien entre le calcul et la valeur rendue est le nom de la variable. Avant de lancer une fonction, vérifier que ce nom reçoit bien une valeur.

Une fonction est ainsi fermée des deux côtés : rien n'en sort que la valeur rendue, et rien n'y entre que par les paramètres. La version suivante l'oublie.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
MainReal puissance, duree, energieInput puissanceInput dureeenergie = CalculerEnergie(puissance)Output "Énergie : " & ToFixed(energie, 1) & " kWh"EndCalculerEnergieReal resultatresultat = puissanceKw * dureeReturn resultat

Une version fautive : la fonction ne reçoit que la puissance, et calcule avec la durée du programme principal.

Chacun ses variables : celles d'une fonction ne se voient pas depuis le programme principal, et inversement. Exécuter cette version : l'exécution entre dans CalculerEnergie et s'arrête sur l'affectation, car duree n'y est pas déclarée. Elle n'existe que dans le programme principal. Tout ce dont une fonction a besoin doit lui arriver par un paramètre. Deux variables de même nom, l'une dans la fonction et l'autre dans le programme principal, restent d'ailleurs deux variables distinctes : le nom ne les relie pas.

Un paramètre peut être un tableau. La fonction ci-dessous compte les relevés qui dépassent un seuil ; elle lit les cases du tableau reçu sans les modifier, et sert deux fois avec deux seuils différents.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
NextDoneFalseTrueNextDoneMainReal Array niveaux[6]Integer i, nbi = 0 to Size(niveaux) - 1Input niveaux[i]nb = CompterDepassements(niveaux, 50)Output "Relevés au-dessus de 50 : " & nbOutput "Au-dessus de 60 : " & CompterDepassements(niveaux, 60)EndCompterDepassementsInteger compte, kcompte = 0k = 0 to Size(valeurs) - 1valeurs[k] > seuilcompte = compte + 1Return compte

Un paramètre tableau. Dans la fonction, Size(valeurs) rend la taille du tableau transmis.

Le programme affiche Relevés au-dessus de 50 : 3 puis Au-dessus de 60 : 1. Le second appel figure directement dans la sortie : la valeur rendue s'insère dans le texte, sans passer par une variable. En Python, une fonction se définit avec def, et l'instruction return rend la valeur, là où Flowgorithm rend le contenu de la variable de retour.

Une fonction se vérifie enfin seule, dans un petit programme qui lit ses arguments et affiche ce qu'elle rend : les cas limites s'y essaient un par un, sans dérouler tout le programme autour.

Fil rouge : la cuve de traitement

Étape 6. Le programme de l'étape précédente est repris tel quel, à une chose près : la formule du taux, qui y figurait deux fois, est isolée dans la fonction CalculerTaux. Le reste ne bouge pas, et le résultat non plus. C'est le propre d'une mise en fonction réussie : elle change la structure du programme, jamais ce qu'il affiche.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
NextDoneFalseTrueNextDoneMainReal Array volumes[24]Integer i, heurePicReal capacite, somme, moyenne, tauxMoyen, tauxPiccapacite = pi * 1.5 ^ 2 * 4 * 1000i = 0 to Size(volumes) - 1Input volumes[i]somme = 0heurePic = 0i = 0 to Size(volumes) - 1somme = somme + volumes[i]volumes[i] > volumes[heurePic]heurePic = imoyenne = somme / Size(volumes)tauxMoyen = CalculerTaux(moyenne, capacite)tauxPic = CalculerTaux(volumes[heurePic], capacite)Output "Volume moyen : " & ToFixed(moyenne, 1) & " L, soit " & ToFixed(tauxMoyen,1) & " %"Output "Pic : " & ToFixed(volumes[heurePic], 0) & " L à " & heurePic & " h, soit " &ToFixed(tauxPic, 1) & " %"EndCalculerTauxReal tauxtaux = volume / capacite * 100Return taux

Étape 6 du fil rouge. La même cuve qu'au chapitre précédent, mais le calcul du taux n'est plus écrit qu'une fois. Comparer les deux organigrammes : seules trois lignes diffèrent, et les deux sorties sont identiques.

Avec les 24 relevés proposés, le programme affiche Volume moyen : 20187.5 L, soit 71.4 % puis Pic : 26100 L à 12 h, soit 92.3 %. À 12 h, le taux dépasse le seuil haut de 90 %. En pas à pas, observer les cases volumes[0] à volumes[23] se remplir, puis heurePic changer 7 fois pendant la seconde boucle, chaque fois qu'un relevé dépasse le pic retenu. L'exécution entre ensuite deux fois dans CalculerTaux : son paramètre capacite porte le même nom que la variable du programme principal, mais c'en est une autre, qui reçoit sa valeur à l'appel.

Exercices type

Une station affiche le débit de ses deux pompes en m³/h, alors que les capteurs mesurent en L/min. Écrire la conversion dans une fonction appelée deux fois, et donner l'affichage pour 250 L/min en entrée et 180 L/min en sortie.

La conversion multiplie par 60 (minutes par heure) et divise par 1000 (litres par m³). Écrite une seule fois dans ConvertirDebit, elle est appelée pour chaque pompe :

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
MainReal pompeEntree, pompeSortie, m3Entree, m3SortieInput pompeEntreeInput pompeSortiem3Entree = ConvertirDebit(pompeEntree)m3Sortie = ConvertirDebit(pompeSortie)Output "Pompe d'entrée : " & ToFixed(m3Entree, 1) & " m3/h"Output "Pompe de sortie : " & ToFixed(m3Sortie, 1) & " m3/h"EndConvertirDebitReal m3Heurem3Heure = litresMinute * 60 / 1000Return m3Heure

Une fonction de conversion, deux appels. Le paramètre litresMinute reçoit 250 au premier appel, 180 au second.

Le programme affiche Pompe d'entrée : 15.0 m3/h puis Pompe de sortie : 10.8 m3/h. Recopier la formule dans les deux affectations aurait donné le même résultat aujourd'hui. Mais une erreur de facteur corrigée dans une copie et oubliée dans l'autre produit deux débits incohérents. Avec la fonction, la formule n'existe qu'à un endroit.

Organigramme, tel que l'affiche Flowgorithm
MainReal volume, capacite, tauxInput volumeInput capacitetaux = 0CalculerTaux(volume, capacite)Output "Taux : " & ToFixed(taux, 1) & " %"EndCalculerTauxReal resultatresultat = v / c * 100Return resultat

Un programme qui appelle CalculerTaux, puis affiche le taux.

Pour un volume de 21500 L et une capacité de 28274 L, ce programme affiche un taux nul, alors que la fonction est juste. Où est l'erreur ?

La fonction calcule bien resultat et le rend. Mais la forme Call sert aux fonctions qui ne retournent rien : ici, la valeur rendue n'est rangée nulle part. La variable taux du programme principal garde la valeur 0 qu'elle avait reçue avant l'appel, et le programme affiche Taux : 0.0 %.

La correction remplace la forme Call par une affectation : la valeur rendue est rangée dans taux, et l'initialisation devient inutile.

Organigramme, tel que l'affiche Flowgorithm
Exécuter comme dans Flowgorithm, en continu ou forme par forme.
MainReal volume, capacite, tauxInput volumeInput capacitetaux = CalculerTaux(volume, capacite)Output "Taux : " & ToFixed(taux, 1) & " %"EndCalculerTauxReal resultatresultat = v / c * 100Return resultat

La version corrigée. Exécuter les deux programmes avec les mêmes saisies pour voir le taux passer de 0.0 à 76.0 %.

Le programme affiche Taux : 76.0 %. Sans l'initialisation taux ← 0, l'erreur serait plus visible : l'exécution s'arrêterait sur la sortie, qui lirait une variable n'ayant jamais reçu de valeur. Donner à resultat le nom taux n'y changerait rien : la variable de retour appartient à la fonction, et le programme principal ne la voit pas. Une valeur rendue se récupère seulement en plaçant l'appel dans une expression.

Vérification

Vérification rapideon peut se reprendre

1.La fonction CalculerTaux retourne une valeur. Comment récupérer cette valeur dans le programme principal ?

2.Dans l'appel ConvertirDebit(pompeEntree), avec l'en-tête ConvertirDebit(litresMinute en Real), qu'est-ce que pompeEntree ?

3.Quelles valeurs Random(10) peut-il rendre ?

4.Une fonction utilise une variable déclarée dans le programme principal. Que se passe-t-il ?

5.Une fonction déclare « retourne resultat en Real », mais range son calcul dans une variable nommée produit. Que se passe-t-il ?

6.Une fonction qui affiche un bilan et ne retourne rien : comment l'appeler ?

La méthode

  1. Repérer ce qui se répète : une même formule écrite deux fois, ou une étape qu'on aimerait nommer. C'est le signal qu'une fonction est utile.
  2. Lister ce qu'elle reçoit et ce qu'elle rend, en français, avant de dessiner : « elle reçoit un volume et une capacité, elle rend un pourcentage ».
  3. Écrire l'en-tête : un nom qui dit l'action, un paramètre par valeur reçue avec son type, et le nom et le type de la variable de retour s'il y en a une.
  4. Déclarer la variable de retour dans la fonction, puis la remplir par une affectation. Il n'existe pas de forme Return : relire l'en-tête et vérifier que le nom qui y figure reçoit bien une valeur.
  5. Appeler dans une expression si la fonction rend une valeur, par la forme Call sinon. Une valeur rendue qu'on ne range nulle part est perdue sans message.
  6. Ne compter sur aucune variable extérieure : tout ce dont la fonction a besoin passe par ses paramètres, dans l'ordre de l'en-tête.
  7. Vérifier la fonction seule, sur deux ou trois valeurs choisies, avant de la brancher dans le programme.

Synthèse

  • Une fonction intégrée est fournie par le logiciel : elle reçoit des arguments et rend une valeur, sans qu'on sache comment. Random(n) rend un entier de 0 à n - 1, et se remplace par une saisie dès qu'il faut vérifier un cas.
  • Une fonction écrite par le lecteur a son propre diagramme, avec sa pilule de début portant l'en-tête et sa pilule de fin.
  • Un paramètre est une variable de la fonction, déclarée dans l'en-tête ; un argument est la valeur transmise à l'appel. La transmission se fait par position, et le nombre doit correspondre.
  • Un appel se déroule en quatre temps : les arguments sont évalués, les paramètres les reçoivent, la fonction s'exécute, et la valeur rendue remplace l'appel dans l'expression.
  • Il n'existe pas de forme Return : la fonction rend le contenu de sa variable de retour, celle que l'en-tête nomme, et qu'une affectation doit avoir remplie.
  • Une fonction qui rend une valeur s'appelle dans une expression ; une fonction qui ne rend rien s'appelle par la forme Call. Se tromper de forme perd la valeur, sans message.
  • Les variables sont locales à leur diagramme : deux variables de même nom de part et d'autre restent distinctes, et rien ne traverse en dehors des paramètres et de la valeur rendue.

Mettre en pratique