Interstices


  Découvrir

Demandez le programme

« Le temps que vous ayez décomposé une idée compliquée en petites étapes que même une machine stupide peut assimiler, vous avez certainement vous-même appris quelque chose à son propos. » (Douglas Adams, Un cheval dans la salle de bains, Gallimard, 2003)

Un ordinateur qui se bloque, un écran bleu, un logiciel qui se ferme intempestivement... c’est un « bug » ! Ce terme, qui signifie « cafard » (l’insecte) en anglais, désigne une erreur dans la programmation d’un logiciel ou, plus rarement, dans son algorithme. Personne n’y échappe ! Ainsi, le premier vol d’Ariane 5 en 1996 a explosé et détruit pour 500 millions de dollars de satellites. La faute était due à un morceau de programme qui convertissait une valeur. Créé pour Ariane 4, il a été réutilisé tel quel. La valeur à convertir, beaucoup plus grande dans le cas d’Ariane 5, a donné un résultat aberrant et des ordres de braquage maximum... On peut également citer la sonde américaine Venus Mariner 1, perdue en 1962 à cause d’un trait d’union oublié dans un programme... Mais quelle est donc cette activité mystérieuse si sujette aux erreurs ? La programmation.

Un petit programme en C. © DocSciences
Le texte visible sur l’écran de l’ordinateur est un programme, c’est une liste d’ordres donnés à la machine. Par exemple, ici, le programme sert à trouver parmi plusieurs noms lequel est le plus long.

C’est elle qui spécifie, dans un langage très précis, ce que l’ordinateur doit exécuter. D’un algorithme décrit verbalement ou avec un dessin, la programmation fait une liste d’ordres que la machine va suivre pour obtenir la réponse désirée (valeur, image, application, etc.). Les programmes sont absolument nécessaires à notre interaction avec l’ordinateur, ils sont partout : un jeu est un programme au même titre qu’un navigateur (ou « butineur » comme Firefox, Internet Explorer...) ou qu’un système d’exploitation (Linux, Mac OS, Windows...), programme très long et compliqué, mais qui, comme pour un jeu, est un texte écrit dans un certain langage.

Le langage de la machine

Le seul langage compris par le processeur, l’unité centrale dans laquelle sont câblées toutes les opérations de base de l’ordinateur, est appelé « assembleur ». Il définit simplement les opérations électroniques que peut exécuter le processeur. Ce langage change d’un processeur à un autre. Il est donc très efficace puisque tout peut être ajusté « sur mesure », mais il est nettement plus compliqué à manipuler que des langages de plus haut niveau, sans faire d’erreur.

L’assembleur est une liste d’ordres, un fichier d’instructions, comme additionner deux valeurs, les comparer... L’une des instructions permet de sauter à un autre ordre de la liste. Cela permet par exemple de faire des boucles comme « Frapper un monstre jusqu’à ce qu’il disparaisse. » ou « Avancer jusqu’à rencontrer un mur. ».

Cette dernière boucle peut également s’écrire : « Avancer. S’il n’y a pas de mur en face, retourner à la ligne précédente. Tourner à gauche. ». Ainsi, on ne tournera à gauche que lorsqu’on aura rencontré un mur. Mais ce genre de programme pose une question : que se passe-t-il si l’on rencontre un obstacle autre qu’un mur ? En effet, si un humain peut penser à contourner cet obstacle, un tel comportement n’a pas été programmé et le programme va donc tenter désespérément de passer à travers. Dans ce cas absurde, le programme exécutera bêtement l’instruction jusqu’à ce qu’il soit arrêté de l’extérieur (par le système d’exploitation ou par l’utilisateur).

Le compilateur, le traducteur homme-machine

Les langages actuels sont de plus haut niveau, ils permettent de préciser plus facilement toutes les actions à exécuter. Cependant, ces langages doivent eux-mêmes être exécutés. Pour cela, le programme source, qui n’est qu’un texte, va être « compilé ». Cela signifie qu’un programme (un de plus !), appelé « compilateur », va transformer le texte du programme source en une suite d’instructions en langage assembleur exécutables par l’ordinateur. Le compilateur joue un rôle de traducteur entre ce qui est écrit par le programmeur et ce qui est compris par la machine. Le compilateur produit un fichier exécutable que nous lançons pour utiliser le programme. De même qu’il y a de nombreuses langues (français, arabe, russe, japonais...), il y a plusieurs types de programmation, c’est-à-dire plusieurs façons de donner des ordres à l’ordinateur. Ces modes de programmation sont appelés « paradigmes ». Le choix du langage et du paradigme de programmation dépend de l’application, de l’algorithme, mais aussi de l’interaction du programme avec d’autres programmes et enfin du choix et de l’expertise du programmeur.

Deux étapes de création de personnages virtuels. © INRIA - CNRS / ARTIS
La partie gauche de l’image est obtenue grâce à un programme. À partir de la trame de droite, ce programme colore les personnages, calcule ce qui se réfléchit sur les boules, les ombres... ce qui rend l’image réaliste.

Les langages et les paradigmes

Le premier paradigme est le fait d’avoir, comme en langage assembleur, une suite d’instructions interprétées dans l’ordre. Ces instructions sont par exemple des ordres permettant de modifier une valeur dans la mémoire de l’ordinateur : si un personnage de jeu vidéo meurt, son nombre de vies restantes doit diminuer d’une unité. Ainsi, selon le programme et les valeurs de la mémoire de l’ordinateur, cette mémoire va être petit à petit modifiée. Ces changements de l’état mémoire sont les instructions de base du paradigme appelé « impératif ». À cela s’ajoutent des boucles de plus haut niveau : « tant que » et « pour ».

Des robots footballeurs.
© INRIA / MAIA - Photo R. Lamoureux
Chacun de ces petits robots footballeurs est programmé simplement. Leur atout est leur capacité à collaborer, ils partagent leurs expériences et ainsi ils « découvrent » des comportements nouveaux.

Dans le deuxième paradigme, toute donnée informatique est un objet composé de valeurs (appelées attributs) et de mini-programmes (appelés méthodes) qui vont modifier ou accéder aux attributs de l’objet ou communiquer avec d’autres objets. C’est une façon différente d’appréhender la programmation appelée « paradigme objet ». Au lieu d’une unique liste d’instructions, on a des objets avec leurs caractéristiques qui s’envoient des messages. On peut comparer cela à un jeu de stratégie où chaque unité a ses caractéristiques propres (santé, attaque, défense) et peut interagir avec d’autres (attaque, réparation). Cela simplifie l’utilisation de programmes déjà écrits et permet également de cacher des détails techniques aux autres programmeurs. Ainsi, on peut comparer cela à utiliser des gants en écaille de dragon dans un jeu de rôle. Quelle que soit la façon dont on a obtenu les gants (la façon de programmer la fonction) : achat, vol, combat contre un dragon, le joueur en profitera de la même manière (la fonction donne la même réponse).

Dans le troisième paradigme, on voit le programme comme une évaluation mathématique de fonctions et les programmes ressemblent plus à l’intuition mathématique qu’on peut en avoir. Ce paradigme, dit « fonctionnel », ressemble à l’écriture d’une fonction mathématique qui prend une ou des valeurs en entrée et renvoie un résultat : on ne gère plus la mémoire ni certains détails de bas niveau. Ce paradigme repose sur la notion de « typage » : si un sort magique nécessite une plume de phénix, il est dangereux de la remplacer par de la bave de crapaud. En termes informatiques, cela signifie que lorsqu’une fonction prend en entrée un nombre entier (comme la multiplication par 2 par exemple), on ne peut pas lui donner un nom de fleur par exemple, sous peine de faire échouer le programme. En effet, comment multiplier par 2 un nom de fleur ? Heureusement, un tel échec est évité par le compilateur, qui signale le problème. Il est impossible d’obtenir un exécutable à partir d’un programme mal typé. Ce mécanisme permet d’éviter de nombreuses erreurs.

Un quatrième paradigme est la programmation logique, représentée par le langage « Prolog ». L'idée est de déclarer des faits, des implications et des règles de type logique. En réponse à une requête, le démonstrateur utilise ces règles et ces faits pour fournir une réponse. Par exemple, un problème d'emploi du temps peut être décrit de cette façon (nombre d'élèves, types de salles, nombre de professeurs, durée des cours...). C'est une programmation plus souple et plus déclarative qui est beaucoup utilisée en intelligence artificielle.

Un cinquième paradigme est utilisé dans les systèmes embarqués, comme les avions ou les voitures. Le paradigme « synchrone » permet de spécifier des événements, leurs durées, la façon dont ils se suivent et se déclenchent. Cela permet de bien formaliser un système qui interagit avec l’environnement, et fonctionne sans s’arrêter en fonction des entrées qu’il reçoit, par exemple de capteurs. Les langages de programmation modernes utilisent un ou plusieurs paradigmes. Par exemple, des langages comme « C » ou « Fortran » ne sont qu’impératifs, « Haskell » n’est que fonctionnel alors que « OCaml » est à la fois impératif, objet et fonctionnel.

L'erreur est humaine

Pour faciliter la programmation et réduire la possibilité d’erreur, il existe de nombreux outils : des programmes eux aussi ! Ces environnements de développement permettent d’aider le programmeur. Ils contiennent typiquement en plus du compilateur, un éditeur de texte coloré, pour mettre en relief les éléments du langage, et des débogueurs qui surveillent la façon dont un programme s’exécute et débusquent les bugs, invisibles au premier coup d’œil. Les outils peuvent également contenir une aide à la création d’interfaces graphiques (boutons, menus déroulants...). Ils peuvent faciliter la gestion des différentes versions des programmes et permettre de gérer de gros projets logiciels (nombreux fichiers, nombreux programmeurs). Certains sont capables de déceler des variables non utilisées ou de faire choisir les éléments à utiliser dans des menus.

Indétrônable clavier.
© INRIA / C. Lebedinsky

Les programmeurs ne sont que des hommes et des femmes. L’erreur est possible, elle peut résulter d’une faute de frappe ou être plus subtile : une fonction qui présuppose qu’une valeur est positive ne peut pas donner un résultat satisfaisant avec une valeur négative. Enfin l’erreur peut découler d’une mauvaise communication entre fonctions ou entre programmeurs, par exemple si une distance est exprimée en miles alors que l’on pense qu’elle l’est en kilomètres, les résultats seront évidemment faussés.

Pour déceler ces bugs, les programmes sont testés. Ils sont utilisés dans des conditions normales, pour voir s’ils se comportent correctement. Mais comment tester tous les cas et tous les comportements ? C’est le plus souvent impossible et les programmes sont fréquemment victimes de bugs plus ou moins coûteux, même dans les domaines les plus sensibles.

Pour résoudre ces problèmes, diverses techniques de génie logiciel peuvent être utilisées. Il existe des formalismes de modélisation, visant à décrire de façon simple et compréhensible ce que doit faire chaque morceau du programme avant de le programmer. Il existe aussi des méthodes formelles plus sûres : ainsi, une telle méthode, la méthode B, a été utilisée pour concevoir la ligne 14 (Météor) du métro parisien. On peut aussi spécifier de façon précise et logique ce que doit faire chaque morceau de programme. Cette programmation par contrat nécessite une preuve mathématique que cette spécification est remplie.

Pour plus de garantie, cette preuve doit être expliquée et vérifiée par l’ordinateur : on est ainsi sûr de n’oublier aucun sous-cas et de ne pas faire d’erreur d’inattention lors de la démonstration. On a ainsi l’assurance que le programme n’échouera pas et qu’il ne fera pas échouer les éventuels programmes qui l’utilisent. Ces programmes formellement prouvés offrent un très haut niveau de garantie.

En conclusion, la programmation n’est pas seulement une simple traduction de l’algorithme vers la machine. La façon de faire, la gestion de la mémoire, les types de données peuvent rendre un même algorithme lent ou très efficace. La programmation introduit également des bugs aux conséquences parfois dramatiques. Les éviter reste un vrai défi, un défi scientifique.

Pour aller plus loin, nous vous proposons de consulter sur Wikipédia l'article Langage de programmation.

Cet article est paru dans la revue DocSciences n°5 Les clés de la révolution numérique, éditée par le CRDP de l'Académie de Versailles en partenariat avec Inria.