Chapitre 1 : Penser comme un programmeur▲
Nous allons introduire dans ce chapitre quelques concepts qu'il vous faut connaître avant de vous lancer dans l'apprentissage de la programmation. Nous avons volontairement limité nos explications afin de ne pas vous encombrer l'esprit. La programmation n'est pas difficile : il suffit d'un peu de méthode et de persévérance.
Une part importante de ce chapitre est traduite d'un chapitre similaire de « How to think like a computer scientist » de Downey, Elkner & Meyers.
1.1. La démarche du programmeur▲
Le but de ce cours est de vous apprendre à penser et à réfléchir comme un analyste-programmeur.
Ce mode de pensée combine des démarches intellectuelles complexes, similaires à
celles qu'accomplissent les mathématiciens, les ingénieurs et les scientifiques.
Comme le mathématicien, l'analyste-programmeur utilise des langages formels pour décrire des
raisonnements (ou algorithmes). Comme l'ingénieur, il conçoit des dispositifs, il assemble des
composants pour réaliser des mécanismes et il évalue leurs performances. Comme le scientifique, il
observe le comportement de systèmes complexes, il ébauche des hypothèses explicatives, il teste
des prédictions.
L'activité essentielle d'un analyste-programmeur est la résolution de problèmes.
Il s'agit là d'une compétence de haut niveau, qui implique des capacités et des connaissances
diverses : être capable de (re)formuler un problème de plusieurs manières différentes, être capable
d'imaginer des solutions innovantes et efficaces, être capable d'exprimer ces solutions de manière
claire et complète.
La programmation d'un ordinateur consiste en effet à « expliquer » en détail à une machine ce
qu'elle doit faire, en sachant d'emblée qu'elle ne peut pas véritablement « comprendre » un langage
humain, mais seulement effectuer un traitement automatique sur des séquences de caractères.
Un programme n'est rien d'autre qu'une suite d'instructions, encodées en respectant de manière très
stricte un ensemble de conventions fixées à l'avance que l'on appelle un langage informatique. La
machine est ainsi pourvue d'un mécanisme qui décode ces instructions en associant à chaque
« mot » du langage une action précise.
Vous allez donc apprendre à programmer, activité déjà intéressante en elle-même parce qu'elle
contribue à développer votre intelligence. Mais vous serez aussi amené à utiliser la programmation
pour réaliser des projets concrets, ce qui vous procurera certainement de très grandes satisfactions.
1.2. Langage machine, langage de programmation▲
A strictement parler, un ordinateur n'est rien d'autre qu'une machine effectuant des opérations
simples sur des séquences de signaux électriques, lesquels sont conditionnés de manière à ne
pouvoir prendre que deux états seulement (par exemple un potentiel électrique maximum ou
minimum). Ces séquences de signaux obéissent à une logique du type « tout ou rien » et peuvent
donc être considérés conventionnellement comme des suites de nombres ne prenant jamais que les
deux valeurs 0 et 1. Un système numérique ainsi limité à deux chiffres est appelé système binaire.
Sachez dès à présent que dans son fonctionnement interne, un ordinateur est totalement
incapable de traiter autre chose que des nombres binaires. Toute information d'un autre type doit
être convertie, ou codée, en format binaire. Cela est vrai non seulement pour les données que l'on
souhaite traiter (les textes, les images, les sons, les nombres, etc.), mais aussi pour les programmes,
c'est-à-dire les séquences d'instructions que l'on va fournir à la machine pour lui dire ce qu'elle doit
faire avec ces données.
Le seul « langage » que l'ordinateur puisse véritablement « comprendre » est donc très éloigné de
ce que nous utilisons nous-mêmes. C'est une longue suite de 1 et de 0 (les "bits") souvent traités par
groupes de 8 (les « octets »), 16, 32, ou même 64. Ce « langage machine » est évidemment
presqu'incompréhensible pour nous. Pour « parler » à un ordinateur, il nous faudra utiliser des
systèmes de traduction automatiques, capables de convertir en nombres binaires des suites de
caractères formant des mots-clés (anglais en général) qui seront plus significatifs pour nous.
Ces systèmes de traduction automatique seront établis sur la base de toute une série de
conventions, dont il existera évidemment de nombreuses variantes.
Le système de traduction proprement dit s'appellera interpréteur ou bien compilateur, suivant la
méthode utilisée pour effectuer la traduction (voir ci-après). On appellera langage de
programmation un ensemble de mots-clés (choisis arbitrairement) associé à un ensemble de règles
très précises indiquant comment on peut assembler ces mots pour former des « phrases » que
l'interpréteur ou le compilateur puisse traduire en langage machine (binaire).
Suivant son niveau d'abstraction, on pourra dire d'un langage qu'il est « de bas niveau » (ex :
Assembler) ou « de haut niveau » (ex : Pascal, Perl, Smalltalk, Clarion, Java...). Un langage de bas
niveau est constitué d'instructions très élémentaires, très « proches de la machine ». Un langage de
haut niveau comporte des instructions plus abstraites ou, plus « puissantes ». Cela signifie que
chacune de ces instructions pourra être traduite par l'interpréteur ou le compilateur en un grand
nombre d'instructions machine élémentaires.
Le langage que vous allez apprendre en premier est Python. Il s'agit d'un langage de haut niveau,
dont la traduction en codes binaires est complexe et prend donc toujours un certain temps. Cela
pourrait paraître un inconvénient. En fait, les avantages que présentent les langages de haut niveau
sont énormes : il est beaucoup plus facile d'écrire un programme dans un langage de haut niveau ;
l'écriture du programme prend donc beaucoup moins de temps ; la probabilité d'y faire des fautes est
nettement plus faible ; la maintenance (c'est-à-dire l'apport de modifications ultérieures) et la
recherche des erreurs (les « bugs ») sont grandement facilitées. De plus, un programme écrit dans
un langage de haut niveau sera souvent portable, c'est-à-dire que l'on pourra le faire fonctionner
sans guère de modifications sur des machines ou des systèmes d'exploitation différents. Un
programme écrit dans un langage de bas niveau ne peut jamais fonctionner que sur un seul type de
machine : pour qu'une autre l'accepte, il faut le réécrire entièrement.
1.3. Compilation et interprétation▲
Le programme tel que nous l'écrivons à l'aide d'un logiciel éditeur (une sorte de traitement de
texte spécialisé) sera appelé désormais programme source (ou code source). Comme déjà signalé
plus haut, il existe deux techniques principales pour effectuer la traduction d'un tel programme
source en code binaire exécutable par la machine : l'interprétation et la compilation.
- Dans la technique appelée interprétation, le logiciel interpréteur doit être utilisé chaque fois que l'on veut faire fonctionner le programme. Dans cette technique en effet, chaque ligne du programme source analysé est traduite au fur et à mesure en quelques instructions du langage machine, qui sont ensuite directement exécutées. Aucun programme objet n'est généré.
- La compilation consiste à traduire la totalité du texte source en une fois. Le logiciel compilateur lit toutes les lignes du programme source et produit une nouvelle suite de codes que l'on appelle programme objet (ou code objet). Celui-ci peut désormais être exécuté indépendamment du compilateur et être conservé tel quel dans un fichier (« fichier exécutable »).
Chacune de ces deux techniques a ses avantages et ses inconvénients :
L'interprétation est idéale lorsque l'on est en phase d'apprentissage du langage, ou en cours
d'expérimentation sur un projet. Avec cette technique, on peut en effet tester immédiatement toute
modification apportée au programme source, sans passer par une phase de compilation qui demande
toujours du temps.
Par contre, lorsqu'un projet comporte des fonctionnalités complexes qui doivent s'exécuter
rapidement, la compilation est préférable : il est clair en effet qu'un programme compilé
fonctionnera toujours nettement plus vite que son homologue interprété, puisque dans cette
technique l'ordinateur n'a plus à (re)traduire chaque instruction en code binaire avant qu'elle puisse
être exécutée.
Certains langages modernes tentent de combiner les deux techniques afin de retirer le meilleur de
chacune. C'est le cas de Python et aussi de Java. Lorsque vous lui fournissez un programme source,
Python commence par le compiler pour produire un code intermédiaire, similaire à un langage
machine, que l'on appelle bytecode, lequel sera ensuite transmis à un interpréteur pour l'exécution
finale. Du point de vue de l'ordinateur, le bytecode est très facile à interpréter en langage machine.
Cette interprétation sera donc beaucoup plus rapide que celle d'un code source.
Les avantages de cette méthode sont appréciables :
- Le fait de disposer en permanence d'un interpréteur permet de tester immédiatement n'importe quel petit morceau de programme. On pourra donc vérifier le bon fonctionnement de chaque composant d'une application au fur et à mesure de sa construction.
- L'interprétation du bytecode compilé n'est pas aussi rapide que celle d'un véritable code binaire, mais elle est très satisfaisante pour de très nombreux programmes, y compris graphiques.
- Le bytecode est portable. Pour qu'un programme Python ou Java puisse s'exécuter sur différentes machines, il suffit de disposer pour chacune d'elles d'un interpréteur adapté.
Tout ceci peut vous paraître un peu compliqué, mais la bonne nouvelle est que tout ceci est pris en charge automatiquement par l'environnement de développement de Python. Il vous suffira d'entrer vos commandes au clavier, de frapper <Enter>, et Python se chargera de les compiler et de les interpréter pour vous.
1.4. Mise au point d'un programme - Recherche des erreurs (« debug »)▲
La programmation est une démarche très complexe, et comme c'est le cas dans toute activité
humaine, on y commet de nombreuses erreurs. Pour des raisons anecdotiques, les erreurs de
programmation s'appellent des « bugs » (ou « bogues », en France)4, et l'ensemble des techniques
que l'on met en oeuvre pour les détecter et les corriger s'appelle « debug » (ou « déboguage »).
En fait, il peut exister dans un programme trois types d'erreurs assez différentes, et il convient
que vous appreniez à bien les distinguer :
4 "bug" est à l'origine un terme anglais servant à désigner de petits insectes gênants, tels les punaises. Les premiers ordinateurs fonctionnaient à l'aide de "lampes" radios qui nécessitaient des tensions électriques assez élevées. Il est arrivé à plusieurs reprises que des petits insectes s'introduisent dans cette circuiterie complexe et se fassent électrocuter, leurs cadavres calcinés provoquant alors des court-circuits et donc des pannes incompréhensibles. Le mot français "bogue" a été choisi par homonymie approximative. Il désigne la coque épineuse de la châtaigne.
1.4.1. Erreurs de syntaxe▲
Python ne peut exécuter un programme que si sa syntaxe est parfaitement correcte. Dans le cas
contraire, le processus s'arrête et vous obtenez un message d'erreur. Le terme syntaxe se réfère aux
règles que les auteurs du langage ont établies pour la structure du programme.
Tout langage comporte sa syntaxe. Dans la langue française, par exemple, une phrase doit
toujours commencer par une majuscule et se terminer par un point. ainsi cette phrase comporte deux
erreurs de syntaxe
Dans les textes ordinaires, la présence de quelques petites fautes de syntaxe par-ci par-là n'a
généralement pas d'importance. Il peut même arriver (en poésie, par exemple), que des fautes de
syntaxe soient commises volontairement. Cela n'empêche pas que l'on puisse comprendre le texte.
Dans un programme d'ordinateur, par contre, la moindre erreur de syntaxe produit
invariablement un arrêt de fonctionnement (un « plantage ») ainsi que l'affichage d'un message
d'erreur. Au cours des premières semaines de votre carrière de programmeur, vous passerez
certainement pas mal de temps à rechercher vos erreurs de syntaxe. Avec de l'expérience, vous en
commettrez beaucoup moins.
Gardez à l'esprit que les mots et les symboles utilisés n'ont aucune signification en eux-mêmes :
ce ne sont que des suites de codes destinés à être convertis automatiquement en nombres binaires.
Par conséquent, il vous faudra être très attentifs à respecter scrupuleusement la syntaxe du langage.
Il est heureux que vous fassiez vos débuts en programmation avec un langage interprété tel que
Python. La recherche des erreurs y est facile et rapide. Avec les langages compilés (tel C++), il
vous faudrait recompiler l'intégralité du programme après chaque modification, aussi minime soitelle.
1.4.2. Erreurs sémantiques▲
Le second type d'erreur est l'erreur sémantique ou erreur de logique. S'il existe une erreur de ce
type dans un de vos programmes, celui-ci s'exécute parfaitement, en ce sens que vous n'obtenez
aucun message d'erreur, mais le résultat n'est pas celui que vous attendiez : vous obtenez autre
chose.
En réalité, le programme fait exactement ce que vous lui avez dit de faire. Le problème est que
ce que vous lui avez dit de faire ne correspond pas à ce que vous vouliez qu'il fasse. La séquence
d'instructions de votre programme ne correspond pas à l'objectif poursuivi. La sémantique (la
logique) est incorrecte.
Rechercher des fautes de logique peut être une tâche ardue. Il faut analyser ce qui sort de la
machine et tâcher de se représenter une par une les opérations qu'elle a effectuées, à la suite de
chaque instruction.
1.4.3. Erreurs à l'exécution▲
Le troisième type d'erreur est l'erreur en cours d'exécution (Run-time error), qui apparaît seulement lorsque votre programme fonctionne déjà, mais que des circonstances particulières se présentent (par exemple, votre programme essaie de lire un fichier qui n'existe plus). Ces erreurs sont également appelées des exceptions, parce qu'elles indiquent généralement que quelque chose d'exceptionnel s'est produit (et qui n'avait pas été prévu). Vous rencontrerez davantage ce type d'erreur lorsque vous programmerez des projets de plus en plus volumineux.
1.5. Recherche des erreurs et expérimentation▲
L'une des compétences les plus importantes à acquérir au cours de votre apprentissage est celle
qui consiste à « déboguer » efficacement un programme. Il s'agit d'une activité intellectuelle parfois
énervante mais toujours très riche, dans laquelle il faut faire montre de beaucoup de perspicacité.
Ce travail ressemble par bien des aspects à une enquête policière. Vous examinez un ensemble
de faits, et vous devez émettre des hypothèses explicatives pour reconstituer les processus et les
événements qui ont logiquement entraîné les résultats que vous constatez.
Cette activité s'apparente aussi au travail expérimental en sciences. Vous vous faites une
première idée de ce qui ne va pas, vous modifiez votre programme et vous essayez à nouveau. Vous
avez émis une hypothèse, qui vous permet de prédire ce que devra donner la modification. Si la
prédiction se vérifie, alors vous avez progressé d'un pas sur la voie d'un programme qui fonctionne.
Si la prédiction se révèle fausse, alors il vous faut émettre une nouvelle hypothèse. Comme l'a bien
dit Sherlock Holmes : « Lorsque vous avez éliminé l'impossible, ce qui reste, même si c'est
improbable, doit être la vérité » (A. Conan Doyle, Le signe des quatre).
Pour certaines personnes, « programmer » et « déboguer » signifient exactement la même chose.
Ce qu'elles veulent dire par là est que l'activité de programmation consiste en fait à modifier, à
corriger sans cesse un même programme, jusqu'à ce qu'il se comporte finalement comme vous le
vouliez. L'idée est que la construction d'un programme commence toujours par une ébauche qui fait
déjà quelque chose (et qui est donc déjà déboguée), à laquelle on ajoute couche par couche de
petites modifications, en corrigeant au fur et à mesure les erreurs, afin d'avoir de toute façon à
chaque étape du processus un programme qui fonctionne.
Par exemple, vous savez que Linux est un système d'exploitation (et donc un gros logiciel) qui
comporte des milliers de lignes de code. Au départ, cependant, cela a commencé par un petit
programme simple que Linus Torvalds avait développé pour tester les particularités du processeur
Intel 80386. Suivant Larry GreenField (« The Linux user's guide », beta version 1) : « L'un des
premiers projets de Linus était un programme destiné à convertir une chaîne de caractères AAAA
en BBBB. C'est cela qui plus tard finit par devenir Linux ! ».
Ce qui précède ne signifie pas que nous voulions vous pousser à programmer par approximations
successives, à partir d'une vague idée. Lorsque vous démarrerez un projet de programmation d'une
certaine importance, il faudra au contraire vous efforcer d'établir le mieux possible un cahier des
charges détaillé, lequel s'appuiera sur un plan solidement construit pour l'application envisagée.
Diverses méthodes existent pour effectuer cette tâche d'analyse, mais leur étude sort du cadre de
ces notes. Veuillez consulter votre professeur pour de plus amples informations et références.
1.6. Langages naturels et langages formels▲
Les langages naturels sont ceux que les êtres humains utilisent pour communiquer. Ces
langages n'ont pas été mis au point délibérément (encore que certaines instances tâchent d'y mettre
un peu d'ordre) : ils évoluent naturellement.
Les langages formels sont des langages développés en vue d'applications spécifiques. Ainsi par
exemple, le système de notation utilisé par les mathématiciens est un langage formel
particulièrement efficace pour représenter les relations entre nombres et grandeurs diverses. Les
chimistes utilisent un langage formel pour représenter la structure des molécules, etc.
Les langages de programmation sont des langages formels qui ont été développés
pour décrire des algorithmes et des structures de données.
Comme on l'a déjà signalé plus haut, les langages formels sont dotés d'une syntaxe qui obéit à
des règles très strictes. Par exemple, 3+3=6 est une représentation mathématique correcte, alors que
$3=+6 ne l'est pas. De même, la formule chimique H2O est correcte, mais non Zq3G2
Les règles de syntaxe s'appliquent non seulement aux symboles du langage (par exemple, le
symbole chimique Zq est illégal parce qu'il ne correspond à aucun élément), mais aussi à la manière
de les combiner. Ainsi l'équation mathématique 6+=+/5- ne contient que des symboles parfaitement
autorisés, mais leur arrangement incorrect ne signifie rien du tout.
Lorsque vous lisez une phrase quelconque, vous devez arriver à vous représenter la structure
logique de la phrase (même si vous faites cela inconsciemment la plupart du temps). Par exemple,
lorsque vous lisez la phrase « la pièce est tombée », vous comprenez que « la pièce » en est le sujet
et « est tombée » le verbe. L'analyse vous permet de comprendre la signification, la logique de la
phrase (sa sémantique). D'une manière analogue, l'interpréteur Python devra analyser la structure
de votre programme source pour en extraire la signification.
Les langages naturels et formels ont donc beaucoup de caractéristiques communes (des
symboles, une syntaxe, une sémantique), mais ils présentent aussi des différences très importantes :
Ambiguïté.
Les langages naturels sont pleins d'ambiguïtés, que nous pouvons lever dans la plupart des cas en
nous aidant du contexte. Par exemple, nous attribuons tout naturellement une signification
différente au mot vaisseau, suivant que nous le trouvons dans une phrase qui traite de circulation
sanguine ou de navigation à voiles. Dans un langage formel, il ne peut pas y avoir d'ambiguïté.
Chaque instruction possède une seule signification, indépendante du contexte.
Redondance.
Pour compenser toutes ces ambiguïtés et aussi de nombreuses erreurs ou pertes dans la
transmission de l'information, les langages naturels emploient beaucoup la redondance (dans nos
phrases, nous répétons plusieurs fois la même chose sous des formes différentes, pour être sûrs
de bien nous faire comprendre). Les langages formels sont beaucoup plus concis.
Littéralité.
Les langages naturels sont truffés d'images et de métaphores. Si je dis « la pièce est tombée ! »
dans un certain contexte, il se peut qu'il ne s'agisse en fait ni d'une véritable pièce, ni de la chute
de quoi que ce soit. Dans un langage formel, par contre, les expressions doivent être prises pour
ce qu'elles sont, « au pied de la lettre ».
Habitués comme nous le sommes à utiliser des langages naturels, nous avons souvent bien du mal à
nous adapter aux règles rigoureuses des langages formels. C'est l'une des difficultés que vous
devrez surmonter pour arriver à penser comme un analyste-programmeur efficace.
Pour bien nous faire comprendre, comparons encore différents types de textes :
Un texte poétique :
Les mots y sont utilisés autant pour leur musicalité que pour leur signification, et l'effet
recherché est surtout émotionnel. Les métaphores et les ambiguïtés y règnent en maîtres.
Un texte en prose :
La signification littérale des mots y est plus importante, et les phrases sont structurées de
manière à lever les ambiguïtés, mais sans y parvenir toujours complètement. Les redondances
sont souvent nécessaires.
Un programme d'ordinateur :
La signification du texte est unique et littérale. Elle peut être comprise entièrement par la seule
analyse des symboles et de la structure. On peut donc automatiser cette analyse.
Pour conclure, voici quelques suggestions concernant la manière de lire un programme
d'ordinateur (ou tout autre texte écrit en langage formel).
Premièrement, gardez à l'esprit que les langages formels sont beaucoup plus denses que les langages
naturels, ce qui signifie qu'il faut davantage de temps pour les lire. De plus, la structure y est très
importante. Aussi, ce n'est généralement pas une bonne idée que d'essayer de lire un programme
d'une traite, du début à la fin. Au lieu de cela, entraînez-vous à analyser le programme dans votre
tête, en identifiant les symboles et en interprétant la structure.
Finalement, souvenez-vous que tous les détails ont de l'importance. Il faudra en particulier faire très
attention à la casse (c'est-à-dire l'emploi des majuscules et des minuscules) et à la ponctuation.
Toute erreur à ce niveau (même minime en apparence, tel l'oubli d'une virgule, par exemple) peut
modifier considérablement la signification du code, et donc le déroulement du programme.