Google Plus

الصفحات

Affiliate Program ”Get Money from your Website”
Get Traffic Like Spam

الاثنين، 3 أكتوبر 2011

Cours d'algorithmique N°9 : procédures et fonctions

1. Sous-procédures
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 (et le contrôle qu’elle implique) d’une réponse par oui ou par non, peuvent être répétés dix fois à des moments différents de la même application. La manière la plus immédiate – et la moins habile – de résoudre la question est bien entendu de répéter le code correspondant autant de fois que nécessaire. Ainsi, la structure peut paraître simple. Mais elle est inutilement lourdingue, et en réalité, pour peu que le programme soit joufflu, il peut devenir parfaitement illisible. 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. Le corps du programme s’appelle alors la procédure principale, et ces groupes d’instructions auxquels on a recours s’appellent des sous-procédures. Reprenons cet exemple de question à laquelle l’utilisateur doit répondre par oui ou par non. Mauvaise Structure
...
Ecrire "Etes-vous marié ?"
Rep = ""
TantQue Rep <> " Oui" et Rep <> "Non"
Ecrire "Tapez Oui ou Non"
Lire Rep
FinTantQue
Ecrire "Avez-vous des enfants ?"
Rep = ""
TantQue Rep <> "Oui" et Rep <> "Non"
Ecrire "Tapez Oui ou Non"
Lire Rep
FinTantQue
Bonne Structure
Ecrire "Etes-vous marié ?"
RéponseOuiNon()
...
Ecrire
"Avez-vous des enfants ?"
RéponseOuiNon()
...
Procédure
RéponseOuiNon()
Rep = ""
TantQue Rep <> " Oui" et Rep <> "Non"
Ecrire "Tapez Oui ou Non"
Lire Rep
FinTantQue
Fin Procédure
Dans ce cas, on se contente donc d’appeler des lignes de codes qui se répètent à l’identique. Mais on peut avoir des cas beaucoup plus rigolos.
2. Passage de paramètres

Reprenons l’exemple qui précède et analysons-le. Nous écrivons un message, puis appelons la procédure pour poser une question ; puis plus tard, on écrit un autre message, et on relance la procédure pour poser la même question, etc. C’est une démarche acceptable, mais qui peut encore être améliorée : puisqu’avant chaque question, on doit écrire un message, autant que cette écriture du message figure directement dans la procédure appelée. Cela implique deux choses :
  • lorsqu’on appelle la procédure, on doit lui préciser quel message elle doit afficher avant de lire la réponse
  • la procédure 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 est un paramètre. Comme le paramètre (le message) est transmis de la procédure principale vers la sous-procédure, il s’agit d’un paramètre en entrée. Voilà comment l’affaire se présente : La procédure sera déclarée comme suit :
Procédure RéponseOuiNon(Msg en Caractère)
... Il y a donc dorénavant une variable, Msg, dont on précise le type, et qui signale à le procédure qu’un paramètre peut lui être envoyé. A présent, le corps de la procédure sera :
Procédure RéponseOuiNon(Msg en Caractère)
Ecrire Msg
Rep = ""
TantQue Rep <> " Oui" et Rep <> "Non"
Ecrire "Tapez Oui ou Non"
Lire Rep
FinTantQue
Fin Procédure
La procédure principale devient alors :
RéponseOuiNon("Etes-vous marié ?")
...
RéponseOuiNon("Avez-vous des enfants ?")
... Et voilà le travail. Là, on a passé qu’un seul paramètre en entrée, mais on peut en passer autant qu’on veut ; mais ne soyons pas gourmands, il suffit d’en passer autant qu’on en a besoin. D’un autre côté, on ne s’est guère préoccupé jusque là de récupérer le résultat de notre sous-procédure, à savoir la réponse à la question posée. Examinons maintenant ce problème. Une première solution serait la suivante : juste après l’appel de la procédure, on teste la variable Rep : Si Rep = "Oui" alors … L’inconvénient de cette technique, c’est que la variable Rep doit pouvoir conserver son contenu en voyageant d’une procédure à l’autre. On en reparlera plus loin mais ce genre de variable est extrêmement gourmand en mémoire vive. Donc, si on appelle comme cela plusieurs procédures, avec plein de variables qu’on veut récupérer, cela risque de coincer sérieusement à l’exécution. La solution la plus économique en ressources consiste à transmettre la valeur de la variable Rep de la sous-procédure vers la procédure principale, exactement comme on transmettait jusque là la valeur de la variable Msg de la procédure principale vers la sous-procédure. On introduit donc un second paramètre, mais qui est maintenant un paramètre en sortie. La déclaration de la procédure devient :
Procédure RéponseOuiNon(Msg en Caractère, Rep en Booléen)
L’appel devient quant à lui :
RéponseOuiNon("Etes-vous marié ?", toto)
si toto = "Oui" alors
...

RéponseOuiNon("Avez-vous des enfants ?", tata)
si tata = "Oui" alors
...
Là encore, rien n’empêche une sous-procédure de renvoyer plusieurs paramètres en sortie, vous avez toute liberté !
3. Fonctions personnalisées

Si vous n’avez qu’un seul paramètre à renvoyer en sortie, alors vous pourrez opter pour une solution encore plus légère : créer, plutôt qu’une sous procédure, une fonction personnalisée. Ce type de fonction, comme une fonction normale, renvoie une valeur. Mais cette fois, c’est vous qui créez la fonction, en indiquant le nombre et le type des paramètres en entrée, le mode de traitement et la valeur qui doit être renvoyée en sortie. Imaginons qu’à plusieurs reprises au cours d’une application, on ait besoin de calculer la moyenne d’un tableau de notes, le nombre de notes étant susceptible de varier d’une fois sur l’autre. Plutôt qu’écrire plusieurs boucles de calcul, qui à chaque fois risquent de se ressembler, il sera préférable de créer une fonction pour calculer cette moyenne. Cette fonction admettra deux arguments, qui lui sont indispensables pour effectuer ses calculs : Le tableau lui-même
Le nombre d’éléments du tableau
Cela donnera, en pseudo-code, la chose suivante :
Fonction Moy(Tab en tableau numérique, n en entier) Som = 0
Pour i = 1 à n
Som = Som + Tab(i)
i suivant
m = som / n
Renvoyer m
Fin Fonction On remarque au passage l’apparition d’un nouveau mot-clé : Renvoyer, qui indique quelle valeur doit prendre la fonction. Quant au programme principal, quelques lignes pourraient en être :
Tableau Notes en Numérique
....

moyenne = Moy(notes, nbnotes)
écrire "la moyenne est : ", moyenne
... D’une manière générale, la création de fonctions est un puissant outil de programmation, qui ne possède que des avantages : on économise des lignes de programmation, on rend le déboguage plus facile, et on ne gaspille aucune ressource de la machine. Pensez-y donc sérieusement à chaque fois que vous devez créer une application un peu joufflue (un projet, par exemple).
4. Une logique vicelarde : la programmation récursive

Vous savez comment sont les informaticiens : on ne peut pas leur donner quoi que ce soit sans qu’ils essayent de jouer avec, et le pire, c’est qu’ils y réussissent. Cette technique des fonctions personnalisées a donné lieu à la floraison d’une logique un peu particulière, destinée en particulier à traiter certains problèmes mathématiques (ou de jeux) : la programmation récursive. Pour vous expliquer de quoi il retourne, nous allons reprendre un exemple cher à vos cœurs : le calcul d’une factorielle (là, je sentais que j’allais encore me faire des copains). Rappelez-vous : la formule de calcul de la factorielle d’un nombre n s’écrit : N ! = 1 x 2 x 3 x … x n Nous avions programmé cela aussi sec avec une boucle Pour, et roule Raoul. Mais une autre manière de voir les choses, ni plus juste, ni moins juste, serait de dire que : N ! = n x (n-1) ! En bon français : la factorielle d’un nombre, c’est ce nombre multiplié par la factorielle du nombre précédent. Encore une fois, c’est une manière ni plus juste ni moins juste de présenter les choses ; c’est simplement une manière différente. Si l’on doit programmer cela, on peut alors imaginer une fonction Fact, chargée de calculer la factorielle. Cette fonction effectue la multiplication du nombre passé en argument par la factorielle du nombre précédent. Et cette factorielle du nombre précédent va bien entendu être elle-même calculée par la fonction Fact. Autrement dit, on va créer une fonction qui pour fournir son résultat, va s’appeler elle-même un certain nombre de fois. C’est cela, la récursivité. Toutefois, il nous manque une chose pour finir : quand ces auto-appels de la fonction Fact vont-ils s’arrêter ? Cela n’aura-t-il donc jamais de fin ? Si, bien sûr, rassure-toi, ô public, la récursivité, ce n’est pas Les Feux de L’Amour. On s’arrête quand on arrive au nombre 1, pour lequel la factorielle est par définition 1. Cela produit l’écriture suivante, un peu déconcertante certes, mais parfois très pratique :
Fonction Fact (N en Numérique)
Si
N = 1 alors
Renvoyer
1
Sinon
Renvoyer
Fact(N-1)
Finsi
Fin Fonction
Vous remarquerez que le processus récursif remplace en quelque sorte la boucle, c’est-à-dire un processus itératif. Et en plus, avec tous ces nouveaux mots qui riment, vous allez pouvoir écrire de très chouettes poèmes. Vous remarquerez aussi qu’on traite le problème à l’envers : on part du nombre, et on remonte à rebours jusqu’à 1 pour pouvoir calculer la factorielle. Cet effet de rebours est caractéristique de la programmation récursive. Pour conclure sur la récursivité, trois remarques fondamentales.
  • la programmation récursive, pour traiter certains problèmes, est très économique pour le programmeur ; elle permet de faire les choses correctement, en très peu de lignes de programmation.
  • en revanche, elle est très dispendieuse de ressources machine. Car à l’exécution, la machine va être obligée de créer autant de variable temporaires que de " tours " de fonction en attente.
  • Last but not least, et c’est le gag final, tout problème formulé en termes récursifs peut également être formulé en termes itératifs ! Donc, si la programmation récursive peut faciliter la vie du programmeur, elle n’est pas indispensable. Mais ça me faisait tant plaisir de vous en parler que je n’ai pas pu résister...
5. Variables publiques et privées
L’existence de sous-procédures, de paramètres, pose le problème de la " durée de vie " des variables, ce qu’on appelle leur portée. Pour faire simple, une variable peut être déclarée :
  • Comme privée, ou locale (c’est en général l’option par défaut). Cela signifie qu’une telle variable disparaît (et sa valeur avec) dès que prend fin la procédure ou elle a été créée.
  • Comme publique, ou globale. Ce qui signifie qu’une telle variable est conservée intacte pour toute l’application, au-delà des ouvertures et fermetures de procédures.
La manière dont ces déclarations doivent être faites est évidemment fonction de chaque langage de programmation. En pseudo-code algorithmique, vous pourrez utiliser le mot-clé Public pour déclarer une variable publique : Public Toto en Entier Comment choisir entre déclarer une variable en Public ou en Privé ? C’est très simple : les variables globales consomment énormément de ressources en mémoire. En conséquence, le principe qui doit présider au choix entre variables publiques et privées doit être celui de l’économie de moyens : on ne déclare comme publiques que les variables qui doivent absolument l’être. Et chaque fois que possible, lorsqu’on crée une sous-procédure, on utilise le passage de paramètres par valeur plutôt que des variables publiques.
6. Algorithmes fonctionnels

Pour clore ce chapitre, quelques mots à propos de la structure générale d’une application. Celle-ci va couramment être formée d’une procédure principale, et de sous-procédures (qui vont au besoin elles-mêmes en appeler d’autres, etc.). L’exemple typique est celui d’un menu, ou d’un sommaire, qui " branche " sur différents traitements, donc différentes sous-procédures. L’algorithme fonctionnel de l’application est la représentation graphique de cette structure générale, ayant comme objectif de faire comprendre d’un seul coup d’œil quelle procédure fait quoi, et quelle procédure appelle quelle autre. L’algorithme fonctionnel est donc en quelque sorte la représentation du squelette de l’application. Il se situe à un niveau plus général, plus abstrait, que l’algorithme normal, qui lui, détaille pas à pas les traitements effectués au sein de chaque procédure. Dans la construction – et la compréhension – d’une application, les deux documents sont indispensables, et constituent deux étapes successives de l’élaboration d’un projet. 



0 commentaires:

إرسال تعليق

earn monye

شارك

Twitter Delicious Facebook Digg Stumbleupon Favorites More