Algorithme:Fonctions personnalisées

Un article de WikiTuto.

Jump to: navigation, search

Sommaire

Explication

Une application, surtout si elle est longue, a toutes les chances de devoir procéder aux mêmes traitements, ou à des traitements similaires, à plusieurs endroits de son déroulement. Par exemple, la saisie d’une réponse par oui ou par non (et le contrôle qu’elle implique), peuvent être répétés dix fois à des moments différents de la même application.

La manière la plus évidente, mais aussi la moins habile, de programmer ce genre de choses, c'est bien entendu de répéter le code correspondant autant de fois que nécessaire. Apparemment, on ne se casse pas la tête : quand il faut que la machine interroge l'utilisateur, on recopie presque mot à mot les lignes de codes voulues, et roule Raoul. Mais en procédant de cette manière, la pire qui soit, on se prépare des lendemains qui déchantent...

D'abord, parce que si la structure d'un programme écrit de cette manière peut paraître simple, elle est en réalité inutilement lourdingue. Elle contient des répétitions, et pour peu que le programme soit joufflu, il peut devenir parfaitement illisible. Or, c'est un critère essentiel pour un programme informatique, qu'il soit facilement modifiable donc lisible, y compris - et surtout - par ceux qui ne l'ont pas écrit ! Dès que l'on programme non pour soi-même, mais dans le cadre d'une organisation (entreprise ou autre), cette nécessité se fait sentir de manière aiguë.

En plus, à un autre niveau, une telle structure pose des problèmes considérables de maintenance : car en cas de modification du code, il va falloir traquer toutes les apparitions de ce code pour faire convenablement la modification ! Et si l'on en oublie une, patatras, on a laissé un bug.

Il faut donc opter pour une autre stratégie, qui consiste à séparer ce traitement du corps du programme et à appeler ces instructions (qui ne figurent donc plus qu’en un seul exemplaire) à chaque fois qu’on en a besoin. Ainsi, la lisibilité est assurée ; le programme devient modulaire, et il suffit de faire une seule modification au bon endroit, pour que cette modification prenne effet dans la totalité de l’application.

Le corps du programme s’appelle alors la procédure principale, et ces groupes d’instructions auxquels on a recours s’appellent des fonctions et des sous-procédures (nous verrons un peu plus loin la différence entre ces deux termes).

Exemple

Reprenons un exemple de question à laquelle l’utilisateur doit répondre par oui ou par non.

Mauvaise Structure :
Ecrire "Etes-vous marié ?"
Rep1 ← ""
TantQue Rep1 <> "Oui" et Rep1 <> "Non"
  Ecrire "Tapez Oui ou Non"
  Lire Rep1
FinTantQue
Ecrire "Avez-vous des enfants ?"
Rep2 ← ""
TantQue Rep2 <> "Oui" et Rep2 <> "Non"
  Ecrire "Tapez Oui ou Non"
  Lire Rep2
FinTantQue

On le voit bien, il y a là une répétition quasi identique du traitement à accomplir. A chaque fois, on demande une réponse par Oui ou Non, avec contrôle de saisie. La seule chose qui change, c'est le nom de la variable dans laquelle on range la réponse. Alors, il doit bien y avoir un truc.

La solution consiste à isoler les instructions demandant une réponse par Oui ou Non, et à appeler ces instructions à chaque fois que nécessaire. Ainsi, on évite les répétitions inutiles, et on a découpé notre problème en petits morceaux autonomes.

Nous allons donc créer une fonction dont le rôle sera de renvoyer la réponse (oui ou non) de l'utilisateur. Ce mot de "fonction", en l'occurrence, ne doit pas nous surprendre : nous avons étudié précédemment des fonctions fournies avec le langage, et nous avons vu que le but d'une fonction était de renvoyer une valeur. Eh bien, c'est exactement la même chose ici, sauf que c'est nous qui allons créer notre propre fonction, que nous appellerons RepOuiNon :

 Fonction RepOuiNon() en caractère
Truc ← ""
TantQue Truc <> "Oui" et Truc <> "Non"
  Ecrire "Tapez Oui ou Non"
  Lire Truc
FinTantQue
Renvoyer Truc
Fin

On remarque au passage l’apparition d’un nouveau mot-clé : Renvoyer, qui indique quelle valeur doit prendre la fonction lorsqu'elle est utilisée par le programme. Cette valeur renvoyée par la fonction (ici, la valeur de la variable Truc) est en quelque sorte contenue dans le nom de la fonction lui-même, exactement comme c’était le cas dans les fonctions prédéfinies.

Une fonction s'écrit toujours en-dehors de la procédure principale. Selon les langages, cela peut prendre différentes formes. Mais ce qu'il faut comprendre, c'est que ces quelques lignes de codes sont en quelque sorte des satellites, qui existent en dehors du traitement lui-même. Simplement, elles sont à sa disposition, et il pourra y faire appel chaque fois que nécessaire. Si l'on reprend notre exemple, une fois notre fonction RepOuiNon écrite, le programme principal comprendra les lignes :

Ecrire "Etes-vous marié ?"
Rep1 ← RepOuiNon()
Ecrire "Avez-vous des enfants ?"
Rep2 ← RepOuiNon()

Et le tour est joué ! On a ainsi évité les répétitions inutiles, et si d'aventure, il y avait un bug dans notre contrôle de saisie, il suffirait de faire une seule correction dans la fonction RepOuiNon pour que ce bug soit éliminé de toute l'application. C'est-y pas beau, la vie ?

Toutefois, les plus sagaces d'entre vous auront remarqué, tant dans le titre de la fonction , que dans chacun des appels, la présence de parenthèses. Celles-ci, dès qu'on crée on qu'on appelle une fonction, sont obligatoires. Et si vous avez bien compris tout ce qui précède, vous devez avoir une petite idée de ce qu'on va pouvoir mettre dedans...

Passage d'arguments

Reprenons l’exemple qui précède et analysons-le. Nous écrivons un message à l'écran, puis appelons la fonction RepOuiNon pour poser une question ; puis, un peu plus loin, on écrit un autre message à l'écran, et on appelle de nouveau la fonction pour poser la même question, etc. C’est une démarche acceptable, mais qui peut encore être améliorée : puisque avant chaque question, on doit écrire un message, autant que cette écriture du message figure directement dans la fonction appelée. Cela implique deux choses :

  • lorsqu’on appelle la fonction, on doit lui préciser quel message elle doit afficher avant de lire la réponse
  • la fonction doit être « prévenue » qu’elle recevra un message, et être capable de le récupérer pour l’afficher.

En langage algorithmique, on dira que le message devient un argument de la fonction. Cela n'est certes pas une découverte pour vous : nous avons longuement utilisé les arguments à propos des fonctions prédéfinies. Eh bien, quitte à construire nos propres fonctions, nous pouvons donc construire nos propres arguments. Voilà comment l’affaire se présente...

La fonction sera dorénavant déclarée comme suit :

Fonction RepOuiNon(Msg en Caractère) en Caractère
Ecrire Msg
Truc ← ""
TantQue Truc <> "Oui" et Truc <> "Non"
  Ecrire "Tapez Oui ou Non"
  Lire Truc
FinTantQue
Renvoyer Truc
Fin Fonction

Il y a donc maintenant entre les parenthèses une variable, Msg, dont on précise le type, et qui signale à la fonction qu’un argument doit lui être envoyé à chaque appel. Quant à ces appels, justement, ils se simplifieront encore dans la procédure principale, pour devenir :

Rep1 ← RepOuiNon("Etes-vous marié ?")
Rep2 ← RepOuiNon("Avez-vous des enfants ?")
Et voilà le travail.

Une remarque importante : là, on n'a passé qu’un seul argument en entrée. Mais bien entendu, on peut en passer autant qu’on veut, et créer des fonctions avec deux, trois, quatre, etc. arguments ; Simplement, il faut éviter d'être gourmands, et il suffit de passer ce dont on en a besoin, ni plus, ni moins !

Dans le cas que l'on vient de voir, le passage d'un argument à la fonction était élégant, mais pas indispensable. La preuve, cela marchait déjà très bien lors de la première version. Nous allons voir maintenant une situation où il faut absolument passer deux arguments à une fonction si l'on veut qu'elle puisse remplir sa tâche.

Imaginons qu'à plusieurs reprises au cours du programme, on ait à calculer la moyenne des éléments de différents tableaux. Plutôt que répéter le code à chaque fois, on va donc créer une fonction Moy, chargée spécialement de calculer cette moyenne. Voyons voir un peu de quoi cette fonction a besoin :

  • du tableau, bien sûr. Comment calculer la moyenne de ses éléments sans cela ?
  • mais il faut également le nombre d'éléments du tableau, ou, au choix, l'indice maximal du tableau. Enfin, quelque chose qui permette à la fonction de savoir combien de tours de boucle elle devra faire.

Voilà donc une situation où la fonction a absolument besoin de deux arguments. Ecrivons-la, juste histoire de vérifier qu'on est bien d'accord :

Fonction Moy(T() en numérique, n en Entier) en Numérique
Som ← 0
Pour i ← 0 à n-1
  Som ← Som + T(i)
i suivant
m ← som / n
Renvoyer m
Fin Fonction

Quant aux différents appels dans la procédure principale, si j'ai un tableau Riri de 43 éléments, un tableau Fifi de 5 éléments et un tableau Loulou de k éléments, et que je range respectivement les moyennes dans les variables M1, M2 et M3, cela donnera :

M1 ← Moy ( Riri, 43)
M2 ← Moy (Fifi, 5)
M3 ← Moy (Fifi, k)

En fait, tout cela, c'est simple comme bonjour... Le plus important, c'est d'acquérir le réflexe de constituer systématiquement les fonctions adéquates quand on doit traiter un problème donné.

Cette partie de la réflexion s'appelle d'ailleurs l'analyse fonctionnelle d'un problème, et c'est toujours par là qu'il faut commencer : en gros, dans un premier temps, on découpe le traitement en modules, et dans un deuxième temps, on écrit chaque module. Cependant, avant d'en venir là, il nous faut découvrir un dernier outil, qui prend le relais là où les fonctions deviennent incapables de nous aider.

Exercices

Voir aussi

Source : Christophe Darmangeat

Boîte à outils
Annuaire gratuitCe site est listé dans la catégorie Informatique : Aide et astuces en informatique Annuaire