Les fonctions

Dans ce cours, nous allons voir comment écrire et manipuler des fonctions. En effet, les programmes que nous avons écrit jusqu’ici ne permettaient que de résoudre de petits problèmes. L’intégralité du code se trouvait dans … la fonction main. Pour résoudre des problèmes plus grands (d’une plus grande complexité, comportant de nombreuses lignes de code), il est nécessaire de réfléchir à la structure du programme, de le rationaliser, le simplifier, diminuer la quantité de code tout en facilitant sa lisibilité. Pourquoi se soucier de la lisibilité ? Tout simplement parce qu’un projet, un vrai, un gros, se fait en équipe. Vos collègues vont devoir lire votre code, et le lire vite. Mais écrire un code qui soit facilement lisible, ce n’est pas si simple.

Le premier réflexe d’un étudiant lorsqu’on lui donne un projet, c’est de se mettre directement à programmer. Au fur et à mesure de l’évolution du projet, et pour contourner les problèmes rencontrés (logiques, techniques), des copier/coller vont apparaitre, générant ainsi du code redondant. Le code va s’alourdir jusqu’à devenir illisible, mais surtout… il deviendra très difficile à appréhender dans sa globalité ! Et lorsque l’enseignant propose d’ajouter une fonctionnalité non prévue au départ, on se rend compte que c’est mission impossible. La solution ? Décomposer le programme en sous-problèmes : le principe, bien connu, de diviser pour régner !

Nous allons apprendre à écrire des fonctions et à les réutiliser. c’est à dire réaliser et ranger des morceaux de code qui seront réutilisables, très simples à debugger, ne faisant qu’une seule chose à la fois et surtout, très simples à comprendre.

Prérequis

Pourquoi écrire des fonctions ?

Tout au long de ce cours, nous allons modifier, améliorer un programme et l’utiliser comme un fil rouge. Pouvez vous me dire ce que fais ce programme ? Quelles sont les valeurs des variables fact5, fact9 et fact15 à la fin du programme ?

Exemple de programme avec du code redondant

Ce programme va calculer 5! (prononcer 5 factorielle) et afficher sa valeur puis 9! et afficher sa valeur et enfin 15! et afficher sa valeur. Pour en savoir plus sur la fonction factorielle vous pouvez consulter la page Wikipédia.
Quelques explications :

  • lignes 7 à 10 : La variable fact5, initialisée à 1 lors de sa déclaration est multipliée à chaque tour de boucle par la valeur de i. La variable i est initialisée à 1, et augmente de 1 à chaque tour de boucle jusqu’à arriver à 5 (inclus). Ainsi fact5 va être multipliée par 1, puis le résultat, stocké dans fact5 sera à son tour multiplié par 2, et ainsi de suite. Après la boucle, fact5 vaut 1x1x2x3x4x5=5! .
  • ligne 11 : On affiche la valeur de la variable fact5.
  • lignes 12 à 15 : Comme pour les lignes 7 à 10, sauf que la boucle ne s’arrête pas lorsque i vaut 6, mais lorsque i vaut 10.
  • ligne 16 : On affiche la valeur de la variable fact9.
  • lignes 17 à 20 : Comme pour les lignes 7 à 10, sauf que la boucle ne s’arrête pas lorsque i vaut 6, mais lorsque i vaut 16.
  • ligne 21 : On affiche la valeur de la variable fact15.

Pour calculer ces trois valeurs, ce programme répète pratiquement le même code. Les seules choses qui vont changer, ce sont :

  • La condition de sortie de la boucle (on continue tant que i<=5, i<=9 ou i<=15).
  • Le nom de la variable dans laquelle nous allons enregistrer le résultat.
Morceau de code redondant

Cette variable qui nous sert à enregistrer le résultat est un accumulateur. C’est à dire qu’elle nous sert calculer, étape par étape, la valeur souhaitée. En fait, le nom de cette variable importe peu. Nous pourrions économiser des variables (c’est à dire en utiliser un nombre moins important) en écrivant ceci:

Le même programme en économisant deux variables

Ce code va produire le même résultat que le programme initial. Donc, maintenant, la seule chose qui change, c’est bien la condition de sortie de la boucle. Or, cette condition de sortie peut tout à fait contenir une variable. Plutôt que d’écrire explicitement i<=5, i<=9 ou encore i<=15, nous pouvons utiliser une variable (que nous allons nommer nbite pour l'occasion). Cette variable contiendra le nombre d'itérations de notre boucle : [jbox color="white" title="Le même programme mais utilisant une variable pour determiner le nombre d'itérations de chaque boucle" ]

[/jbox] Cette fois ci, le code utilisé pour calculer 5!, 9! et 15! est quasiment identique ! La seule différence provient de l'affectation de la variable nbite aux lignes 7, 14 et 21. Imaginons maintenant qu'une erreur se soit glissée dans un morceau de code redondant. Pour réparer cette erreur vous allez être obligés de corriger chaque morceau de votre code. Dans notre cas, le code de la boucle est répété 3 fois, donc humainement c'est tout à fait gérable. Mais si vous êtes intégrés dans un projet d'envergure, comportant des milliers de lignes de code, cela sera très long et laborieux. Pire, vous risquez de corriger votre code "presque" partout... oubliant juste un morceau. Votre projet risque de ne jamais aboutir, et je parle d’expérience... De manière générale, vous devez vous souvenir d'un principe connu de tous les développeurs de l'industrie : [jbox color="blue" title="Copier/coller du code, c'est copier/merder..." ] Depuis que j'enseigne, vous n'imaginez pas le nombre de fois qu'un étudiant (sérieux en plus) ne comprenait pas pourquoi son programme ne fonctionnait pas comme attendu, alors que l'origine du problème venait d'un copier/coller mal fait. Je sais que ça semble bête, mais en copiant du code, on oubli souvent de modifier les noms des variables ou alors on place le code avant, et non après une accolade fermante etc... Bref, évitez le copier/coller ! [/jbox]
La solution ? Écrire une fonction !

Qu’est-ce qu’une fonction ?

Une fonction est un objet qui, à partir d’une liste d’arguments va produire un résultat. C’est à dire un affichage et/ou le calcul d’un résultat :

L’un des points forts d’une fonction est qu’elle va contenir un code, un ensemble d’instructions, qui seront exécutées lors de l’appel de la fonction. Les valeurs des arguments utilisés lors de son appel (les paramètres effectifs) vont déterminer les valeurs des variables d’entrée de la fonction (les paramètres formels), utilisés durant l’exécution des différentes instructions de la fonction. Ainsi, le code d’une fonction va être écrit une seule fois, ce qui va éviter d’avoir du code redondant.

Voici comment définir une fonction

C’est au développeur d’écrire la fonction. De dire ce qu’elle va faire. Pour cela, il faut la définir :

Définition d'une fonction

Quelques explications :

  • Ligne 1 : Il s’agit du prototype de la fonction. Un prototype est toujours composé de trois parties :
    1. Le type de retour de la fonction. Une fonction peut retourner soit une valeur, soit rien du tout. Si la fonction retourne une valeur, alors cette valeur doit obligatoirement être du type «TypeDeRetour» (int, long, float, char, double …). Par exemple, une fonction qui va calculer le carré d’un entier devra avoir pour type de retour «int». Dans le cas d’une fonction ne retournant aucune valeur, il faut alors indiquer que le type de retour est « void ». C’est assez fréquent, par exemple lorsqu’une fonction se charge de réaliser un affichage.
    2. Le nom de la fonction. Vous pouvez presque donner le nom que vous voulez. Il doit seulement suivre les mêmes règles que pour les noms de variables. Cependant, il est très fortement conseillé de donner un nom qui soit représentatif de ce que fait la fonction. Par exemple, si la fonction retourne une valeur approchée de π, vous pouvez lui donner le nom pi_approx.
    3. La liste des paramètres formels. Il s’agit de la liste des arguments transmis à la fonction lors de son appel. Cette liste, qui peut être vide, représente les informations dont la fonction a besoin pour produire le résultat escompté. Par exemple, une fonction qui calcul l’indice de masse corporelle (IMC) d’un individu aura besoin de la taille en cm (un entier) et du poids en kg (un entier).
  • Ligne 2 à 9 : Les accolades des lignes 2 et 9 délimitent le corps de la fonction. Le corps de la fonction définit la suite des instructions nécessaires à la réalisation de la fonction.
  • Ligne 8 : Si la fonction retourne une valeur, alors chaque branche d’exécution doit se terminer par une instruction de retour de valeur : « return expression ; » où « expression » correspond au résultat retourné par la fonction. Le type de « expression » doit obligatoirement être le même que le type de retour de la fonction.
Très important

La définition d’une fonction doit se faire en dehors des autres fonctions.
Pour résumer :
  • Une fonction doit avoir un nom.
  • Elle attends en argument la liste des valeurs nécessaires à son exécution.
  • Elle peut retourner une valeur, ou rien du tout.

Voila ! Il ne nous reste plus qu’a revenir sur notre programme de calcul de factorielles. Nous allons prendre notre morceau de code redondant, et le placer dans une fonction. Notre fonction va calculer la factorielle d’un nombre entier. Donc nous allons lui donner le nom « factorielle ». Elle doit prendre un paramètre, l’entier dont on souhaite connaitre la factorielle. Enfin, elle nous retourne la factorielle du nombre passé en paramètre :

Le même programme en utilisant une fonction

Comment trouvez-vous ce programme désormais ? N’est-il pas plus simple ? Plus propre ? Plus logique ? Mieux structuré ? Plus lisible ?

Appel d’une fonction

Nous allons maintenant aborder un point un peu plus délicat. Dans le programme précédent, vous avez sans doute remarqué dans la fonction main ces trois lignes de code :

Appel de notre fonction factorielle


C’est ce que l’on nomme « l’appel de fonction ». Cela signifie que nous demandons à notre programme d’exécuter la fonction factorielle avec le paramètre (l’entier dont on souhaite connaitre la factorielle) et de placer la valeur retournée par la fonction dans la variable fact5 (ou fact9 pour le deuxième ligne ou fact15 pour la troisième). Sans ces appels, la fonction ne sera pas utilisée !

Très important

La définition d’une fonction n’exécute pas la suite d’instructions qui la compose. Pour exécuter le code d’une fonction, il est nécessaire de procéder à son appel.

Il est important de faire la distinction entre les paramètres formels et les paramètres effectifs. Les paramètres formels sont les variables déclarées dans le prototype de la fonction. Ces variables vont recevoir les paramètres effectifs. Les paramètres effectifs sont les valeurs (les expressions) que l’on donne lors de l’appel de la fonction.

Un paramètre effectif est soit

  • Une valeur (dans notre cas 5, 9 ou 15).
  • La valeur d’une expression.
  • Le retour d’un appel de fonction.

De plus, le nombre et l’ordre des paramètres formels et des paramètres effectifs d’une fonction doivent être identiques.

La définition d’une fonction doit toujours précéder son appel.
L’appel d’une fonction peut se faire uniquement à l’intérieur d’une autre fonction.
La seule fonction qu’il n’y a pas besoin d’appeler explicitement est la fonction « main ».

Les variables utilisées au sein d’une fonction doivent être déclarées comme variables locales en tête du corps de la fonction, ou doivent correspondre à des paramètres de la fonction.

Une fonction définit un morceau de programme permettant de résoudre un problème.
Ce problème peut lui-même être décomposé…
Meilleure organisation et lisibilité du code : le programme est décomposé en sous-programmes.

Plus facile à « débugger » : chaque fonction peut être testée séparément du reste du programme.

Réutilisabilité du code : il est possible de réutiliser des fonctions déjà écrites pour résoudre d’autres problèmes
(c’est le cas des bibliothèques de fonctions prédéfinies : printf, scanf, …).

Écrire le plus possible de fonctions, les plus petites possibles (réutilisation future). N’oubliez as que factoriser son code, c’est gagner du temps !
Toujours regarder s’il n’existe pas une fonction dans une bibliothèque qui résout le problème.
Toujours commencer par se demander quels sont les paramètres formels et le type de retour de la fonction.
Différencier les fonctions qui réalisent des affichages et les fonctions qui retournent des résultats.
Mettre un commentaire au-dessus de la déclaration pour expliquer ce que fait la fonction et comment elle s’utilise !

Leave a Reply