Algorithmique (12/14) - Fonctions et procédures (sous-programmes)

Cette leçon d’algorithmique explique pourquoi et comment factoriser le code redondant à l’aide de sous‑programmes, en distinguant procédures et fonctions. Elle montre d’abord les limites du copier‑coller et introduit la notion de procédure : nom, bloc de variables locales, corps, appel, et passage d’arguments pour paramétrer le comportement (exemple d’une ligne de caractères soulignés). La différence avec les fonctions est ensuite détaillée : les fonctions retournent une valeur, ce qui permet de réutiliser le résultat dans d’autres calculs. Des exemples concrets illustrent ces concepts : une fonction factorielle itérative, son usage pour calculer des combinaisons, puis une version récursive de la même fonction avec explication du mécanisme d’appel et de la condition d’arrêt. Le cours aborde également la portée des variables (locales vs globales) et insiste sur les avantages de la réutilisabilité, de la maintenabilité et de la clarté du code grâce aux sous‑programmes.
Chapters
Bonjour à tous et bienvenue dans cette nouvelle leçon d'algorithmique. Aujourd'hui nous allons voir les procédures et fonctions. Dans les grands programmes, il y a souvent des bouts de code qui peuvent se répéter à plusieurs endroits. Si ces morceaux de code contiennent de nombreuses lignes, alors on peut les copier puis les coller aux endroits souhaités. Mais cette pratique révèle aussitôt ses limites et ne constitue pas une bonne solution pour un code durable et facilement maintenable. Pour mieux comprendre le principe, imaginons que l'on veut créer un algorithme qui demande à l'utilisateur de saisir ses informations personnelles comme le nom, le prénom, l'adresse, etc. Après chaque information saisie, on souhaite tracer une ligne faite par un nombre défini de caractères soulignés. On dira par exemple que l'on veut afficher 20 caractères soulignés à chaque fois. On peut donc imaginer notre algorithme comme ceci: Bien évidemment, il aurait été plus facile d'écrire une chaîne qui contient 20 caractères soulignés au lieu d'utiliser une boucle Pour. Mais l'utilité de la boucle sera dévoilée un peu plus loin dans cette vidéo. De toute façon, vous avez certainement remarqué que le code de la boucle Pour a été répété à l'identique 3 fois dans l'algorithme. Avec un copier-coller ça sera un jeu d'enfant et on ne sentira pas trop la redondance lors de la saisie. Mais imaginez un instant que l'on souhaite afficher cette ligne une dizaine de fois, voire plus, dans ce cas la redondance sera plus perceptible. En fait, le problème ne réside pas dans la redondance en soi, bien que celles-ci rendent le code un peu plus moche, mais le plus grand problème c'est que le jour où vous déciderez de modifier la forme de la ligne en agissant sur le nombre de caractères ou sur la valeur de ceux-ci, alors là, il faudra passer en revue tous ces blocs de code que vous avez répétés un peu partout afin de les modifier un à un. Figurez-vous que le code redondant dans notre exemple est trop petit, s'il contenait des dizaines voire des centaines de lignes alors là ça sera toute une autre histoire. La solution à ce problème consiste à factoriser le code redondant, c'est-à-dire que l'on va le déclarer une seule fois dans notre algorithme ou programme et à chaque fois que l'on en aura besoin on va tout simplement l'invoquer. Ce code factorisé est connu sous le nom de sous-programme. En fait, il s'agit d'une sorte de programme que l'on va intégrer dans un programme plus grand que l'on appelle le programme principal et que l'on va exécuter à la demande. Ces sous-programmes peuvent être soit des procédures ou des fonctions. Le concept de procédure et fonction est relativement similaire, à une différence près que l'on va voir plus loin. Donc, pour notre exemple nous allons créer un sous-programme qui dans ce cas sera une procédure. Quand je dis sous-programme, alors il a la même ossature qu'un programme ordinaire, c'est-à-dire qu'il a un nom, un bloc pour déclarer les variables et un corps délimité par début et fin comme ceci. Le mot-clé Procedure indique qu'il s'agit d'une procédure et non pas d'une fonction. Le nom de la procédure doit respecter les mêmes règles que pour les noms de variables. Concernant les parenthèses situées juste après, nous allons voir leur utilité un peu plus loin. Le bloc Variables permet de déclarer toutes les variables que l'on invoquera dans la procédure courante. Elles peuvent être des variables scalaires ou des tableaux. Notez que l'ensemble des variables déclarées dans une procédure sont dites variables locales, c'est-à-dire qu'elles sont prises en compte localement au sein de la procédure courante, donc si on veut y accéder de l'extérieur de celle-ci, alors elles seront comme inexistantes. Notez qu'il existe aussi ce que l'on appelle des variables globales, c'est-à-dire qu'elles sont reconnues partout dans le programme y compris à l'intérieur des procédures qui y sont déclarées. Le fait qu'une variable soit locale ou globale est connu en programmation par le terme portée de la variable, et cette portée est gérée différemment d'un langage de programmation à un autre. Et pour faire simple, on va considérer que les variables déclarées dans nos procédures sont locales. À titre d'exemple, la procédure qui permet de dessiner la ligne de tout à l'heure sera déclarée de cette manière: Maintenant, afin d'exécuter cette procédure et afficher ainsi la ligne, nous allons simplement l'invoquer en déclarant son nom comme ceci. On dit alors qu'on a appelé la procédure. Celle-ci sera exécutée au moment de l'appel et affichera à l'écran nos 20 caractères soulignés. En algorithmique, on préfère souvent déclarer nos procédures entre la déclaration du nom de l'algorithme et le bloc consacré aux variables de celui-ci. Donc, notre algorithme qui permet à l'utilisateur de saisir ses informations personnelles sera fait de cette manière. Vous avez donc remarqué comment nous avons factorisé le code qui permet de tracer notre ligne dans une seule et unique procédure que l'on a appelée 3 fois dans le corps de l'algorithme. Désormais, si on souhaite modifier le comportement de notre procédure, il suffit d'agir sur son code dans un seul endroit. Jusqu'ici, nous avons pu factoriser et réutiliser le code de traçage de la ligne plusieurs fois. Cependant, à l'appel de la procédure, celle-ci agira toujours de la même façon, c'est-à-dire qu'elle va afficher 20 caractères soulignés à chaque fois. Or, il serait mieux qu'au moment de l'appel, on exprime à notre procédure la nature des caractères à afficher et le nombre de fois que l'on souhaite le faire. En procédant ainsi, on pourra changer son comportement sans toucher à son code. C'est là où nous aurons besoin des parenthèses qui suivent le nom de la procédure. Ces parenthèses permettent de renfermer les arguments. Ces derniers sont des variables qui seront utilisées au sein de la procédure, mais leurs valeurs sont spécifiées au moment de l'appel. En général, pour spécifier les arguments on procède de cette manière: les arguments sont spécifiés avec leur type et ils sont séparés par une virgule. Au moment de l'appel, il faut renseigner la valeur de chacun des arguments tout en veillant sur le respect du type défini comme ceci: Dans ce cas, on affecte systématiquement la valeur 1 à l'argument 1, la valeur 2 à l'argument 2, etc. On dit alors qu'on a passé la valeur 1 à l'argument 1 et la valeur 2 à l'argument 2. On réécrit maintenant notre algorithme de cette manière. Lors du premier appel, l'argument nbr vaut 20 et l'argument car vaut souligné. Dans la procédure, on remplace les 2 arguments par leurs valeurs et on exécute le traitement. On affiche donc 20 caractères soulignés. De la même manière, on affiche 15 astérisques au moment du deuxième appel, et 10 symboles+ lors du dernier appel. Vous avez donc vu qu'on a rendu notre procédure plus maniable, on a d'autant d'arguments. Elle peut donc agir différemment selon les valeurs qui sont passées à ceux-ci. Jusqu'ici, nous avons vu comment factoriser un code au sein d'une procédure, et comment appeler celle-ci en lui passant ou non des arguments, Mais imaginez qu'au moment de l'appel de notre procédure, on veut pas qu'elle affiche directement la ligne à l'écran, mais on veut juste qu'elle nous renvoie cette ligne-là. Ensuite, c'est à nous de décider si on veut l'afficher, l'affecter à une variable de notre choix, ou en faire autre chose. Si on veut avoir un tel comportement, alors on va utiliser les fonctions. Une fonction est un sous-programme qui peut aussi accepter des arguments et faire un traitement comme une procédure, mais à la différence de celle-ci, la fonction nous renvoie un résultat. Dans le jargon de la programmation on dit qu'elle retourne un résultat. En général, on représente une fonction de cette manière. La structure de la fonction est semblable à celle d'une procédure, cependant on constate la présence du type de retour juste après le nom de la fonction et la ligne Retourne valeur de retour dans son corps. En fait, quand on appelle une fonction on reçoit automatiquement la valeur qu'elle retourne. Cette valeur a un type, c'est le type de retour. Pour mieux comprendre le principe, imaginez que l'on veut créer une fonction qui permet de calculer la factorielle d'un nombre entier positif, mais au lieu d'afficher le résultat à l'écran, on veut récupérer ce résultat-là afin de l'inclure dans des opérations de calcul. On peut donc proposer la fonction suivante. Dans ce cas, quand notre fonction aura fini le traitement, elle retournera la variable R qui contient la factorielle du nombre passé en argument. Cette factorielle est un nombre entier, c'est pourquoi nous avons spécifié le type de retour de la fonction en tant qu'entier. Notez que dans ce cas aussi les variables R et i sont considérées comme locales. Désormais, si on appelle notre fonction par l'instruction suivante, alors on aura calculé la factorielle de 3 qui vaut 6, mais rien ne sera affiché à l'écran car nous n'avons rien fait de la valeur de retour qui est 6. Mais si on fait ça, dans ce cas on affiche à l'écran le message la factorielle de 3 vaut 6. Sinon, on peut juste affecter le résultat de retour à une variable de notre choix ou l'inclure dans une opération arithmétique tout simplement. À titre d'exemple, le programme qui permet de calculer la combinaison de k parmi n objets ressemblera à ceci. Vous avez sans doute compris l'avantage des fonctions dans cet exemple. Au lieu de calculer la factorielle de 3 nombres différents 3 fois dans l'algorithme, nous n'avons déclaré qu'une seule et unique fonction qui se charge de nous retourner le résultat, en lui passant en argument le nombre dont on souhaite calculer la factorielle. Dans l'une des vidéos où nous avons vu les structures itératives, j'ai mentionné que pour répéter un traitement plusieurs fois, on peut également se servir de fonctions récursives. La récursivité est le fait d'appeler une fonction depuis son corps, c'est-à-dire que notre fonction s'appelle par elle-même, ce qui donne lieu à un traitement répétitif. Si cette récursivité ne prend pas fin, alors on tombe dans une sorte de boucle infinie exactement comme c'est le cas pour les structures itératives mal formulées. À titre d'exemple, la fonction qui permet de calculer la factorielle peut être exprimée ainsi. Notez qu'une fonction prend fin si elle retourne quelque chose. Dans ce cas, tant que nbr ne vaut pas 0, alors la fonction retourne le nombre multiplié par la factorielle de ce même nombre moins 1. À ce stade, on devrait quitter la fonction, mais celle-ci est rappelée d'une manière récursive et se réexécute une fois de plus jusqu'à ce que nbr vaut 0. Dans ce cas, il renvoie 1 et la récursivité prend fin car aucun appel de la fonction n'est programmé. À la fin, on aura calculé nbr fois nbr- 1 fois nbr- 2 jusqu'à fois 1. J'espère que cette leçon vous a plu. Merci pour votre attention et à la vidéo prochaine.
More from Yamossoun

Algorithme de calcule de la racine carrée par dichotomie #dichotomy #squareroot #algorithm









