Вы находитесь на странице: 1из 899

Sauf mention contraire, le contenu de cet ouvrage est publi sous la licence :

Creative Commons BY-NC-SA 2.0


La copie de cet ouvrage est autorise sous rserve du respect des conditions de la licence.
Texte complet de la licence disponible sur : http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
Mentions lgales
Conception couverture : Alexandra Persil
Illustrations chapitres : Alexandra Persil
OpenClassrooms 2014 - ISBN : 979-10-90085-58-9

Avant-propos
ui na jamais rv, enfant, de crer son propre jeu vido intgrant ses ides les
plus folles ? Qui na jamais cherch en vain un programme ralisant une fonctionnalit anodine mais trs spcifique ? Qui na jamais pest face un logiciel mal
conu ou mme buggu ? Comme tout un chacun, je suis pass par ces diffrentes
tapes. Comme certains dentre vous peut-tre, je me suis parfois dit : et si je le
faisais moi-mme ? Et si japprenais programmer ? Jentamais alors mes recherches
avec enthousiasme. Mais les livres que je trouvais lpoque ne correspondaient ni aux
connaissances ni au budget du jeune garon que jtais. Internet entamait sa dmocratisation et les quelques sites traitant de programmation que je dnichais taient eux aussi
bien trop techniques et trop peu pdagogiques. Les symboles tranges et les termes anglais souvent barbares finissaient toujours par avoir raison de mon entrain initial. Cest
au cours de mes tudes que jai appris la programmation et dcouvert le langage Ada.
A peine les rudiments taient-ils acquis que mes recherches reprenaient de plus belle.
Je souhaitais dvelopper mes propres programmes et je fourmillais dides : il fallait
donc en apprendre toujours plus, dcouvrir de nouvelles fonctionnalits, exprimenter
de nouvelles faons de programmer. . . La programmation est ainsi devenue lune de
mes passions. Mais l encore, il restait difficile de trouver des ressources francophones
aisment comprhensibles, toute information devant se payer par de longues heures de
recherche. Mes tudes termines, je nai pas choisi la voie de linformatique comme
certains lauraient pens. Mais si la passion sest attnue, elle ne sest pas teinte pour
autant et je nai jamais cess de programmer, pour le plaisir. Je me suis tourn vers le
langage Python pendant plusieurs annes avant dapprendre le C, le C++ ou le Java,
entre autres. Cest au Site du Zro, devenu OpenClassrooms, que je dois la dcouverte
de certains de ces langages.

Retour vers Ada


Comme on revient toujours ses premires amours, cest aujourdhui en Ada que je
programme : ce langage ma beaucoup apport en termes de rigueur ou de structuration de mes programmes et de ma pense. Lorsque jai commenc enseigner la
programmation, cest donc en Ada que je lai fait. Lorsque jai voulu publier un cours
de programmation, cest encore le langage Ada que je choisis. Me souvenant des difficults que javais rencontres, plus jeune, en cherchant des cours accessibles et en
i

CHAPITRE 0. AVANT-PROPOS
franais, jai donc dcid dcrire ce tutoriel sur le langage Ada. Les encouragements
que jai pu recevoir de la part de dbutants comme de professionnels mont pouss
toffer ce qui ntait au dpart quun modeste cours sans ambition. Les validateurs
et modrateurs dOpenClassrooms quant eux mont incit plus de prcision et de
rigueur. Cest ainsi que mon petit tuto a t mis en avant sur le site et quil est
aujourdhui publi en livre. Jespre que vous trouverez autant de plaisir le lire et
dcouvrir cet univers que jen ai eu le rdiger.

Quallez-vous apprendre en lisant ce livre ?


Peut-tre avez-vous dj appris ou tent dapprendre programmer en Ada ou dans
un autre langage ? Quoi quil en soit, je suis parti du principe que vous ne connaissiez
rien la prog et que je devais vous guider pas pas. Ce livre se dcompose donc
en cinq parties.
1. Premiers pas avec Ada : cette partie aura pour tche de vous prsenter ce que
sont la programmation, lalgorithmique ou les langages de programmation. Nous
poserons le vocabulaire de base puis installerons les logiciels ncessaires avant de
raliser notre tout premier programme en Ada.
2. Ada, notions essentielles : cette seconde partie sera consacre aux rudiments
de la programmation. Nous y aborderons les variables et leurs types, les oprations, les conditions, les boucles et les sous-programmes. Vous raliserez enfin
votre premier TP.
3. Ada, les types composites : la troisime partie sera consacre des types de
donnes plus complexes comme les tableaux, les fichiers ou les pointeurs. Nous
aborderons galement des techniques de programmation comme la programmation modulaire ou la rcursivit. Cette partie sera donc un peu plus difficile que
les prcdentes, mais vous permettra de raliser des programmes plus importants
et intressants. Deux TP seront au menu.
4. Ada, notions avances et programmation oriente objet : cette partie sera
de loin la plus complique. Vous y dcouvrirez une nouvelle faon de structurer
vos programmes et de les penser. Nous explorerons les fonctionnalits les plus
pousses du langage Ada. Un TP vous permettra de mettre en pratique toute la
thorie que vous aurez apprise.
5. Ada et GTK, la programmation vnementielle : cette partie sera votre
rcompense aprs autant defforts. Vous y dcouvrirez comment raliser des interfaces graphiques avec GTK. Vos programmes se pareront alors de boutons,
dimages, de menus droulants ou dicnes et vous raliserez un dernier projet au
travers de deux TP.
ii

COMMENT LIRE CE LIVRE ?

Comment lire ce livre ?


Suivez lordre des chapitres
Lisez ce livre comme on lit un roman. Il a t conu pour cela.
Contrairement beaucoup de livres techniques o il est courant de lire en diagonale et
de sauter certains chapitres, il est ici trs fortement recommand de suivre lordre du
cours, moins que vous ne soyez dj un peu expriment.

Pratiquez en mme temps


Pratiquez rgulirement. Nattendez pas davoir fini de lire ce livre pour allumer votre
ordinateur et faire vos propres essais.

Remerciements
Si lcriture demeure une tche solitaire, les travaux de recherche, daccompagnement,
de relecture ou de critique qui laccompagnent sont gnralement collectifs. Je ne peux
donc commencer sans remercier tous ceux qui mont aid ou soutenu dans ma rdaction :
ma femme qui, malgr son dsintrt complet pour la programmation, na cess
de mencourager et a d supporter mes longues heures devant mon cran. Merci
dtre l dans les bons moments comme dans les mauvais, merci de me transmettre ton inpuisable nergie et de me remettre dans le bon chemin quand cela
simpose... Pour toutes tes qualits mais aussi pour tes (petits) dfauts, je taime ;
mes amis, Fred et Aml, qui mont apport le regard et le savoir-faire professionnels qui parfois me manquaient ;
AstroB et Holt avec qui jai entretenu des changes nourris, qui mont apport
leur expertise et mont soumis leurs ides ;
Arthurus, John-John, AnnaStretter, ThunderSeb, Fayden, Nazli et plus gnralement toute lquipe dOpenClassrooms pour lexigence de rigueur quils mont
impose et la confiance quils mont accorde ;
Basquer, Ludolost, Ranhan19, Manildomin, Thor et tous ceux qui, par leurs questions ou remarques, mont permis de corriger quelques erreurs ou oublis, parfois
anodins pour moi mais essentiels pour les dbutants ;
Daniel Feneuille dont les notes de cours en ligne ont longtemps t ma seule source
dinformation francophone sur le langage Ada et sans qui je naurais jamais eu
les capacits dcrire ce livre ;
Merci enfin tous les lecteurs dOpenClassrooms qui, par pure gentillesse, mont
flicit, encourag ou remerci. Leurs paroles mont surpris, touch et motiv. Ce
livre est l pour vous et grce vous.
Bonne lecture.
iii

CHAPITRE 0. AVANT-PROPOS

iv

Table des matires

Avant-propos

Retour vers Ada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Quallez-vous apprendre en lisant ce livre ? . . . . . . . . . . . . . . . . . . . .

ii

Comment lire ce livre ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iii

Suivez lordre des chapitres . . . . . . . . . . . . . . . . . . . . . . . . .

iii

Pratiquez en mme temps . . . . . . . . . . . . . . . . . . . . . . . . . .

iii

Remerciements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iii

Premiers pas avec Ada

1 Programmation, algorithmique et Ada ?

Quest-ce quun programme ? . . . . . . . . . . . . . . . . . . . . . . . . . . .

Quappelle-t-on programme ? . . . . . . . . . . . . . . . . . . . . . . . .

Alors quallons-nous faire ? . . . . . . . . . . . . . . . . . . . . . . . . . .

Comment raliser un programme ? . . . . . . . . . . . . . . . . . . . . . . . .

Pourquoi Ada et pas autre chose ? . . . . . . . . . . . . . . . . . . . . . . . .

Algorithmique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Pourquoi Ada ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Les logiciels ncessaires

11

IDE et compilateur : kesako ? . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

Le compilateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

LIDE ou EDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

TABLE DES MATIRES


Tlcharger et installer Adagide. . . . . . . . . . . . . . . . . . . . . . . . . .

12

Tlchargement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

Tlcharger et installer GNAT . . . . . . . . . . . . . . . . . . . . . . . . . .

13

Tlchargement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

Tlcharger et installer GPS . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

3 Notre premier programme en Ada

II

17

Dcouvrir son IDE en quelques secondes . . . . . . . . . . . . . . . . . . . . .

18

Soyons rapide avec Adagide . . . . . . . . . . . . . . . . . . . . . . . . .

18

Pour les utilisateurs de GPS (plus long) . . . . . . . . . . . . . . . . . .

19

Notre premier programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

Un petit copier-coller ! . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

Compiler, crer. . . lancer ! . . . . . . . . . . . . . . . . . . . . . . . . . .

22

Mon dieu, quai-je fait ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

Le corps du programme : la procdure Hello . . . . . . . . . . . . . . . .

23

Les Packages avec With et Use . . . . . . . . . . . . . . . . . . . . . . .

25

Une dernire remarque qui a son importance . . . . . . . . . . . . . . .

27

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

Ada, notions essentielles

31

4 Variables I : Typage et affectation

33

vi

Dclaration de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

Diffrents types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

Les types Integer et Natural . . . . . . . . . . . . . . . . . . . . . . . . .

35

Le type Float . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

37

Le type Character . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

Affectation par le programmeur (ou le programme) . . . . . . . . . . . .

41

TABLE DES MATIRES


Affectation par lutilisateur . . . . . . . . . . . . . . . . . . . . . . . . .

41

Linstruction Skip_line . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

Complments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

Attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

Bloc de dclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5 Variables II : Oprations

49

Oprations sur les Integer et les Natural . . . . . . . . . . . . . . . . . . . . .

50

Oprations sur les float . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

Oprations lmentaires . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

Ne faites pas derreur de casting . . . . . . . . . . . . . . . . . . . . . .

51

Oprations mathmatiques . . . . . . . . . . . . . . . . . . . . . . . . .

52

Oprations sur les character . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54

Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54

Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

Exercice 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

6 Les conditions I

59

Conditions simples avec IF . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

Un dbut en douceur . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

Une premire alternative . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

Conditions multiples avec IF . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

Conditions multiples avec CASE . . . . . . . . . . . . . . . . . . . . . . . . .

63

Tester de nombreux cas . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

Ne rien faire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

Les oprateurs de comparaison et dappartenance . . . . . . . . . . . . . . . .

64

Les oprateurs de comparaison . . . . . . . . . . . . . . . . . . . . . . .

64

Loprateur dappartenance . . . . . . . . . . . . . . . . . . . . . . . . .

66

Pour les utilisateurs de la norme Ada2012 . . . . . . . . . . . . . . . . . . . .

67

Les expressions IF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67

Les expressions CASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67

Toujours plus complexe . . . . . . . . . . . . . . . . . . . . . . . . . . .

68
vii

TABLE DES MATIRES


7 Les conditions II : les boolens
Introduction aux boolens . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

Un bref historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

Quest-ce quun boolen ? . . . . . . . . . . . . . . . . . . . . . . . . . .

70

Les oprateurs boolen . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

La ngation avec Not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

Les oprations Or et And . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

Linstruction OR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

Linstruction AND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

Lopration XOR (optionnel) . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

74

Priorits boolennes et ordre de test (Supplment) . . . . . . . . . . . . . . .

75

Priorits avec les boolens . . . . . . . . . . . . . . . . . . . . . . . . . .

75

Ordre de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

8 Les boucles

79

La boucle Loop simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Principe gnral

80

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

80

Arrter une itration . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

Nommer une boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

La boucle While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

83

La boucle For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

83

Les antiquits : linstruction goto . . . . . . . . . . . . . . . . . . . . . . . . .

85

Boucles imbriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

Mthode avec une seule boucle (plutt mathmatique) . . . . . . . . . .

86

Mthode avec deux boucles (plus naturelle) . . . . . . . . . . . . . . . .

87

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

Exercice 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

9 Procdures et fonctions I
viii

69

91

TABLE DES MATIRES


Les procdures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

Procdure sans paramtre . . . . . . . . . . . . . . . . . . . . . . . . . .

92

Procdure avec un paramtre (ou argument) . . . . . . . . . . . . . . . .

94

Procdure avec plusieurs paramtres (ou arguments) . . . . . . . . . . .

96

Les fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98

Une bte fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98

Une fonction un peu moins bte (optionnel) . . . . . . . . . . . . . . . . 100


Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Prdfinir ses paramtres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
In, Out, In Out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Paramtres de procdure . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Paramtres de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
10 [TP] Le craps

109

Les rgles du craps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110


Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Simuler le hasard (ou presque) . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Un plan de bataille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Une solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

III

Ada, les types composites

11 Les tableaux

119
121

Les types composites, cest quoi ? . . . . . . . . . . . . . . . . . . . . . . . . . 122


Tableaux unidimensionels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Problme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Cration dun tableau en Ada . . . . . . . . . . . . . . . . . . . . . . . . 124
Attributs pour les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . 127
Tableaux multidimensionels . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Tableaux bidimensionnels . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Tableaux tridimensionnels et plus . . . . . . . . . . . . . . . . . . . . . . 130
Et mes attributs ?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

Et mes agrgats ?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
ix

TABLE DES MATIRES


Des tableaux un peu moins contraints . . . . . . . . . . . . . . . . . . . . . . 132
Un type non-contraint ou presque . . . . . . . . . . . . . . . . . . . . . . 132
Affectation par tranche . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Dclarer un tableau en cours de programme . . . . . . . . . . . . . . . . 133
Quelques exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Pour les utilisateurs de la norme Ada2012 . . . . . . . . . . . . . . . . . . . . 141
Boucle FOR OF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Expressions quantifies . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
12 Les chanes de caractres

145

Prsentation des Chanes de Caractres . . . . . . . . . . . . . . . . . . . . . 146


Dclaration et affectation dun string . . . . . . . . . . . . . . . . . . . . . . . 146
Quelques oprations sur les strings . . . . . . . . . . . . . . . . . . . . . . . . 147
Accs une valeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Accs plusieurs valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Modifier la casse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Concatnation

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Transformer une variable en string et inversement . . . . . . . . . . . . . 150


Comparaison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Saisie au clavier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Chanes de caractres non contraintes . . . . . . . . . . . . . . . . . . . . . . 151
Dclarer des strings illimits ! . . . . . . . . . . . . . . . . . . . . . . . . 151
Oprations sur les unbounded_string . . . . . . . . . . . . . . . . . . . . 152
13 La programmation modulaire I : les packages

155

Les fichiers ncessaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156


Notre premire procdure. . . empaquete . . . . . . . . . . . . . . . . . . . . . 157
Variables et constantes globales . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Trouver et classer les fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Les packages fournis avec GNAT . . . . . . . . . . . . . . . . . . . . . . 163
Organiser nos packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Petite astuce pour limiter les fichiers gnrs . . . . . . . . . . . . . . . 165
x

TABLE DES MATIRES


Complter notre package (exercices) . . . . . . . . . . . . . . . . . . . . . . . 166
Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Solutions

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

Vecteurs et calcul vectoriel (optionnel) . . . . . . . . . . . . . . . . . . . . . . 173


Quest-ce exactement quun T_Vecteur ?

. . . . . . . . . . . . . . . . . 173

Calcul vectoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174


14 Les fichiers

177

Ouvrir / Fermer un fichier texte . . . . . . . . . . . . . . . . . . . . . . . . . 178


Package ncessaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Le type de lobjet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Fermer un fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Ouvrir un fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
Le paramtre Mode

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

Lecture seule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181


criture seule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Ajout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Oprations sur les fichiers textes . . . . . . . . . . . . . . . . . . . . . . . . . 182
Mode lecture seule : In_File . . . . . . . . . . . . . . . . . . . . . . . . . 182
Mode criture : Out_File / Append_File . . . . . . . . . . . . . . . . . 185
Autres oprations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Les fichiers binaires squentiels . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Les fichiers binaires directs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Les rpertoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Chemins absolus et relatifs

. . . . . . . . . . . . . . . . . . . . . . . . . 191

Indiquer le chemin daccs . . . . . . . . . . . . . . . . . . . . . . . . . . 192


Grer fichiers et rpertoires . . . . . . . . . . . . . . . . . . . . . . . . . 192
Quelques exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
15 Crer vos propres types

199

Crer partir de types prdfinis . . . . . . . . . . . . . . . . . . . . . . . . . 200


Sous-type comme intervalle . . . . . . . . . . . . . . . . . . . . . . . . . 200
xi

TABLE DES MATIRES


Types modulaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
numrer les valeurs dun type . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Les types structurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Dclarer un type construit . . . . . . . . . . . . . . . . . . . . . . . . 203
Ordre des dclarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Dclarer et modifier un objet de type structur . . . . . . . . . . . . . . 206
22 ! Revl les tableaux ! . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Les types structurs : polymorphes et mutants ! . . . . . . . . . . . . . . . . . 208
Les types structurs polymorphes . . . . . . . . . . . . . . . . . . . . . . 208
Les types structurs mutants . . . . . . . . . . . . . . . . . . . . . . . . 212
16 TP : Logiciel de gestion de bibliothque

217

Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218


Quelles donnes pour quels types de donnes ?

. . . . . . . . . . . . . . 218

Quelle architecture pour les fichiers . . . . . . . . . . . . . . . . . . . . . 219


Quelles fonctionnalits pour quelles fonctions et procdures ? . . . . . . 220
Architecture du code source . . . . . . . . . . . . . . . . . . . . . . . . . 220
Conception du programme (suivez le guide) . . . . . . . . . . . . . . . . . . . 221
Cration des types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Affichage dune uvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Saisie dune oeuvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Gestion des fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Les commandes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Solutions possibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
17 Les pointeurs I : allocation dynamique

239

Mmoire, variable et pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . 240


Le type access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Dclarer un pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Que contient mon pointeur ?

. . . . . . . . . . . . . . . . . . . . . . . . 243

Comment accder aux donnes ? . . . . . . . . . . . . . . . . . . . . . . 244


Oprations sur les pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . 245
Une erreur viter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Libration de la mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
xii

TABLE DES MATIRES


Un programme (un peu) gourmand . . . . . . . . . . . . . . . . . . . . . 247
Un problme de mmoire . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Rsolution du problme . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
18 Les pointeurs II

257

Cas gnral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258


Pointeurs gnraliss : pointer sur une variable . . . . . . . . . . . . . . 258
Pointeur sur une constante et pointeur constant . . . . . . . . . . . . . . 259
Pointeur sur pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Pointeur comme paramtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Pointeur sur un programme (optionnel) . . . . . . . . . . . . . . . . . . . . . 262
Un exemple simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Un exemple de la vie courante . . . . . . . . . . . . . . . . . . . . . . . . 264
Un exemple trs. . . mathmatique

. . . . . . . . . . . . . . . . . . . . . 265

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Exercice 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Exercice 5 (Niveau Scientifique) . . . . . . . . . . . . . . . . . . . . . . . 272
19 Fonctions et procdures II : la rcursivit

275

Une premire dfinition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276


Exemple dalgorithme rcursif . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Notre premire fonction rcursive . . . . . . . . . . . . . . . . . . . . . . . . . 279
nonc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Indications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Une solution possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Algorithme de recherche par dichotomie . . . . . . . . . . . . . . . . . . . . . 282
Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Mise en uvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
xiii

TABLE DES MATIRES


Quelques exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
Exercice 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
20 Les Types Abstraits de Donnes : listes, files, piles. . .

291

Quest-ce quun Type Abstrait de Donnes ? . . . . . . . . . . . . . . . . . . . 292


Un premier cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Autres cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Les piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
Cration du type T_Pile

. . . . . . . . . . . . . . . . . . . . . . . . . . 297

Cration des primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298


Jouons avec le package P_Pile . . . . . . . . . . . . . . . . . . . . . . . 302
Les files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Implmentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
Amusons-nous encore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Les listes chanes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Quelques rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
Le package Ada.Containers.Doubly_Linked_Lists . . . . . . . . . . . . 309
Le package Ada.Containers.Vectors . . . . . . . . . . . . . . . . . . . . . 313
21 [TP] Le jeu du serpent

317

Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318


Fonctionnalits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Organisation des types et variables . . . . . . . . . . . . . . . . . . . . . 319
Un package bien utile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
Le package NT_Console . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
Le contenu en dtail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
. . . et encore un autre ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Quelques indications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Jouer en temps rel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Comment afficher un serpent et une zone de jeu en couleur ?

. . . . . . 339

Par o commencer ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340


xiv

TABLE DES MATIRES


Une solution possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351

IV
jet

Ada : Notions avances et Programmation Oriente Ob353

22 Algorithmique : tri et complexit

355

Algorithmes de tri lents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356


Tri par slection (ou par extraction) . . . . . . . . . . . . . . . . . . . . 357
Tri par insertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Tri bulles (ou par propagation) . . . . . . . . . . . . . . . . . . . . . . 360
Algorithmes de tri plus rapides . . . . . . . . . . . . . . . . . . . . . . . . . . 361
Tri rapide (ou Quick Sort) . . . . . . . . . . . . . . . . . . . . . . . . . . 361
Tri fusion (ou Merge Sort) . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Tri par tas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
Thorie : complexit dun algorithme . . . . . . . . . . . . . . . . . . . . . . . 371
Complexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Lcriture O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Quelques fonctions mathmatiques . . . . . . . . . . . . . . . . . . . . . 373
Mesures de complexit des algorithmes . . . . . . . . . . . . . . . . . . . . . . 375
Un algorithme pour mesurer la complexit. . . des algorithmes . . . . . . 375
Traiter nos rsultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
23 Variables III : Gestion bas niveau des donnes

383

Reprsentation des nombres entiers . . . . . . . . . . . . . . . . . . . . . . . . 384


Le code binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
Conversions entre dcimal et binaire . . . . . . . . . . . . . . . . . . . . 385
Retour sur le langage Ada . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Reprsentation du texte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Reprsentation des nombres dcimaux en virgule flottante . . . . . . . . . . . 393
Reprsentation des float . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
Implications sur le langage Ada . . . . . . . . . . . . . . . . . . . . . . . 395
24 La programmation modulaire II : Encapsulation

401

Quest-ce quun objet ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402


xv

TABLE DES MATIRES


Une premire approche . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
Posons le vocabulaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
De nouvelles contraintes . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Un package. . . priv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Partie publique / partie prive . . . . . . . . . . . . . . . . . . . . . . . 406
Visibilit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Un package priv et limit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Que faire avec un type PRIVATE ?

. . . . . . . . . . . . . . . . . . . . . 410

Restreindre encore notre type . . . . . . . . . . . . . . . . . . . . . . . . 411


Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
25 La programmation modulaire III : Gnricit

417

Gnricit : les grandes lignes . . . . . . . . . . . . . . . . . . . . . . . . . . . 418


Que veut-on faire ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Plan de bataille . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Un dernier point de vocabulaire . . . . . . . . . . . . . . . . . . . . . . . 419
Crer et utiliser une mthode gnrique . . . . . . . . . . . . . . . . . . . . . 421
Crer une mthode gnrique . . . . . . . . . . . . . . . . . . . . . . . . 421
Utiliser une mthode gnrique . . . . . . . . . . . . . . . . . . . . . . . 422
Paramtres gnriques de types simples et privs . . . . . . . . . . . . . . . . 424
Types gnriques simples . . . . . . . . . . . . . . . . . . . . . . . . . . 424
Types gnriques privs . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
Paramtres gnriques de types composites et programmes

. . . . . . . . . . 425

Tableaux gnriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425


Pointeurs gnriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
Paramtre de type programme : le cas dun paramtre LIMITED PRIVATE 428
Packages gnriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
26 La programmation modulaire IV : Hritage et drivation

435

Pour bien commencer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436


xvi

TABLE DES MATIRES


Hritage : une premire approche thorique . . . . . . . . . . . . . . . . 436
Hritage : une approche par lexemple . . . . . . . . . . . . . . . . . . . 437
Hritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
Hritage de package simple . . . . . . . . . . . . . . . . . . . . . . . . . 438
Hritage avec des packages privs . . . . . . . . . . . . . . . . . . . . . . 443
Hritage avec des packages gnriques . . . . . . . . . . . . . . . . . . . 444
Drivation et types tiquets . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
Crer un type tiquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
Et nos mthodes ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
Un autre avantage des classes . . . . . . . . . . . . . . . . . . . . . . . . 452
27 La programmation modulaire V : Polymorphisme, abstraction et hritage multiple
455
Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
Mthodes polymorphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
Objets polymorphes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
Types abstraits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
Mthodes abstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
Hritage multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
Comment Ada gre-t-il lhritage multiple ? . . . . . . . . . . . . . . . . 465
Raliser des interfaces Ada

. . . . . . . . . . . . . . . . . . . . . . . . . 467

28 La programmation modulaire VI : Finalisation et types contrls

475

Objectifs et prrequis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476


De quoi parle-t-on exactement ? . . . . . . . . . . . . . . . . . . . . . . . 476
Comment sy prendre ?

. . . . . . . . . . . . . . . . . . . . . . . . . . . 478

Mise en uvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478


Mise en place de nos types . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Mise en place de nos mthodes . . . . . . . . . . . . . . . . . . . . . . . 479
29 [TP] Bataille navale

485

Rgles du jeu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486


Le droulement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
Les navires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
xvii

TABLE DES MATIRES


Les statistiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
Gameplay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487
Les calculs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
Une solution possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Lorganisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Le code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
30 Les exceptions

529

Fonctionnement dune exception . . . . . . . . . . . . . . . . . . . . . . . . . 530


Vous avez dit exception ?

. . . . . . . . . . . . . . . . . . . . . . . . . . 530

Le fonctionnement par lexemple . . . . . . . . . . . . . . . . . . . . . . 531


Traitement dune exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
Le bloc EXCEPTION . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
O et quand grer une exception . . . . . . . . . . . . . . . . . . . . . . 534
Exceptions prdfinies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
Exceptions standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
Autres exceptions prdfinies . . . . . . . . . . . . . . . . . . . . . . . . 537
Crer et lever ses propres exceptions . . . . . . . . . . . . . . . . . . . . . . . 538
Dclarer une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538
Lever sa propre exception . . . . . . . . . . . . . . . . . . . . . . . . . . 538
Propagation de lexception . . . . . . . . . . . . . . . . . . . . . . . . . . 539
Rectifier son code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
Assertions et contrats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
Programmation par contrats (Ada2012 seulement) . . . . . . . . . . . . 544
31 Multitasking

547

Paralllisme, tches et types tches . . . . . . . . . . . . . . . . . . . . . . . . 548


Multitasking ou lart de faire plusieurs tches la fois . . . . . . . . . . 548
Les tches en Ada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
Le type tche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
Communication inter-tche directe . . . . . . . . . . . . . . . . . . . . . . . . 556
xviii

TABLE DES MATIRES


Le paralllisme, comment a marche ? . . . . . . . . . . . . . . . . . . . 556
Les entres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
Les synchronisations slectives
Gardes et complments

. . . . . . . . . . . . . . . . . . . . . . . 563

. . . . . . . . . . . . . . . . . . . . . . . . . . . 565

Communication inter-tche indirecte . . . . . . . . . . . . . . . . . . . . . . . 568


Les types protgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
Les smaphores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570
Les moniteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573
Complments : priorits et POO . . . . . . . . . . . . . . . . . . . . . . . . . 575
Priorits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575
Quand la programmation oriente objet rejoint la programmation concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
Programme multitche et processeur multicur (Ada 2012 uniquement) 580
Exercices fondamentaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580
Modle des producteurs et consommateurs . . . . . . . . . . . . . . . . . 580
Modle des lecteurs et rdacteurs . . . . . . . . . . . . . . . . . . . . . . 584
Le dner des philosophes . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
32 Interfaage entre Ada et le C

595

Quelques prparatifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596


Logiciels ncessaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
Quelques rudiments de C . . . . . . . . . . . . . . . . . . . . . . . . . . 597
Packages ncessaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
Hello World : du C lAda . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
Notre programme en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
Notre programme Ada avec les normes 95 ou 2005 . . . . . . . . . . . . 601
Notre programme Ada avec la norme 2012 . . . . . . . . . . . . . . . . . 602
Quelques menus problmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
Procdure avec paramtres . . . . . . . . . . . . . . . . . . . . . . . . . 603
Avec un type structur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605

Ada et GTK : la programmation vnementielle

33 GTKAda : introduction et installation

609
611

Vous avez dit GTK ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612


xix

TABLE DES MATIRES


Quest-ce que GTK ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
GTK, GTKAda, GDK, Glib et toute la famille . . . . . . . . . . . . . . 613
Pourquoi ce choix ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
Tlcharger et installer GTKAda . . . . . . . . . . . . . . . . . . . . . . . . . 615
Tlcharger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Installer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
Configurer votre IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
Un premier essai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
34 Votre premire fentre

623

Analysons notre code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624


Code GtkAda minimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
Crer une fentre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
Personnaliser la fentre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
Changer de type de fentre . . . . . . . . . . . . . . . . . . . . . . . . . 626
Dfinir les paramtres avec Set_# . . . . . . . . . . . . . . . . . . . . . . 627
Ajout dun widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
Quest-ce quun widget ?

. . . . . . . . . . . . . . . . . . . . . . . . . . 633

Ajouter un bouton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633


Personnaliser le bouton . . . . . . . . . . . . . . . . . . . . . . . . . . . 634
Ajouter un second bouton ? . . . . . . . . . . . . . . . . . . . . . . . . . 636
Retour sur la POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
Mthode brutale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
Mthode subtile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
35 Les conteneurs I

643

Des conteneurs pour. . . contenir ! . . . . . . . . . . . . . . . . . . . . . . . . . 644


Quest-ce quun conteneur ? . . . . . . . . . . . . . . . . . . . . . . . . . 644
Prsentation de diffrents conteneurs . . . . . . . . . . . . . . . . . . . . 645
Les alignements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
Crer un GTK_Alignment . . . . . . . . . . . . . . . . . . . . . . . . . 647
Le padding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
Les botes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
Botes classiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
xx

TABLE DES MATIRES


Botes boutons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
Les tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Crer une table de widgets . . . . . . . . . . . . . . . . . . . . . . . . . . 663
Ajouter des widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
Les paramtres supplmentaires . . . . . . . . . . . . . . . . . . . . . . . 666
Le widget pour position fixe . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Utilisation des GTK_Fixed . . . . . . . . . . . . . . . . . . . . . . . . . 669
36 Les signaux

671

Le principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Les problmes de la programmation vnementielle . . . . . . . . . . . . 672
Le principe Signal-Procdure de rappel . . . . . . . . . . . . . . . . . . . 672
Connecter un signal un callback

. . . . . . . . . . . . . . . . . . . . . . . . 673

Fermer proprement le programme . . . . . . . . . . . . . . . . . . . . . . 673


Utiliser le bouton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
Interagir avec les widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
Un callback deux paramtres . . . . . . . . . . . . . . . . . . . . . . . 679
Un nouveau package de callbacks . . . . . . . . . . . . . . . . . . . . . . 680
La connexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Perfectionner encore notre code . . . . . . . . . . . . . . . . . . . . . . . 683
Autres packages de callback . . . . . . . . . . . . . . . . . . . . . . . . . 685
GDK et les vnements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
Le clic droit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
Le double clic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
Le clavier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
37 Les widgets I

697

Les tiquettes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698


Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
Quelques mthodes des GTK_Label . . . . . . . . . . . . . . . . . . . . 698
Pour une mise en forme plus pousse . . . . . . . . . . . . . . . . . . . . 701
Les images

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705

Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705


xxi

TABLE DES MATIRES


Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
Exercice : crer un bouton avec icne . . . . . . . . . . . . . . . . . . . . 708
Les zones de saisie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
La saisie de texte sur une ligne : les entres . . . . . . . . . . . . . . . . 710
Saisie de texte multiligne

. . . . . . . . . . . . . . . . . . . . . . . . . . 714

Saisie numrique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725


Saisie numrique par curseur . . . . . . . . . . . . . . . . . . . . . . . . 727
Dautres boutons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
Boutons bascule . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
Boutons-liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730
Boutons cocher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731
Boutons radios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 732
Widgets divers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
Les sparateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733
Les flches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
Le calendrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
Les barres de progression . . . . . . . . . . . . . . . . . . . . . . . . . . 737
38 Les Widgets II : les botes de dialogue

739

Message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Les signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
propos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 743
Slection de fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 746
Mthodes des GTK_File_Chooser_Dialog . . . . . . . . . . . . . . . . . 746
Les GTK_File_Chooser (sans le Dialog) . . . . . . . . . . . . . . . . . . 748
Deux widgets supplmentaires . . . . . . . . . . . . . . . . . . . . . . . . 750
Signaux des Gtk_File_Chooser . . . . . . . . . . . . . . . . . . . . . . . 752
Slection de police . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
xxii

TABLE DES MATIRES


Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 752
Slection de couleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
Signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Cas gnral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755
Signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
39 [TP] Le dmineur

757

Rgles du jeu et cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . 758


Quelques rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
Rgles retenues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
Quelques ressources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759

Un petit coup de main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 760


Premier objectif : dtruire une case . . . . . . . . . . . . . . . . . . . . . 760
Second objectif : placer un drapeau . . . . . . . . . . . . . . . . . . . . . 761
Troisime objectif : passer du bouton unique la grille . . . . . . . . . . 761
Quatrime objectif : destruction de cases en cascade . . . . . . . . . . . 761
Une solution possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762
Les spcifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762
Le corps des packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769
La procdure principale . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
40 Les conteneurs II

781

Les onglets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782


Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
Les barres de dfilement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
Exemples dutilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794
Les panneaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
xxiii

TABLE DES MATIRES


Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
Les cadres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
Les cadres simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
Les cadres daspect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798
Les extenseurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
Les botes dtachables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
41 Les widgets III : barres et menus

805

La barre de menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806


Crer une barre de menu . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
Crer et ajouter des items . . . . . . . . . . . . . . . . . . . . . . . . . . 808
Crer et ajouter un menu droulant

. . . . . . . . . . . . . . . . . . . . 810

Amliorer la barre de menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811


Crer un menu en image . . . . . . . . . . . . . . . . . . . . . . . . . . . 812
Crer un sous-menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813
Proposer un item cocher dans le menu . . . . . . . . . . . . . . . . . . 815
Organiser vos menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
La barre dicnes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Fiches didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Crer notre barre dicnes . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Amliorer les barres dicnes

. . . . . . . . . . . . . . . . . . . . . . . . 819

Combiner menus et icnes . . . . . . . . . . . . . . . . . . . . . . . . . . 822


La barre de statut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825
Mthodes et fonctionnement . . . . . . . . . . . . . . . . . . . . . . . . . 825
Exemples dutilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
Le menu droulant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
Fiche didentit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832
Mthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
xxiv

TABLE DES MATIRES


Menu droulant avec saisie . . . . . . . . . . . . . . . . . . . . . . . . . . 833
42 [TP] Dmineur (le retour)

835

Cahier des charges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836


Objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
Widgets ncessaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
Une solution possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
Les spcifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837
Le corps des packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . 850
La procdure principale . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
Pistes damlioration : . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868

xxv

TABLE DES MATIRES

xxvi

Premire partie

Premiers pas avec Ada

Chapitre

Programmation, algorithmique et Ada ?


Difficult :
Nous allons dans ce chapitre rpondre quelques questions prliminaires. Quest-ce quun
programme ? quoi cela ressemble-t-il ? Comment cre-t-on un programme ? Quest-ce
que lalgorithmique ? Pourquoi utiliser Ada et non pas un autre langage ? Ce que japprends
ici sera-t-il utilisable avec un autre langage ?
Bref, beaucoup de rponses existentielles trouveront (je lespre) leur rponse ici. Ce chapitre, relativement court et sans difficults, permettra de poser un certain nombre dides et
de termes. Pour bien commencer, mieux vaut en effet que nous ayons le mme vocabulaire.
ceux qui auraient dj des notions de programmation, vous pouvez bien entendu viter
ce chapitre. Je ne saurais trop toutefois vous conseiller dy jeter un il, histoire de remettre
quelques ides au clair.

CHAPITRE 1. PROGRAMMATION, ALGORITHMIQUE ET ADA ?

Quest-ce quun programme ?


Avant de commencer, il est important que nous nous entendions sur les termes que
nous allons utiliser.

Quappelle-t-on programme ?
Les programmes sont des fichiers informatiques particuliers. Sous Windows, ce sont
gnralement des fichiers se terminant par lextension .exe ; sous MacOS ce sont globalement des fichiers .app ; sous Linux ce sont schmatiquement des fichiers .bin. Cette
explication est trs (TRS) schmatique, car dautres types de fichiers peuvent correspondre des programmes.
Ces fichiers ont comme particularit de ne pas seulement contenir des donnes, mais
galement des instructions. Lorsque vous ouvrez un fichier .exe sous windows, celui-ci
transmet votre ordinateur une liste de tches effectuer comme :

ouvrir une fentre


afficher du texte
effectuer des calculs
...

Cette liste dinstructions envoyes au processeur, forme alors ce que lon appelle un
processus.
On fait gnralement un amalgame entre programme et processus. Le programme est, rappelons-le, un fichier. Le processus correspond aux instructions
effectues par le processeur et qui viennent du programme. Mais, en gnral,
on appelle tout cela programme.
Attention, tous les programmes ne sont pas ncessairement visibles. Beaucoup nouvrent
mme pas de fentre
! Pour
compte,
sous Windows, appuyez simultanment

 sen rendre


sur les touches Alt + Ctrl + Suppr , puis cliquez sur le second onglet (voir figure


1.1), cela affichera la liste de tous les processus en cours.
Bon nombre dentre eux effectuent des tches tranquillement sans que vous ne vous en
rendiez compte.

Alors quallons-nous faire ?


Eh bien, programmer (en Ada ou avec tout autre langage) cest tout simplement crer
des programmes. Mais je vous arrte tout de suite, malgr tout le talent de votre humble
guide, vous ne confectionnerez pas un jeu vido en 3D dici la fin de ce tutoriel. La plupart des logiciels, comme le navigateur internet que vous utilisez actuellement, exigent
la participation de nombreuses personnes et de nombreuses autres comptences ne relevant pas de ce cours (modlisation 3D par exemple). Les premiers programmes que nous
4

QUEST-CE QUUN PROGRAMME ?

Figure 1.1 Gestionnaire des tches de Windows


allons concevoir seront des programmes en console. Cest--dire quils ressembleront
la figure 1.2.

Figure 1.2 Programme en console


Nous ne pourrons utiliser que le clavier ! Pas de souris ou de joystick ! Pas dimages ou
de vidos ! Pas de 3D puisquil ny aura mme pas de 2D ! Que du texte blanc sur fond
noir.
5

CHAPITRE 1. PROGRAMMATION, ALGORITHMIQUE ET ADA ?

Aaargh ! Mais, cest possible ? Ces horreurs existent encore ?

Bon, cest vrai quaujourdhui on a tendance oublier la console, mais il est ncessaire
de passer par ce stade pour apprendre les bases de la programmation. Nous pourrons
ensuite nous atteler des programmes plus consquents (avec des boutons, des images,
la possibilit dutiliser la souris. . . ). Mais il faudra tre patient.

Comment raliser un programme ?


Alors comment faire pour crer votre propre programme ? Il faut avant tout savoir
comment est constitu un programme (ou tout autre type de fichier). Vous le savez
peut-tre dj, mais un ordinateur a un langage trs trs basique. Il ne connat que
deux chiffres : 1 et 0 ! Donc tout fichier (et donc tout programme) ne peut ressembler
qu ceci :
10001001111010110110111011011 . . .
a rappelle Matrix ! Mais comment je fais moi ? Jy comprends rien tous
ces chiffres ! Il faut faire Math Sup pour afficher une fentre ?
Pas dinquitude : personne nest capable de crer un programme informatique ainsi.
Cest pourquoi ont t invents diffrents langages pour la programmation : le Pascal,
le Basic, le C, le C++, le Python, le Java. . . et bien sr lAda. Ce sont ce que lon
appelle des langages de haut niveau. quoi cela ressemble-t-il ? Voyez vous-mme :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10
11
12
13
14
15

procedure exemple is
n : integer ;
begin
loop
Put ( " Saisir un nombre : " ) ;
Get ( n ) ; Skip_line ;
if n mod 2 = 0
then Put ( " Ce nombre est pair ! Tres bien ! " ) ; exit ;
else Put ( " Ce nombre est impair ! Recommencez . " ) ;
end if ;
end loop ;
end exemple ;

Vous serez capables dici quelques chapitres de comprendre ce texte aisment. Cest
comme apprendre une langue trangre ; dailleurs si vous connaissez langlais, vous
avez peut-tre reconnu quelques mots : end, if, else, then, begin et is. Cest ainsi
que nous crerons nos programmes.
6

POURQUOI ADA ET PAS AUTRE CHOSE ?

Et comment on traduit a en 1 et en 0 ?

Une fois notre texte rdig en Ada (mais cest aussi valable pour les autres langages),
il faudra utiliser un programme-traducteur pour le convertir en un programme lisible
par lordinateur. Les programmes-traducteurs sont appels compilateurs.

Pourquoi Ada et pas autre chose ?


Algorithmique
Il ne vous reste donc plus qu apprendre un langage de programmation, et avec ce
langage, vous apprendrez galement ce que lon appelle lalgorithmique.
Algorithmique ? Quest-ce que cest que a encore ?

Lalgorithmique est une branche des mathmatiques qui traite de la rsolution de problmes laide dinstructions simples. Jen vois dj certains qui paniquent la vue du
mot Mathmatiques , mais nayez crainte vous avez dj vu plusieurs fois dans votre
vie des algorithmes. Par exemple, comment faire pour diviser 742 par 5 ? Vous vous
souvenez, ctait en primaire ? Vous aviez appris vous demander : dans 7 combien
de fois 5 ? Il y va 1 fois et il reste 2 . Puis vous rptiez cette opration : dans 24
combien de fois 5 ? Il y va 4 fois et il reste 4 . . . Il suffisait de rpter plusieurs fois
cette mthode basique pour obtenir le rsultat dsir (Voir la figure 1.3).

Figure 1.3 La division illustre


Eh bien a, ctait un algorithme : vous rptiez des instructions simples dans un
certain ordre (abaisser les chiffres de 742, se demander dans machin, combien de fois
7

CHAPITRE 1. PROGRAMMATION, ALGORITHMIQUE ET ADA ?


truc ? . . .) afin de rsoudre un problme plus compliqu (Combien vaut 742 5 ? ).
Et tout programme informatique nest en fait quun algorithme crit dans un langage
comprhensible par votre ordinateur. Donc apprendre programmer, cest en fait apprendre la science de lalgorithmique dans un langage informatique donn.

Pourquoi Ada ?
Mais alors, pourquoi apprendre Ada et pas simplement lalgorithmique ? Et
pourquoi utiliser Ada et pas un autre langage ?
Tout dabord, lalgorithmique nest pas comprhensible par les ordinateurs, je vous
rappelle que cest une science. Lalgorithme de la division euclidienne vu au-dessus est
expliqu en franais, or je vous ai dit quil doit tre crit dans un langage qui pourrait
tre traduit par un compilateur, un langage trs structur et norm (qui ne changera
pas en changeant de compilateur), et a, cest ce quon appelle la programmation. Et
Ada rpond ces exigences.
Au dbut, on parlait dAda tout court ou dAda83. Puis le langage a volu,
donnant naissance aux normes Ada95 puis Ada2005 et enfin Ada2012. Mais
rassurez-vous, tout cela ne constitue quune sorte de grosse mise jour
et pas 3 langages diffrents ! Cest pourquoi nous ne parlerons pas ici des
langages Ada83, Ada95 ou Ada2005 mais simplement du langage Ada.
Maintenant, pourquoi utiliser Ada et non pas un autre langage ? Il est vrai que Ada nest
pas le langage le plus rpandu. Mais il prsente de nombreux avantages par rapport
dautres langages plus courants comme le C, le C++, le Java. . .
Tout dabord il prsente un avantage pdagogique. Ada consitue un langage parfait
pour apprendre programmer. Dailleurs de nombreuses coles ou universits lutilisent comme support de leurs cours de programmation. La rigueur quil impose au
programmeur permet aux dbutants dapprendre coder correctement, de prendre de
bonnes habitudes quils conserveront par la suite, mme sils venaient changer de
langage de programmation durant leur carrire. Qui plus est, cest un langage facilement lisible car il utilise des mots complets (begin, end, function, if. . . cest de
langlais en effet) plutt que les symboles barbares et bizarrodes pour un dbutant
que prfrent la plupart des langages actuels. Les codes des programmes sont certes
moins condenss, mais plus faciles reprendre en cas de bogue ce qui constitue un
avantage non ngligeable quand on sait que les cots de maintenance dun logiciel
sont souvent plus levs que le cot de son dveloppement.
Ensuite, le langage prsente galement des avantages pour les professionnels. Son
fort typage (vous aurez loccasion de le dcouvrir durant la deuxime partie) ou sa
trs bonne gestion des erreurs (aborde lors de la quatrime partie) garantissent des
programmes fiables. Ce nest pas pour rien quAda est utilis pour de gros projets
comme Ariane, chez Thals pour ses avions ou radars, ou encore pour la scurit
8

POURQUOI ADA ET PAS AUTRE CHOSE ?


des centrales nuclaires, du trafic arien ou ferroviaire. . . Utiliser Ada est un gage de
fiabilit et pour cause, il a t dvelopp par le polytechnicien franais Jean Ichbiah
pour le compte du dpartement de la dfense amricain, le fameux DoD (vous vous
doutez bien que rien na t laiss au hasard ).
Maintenant que ces quelques explications prliminaires sont faites, jespre que vous
vous sentez toujours prts vouloir apprendre un langage. Si cela vous semble compliqu, nayez crainte, nous commencerons doucement et je vous guiderai pas pas. Nos
premiers programmes seront peut-tre inutiles ou ringards, mais nous apprendrons peu
peu de nouvelles notions qui nous permettront de les perfectionner.

En rsum
Un programme est un fichier contenant des instructions destination du processeur de votre ordinateur.
Un programme est crit en langage binaire, le seul langage que comprend votre
ordinateur.
Pour raliser un programme, vous devez crire des instructions dans un fichier
texte. Celles-ci sont crites dans un langage de programmation (ou langage
formel) comme Ada, comprhensible par les humains.
Pour traduire un texte crit en langage de programmation en un programme crit
en binaire, vous devrez utiliser un logiciel appel compilateur.

CHAPITRE 1. PROGRAMMATION, ALGORITHMIQUE ET ADA ?

10

Chapitre

Les logiciels ncessaires


Difficult :
Nous allons au cours de ce chapitre, tlcharger et installer les logiciels ncessaires lutilisation dAda.
Comment a LES logiciels ? Il ny a pas un seul logiciel Ada ?

Eh bien non. Comme tout langage, Ada a besoin de deux types de logiciels : un diteur de
texte avanc (appel IDE) et un compilateur. Nous utiliserons lIDE appel Adagide (mme
si le logiciel GPS pourra tre utilis) et le compilateur GNAT. Aprs quoi, nous pourrons
dcouvrir (enfin) le joyeux monde de la programmation.

11

CHAPITRE 2. LES LOGICIELS NCESSAIRES

IDE et compilateur : kesako ?


Le compilateur
Nous avons dors et dj parl du compilateur : il sagit dun programme qui va traduire notre langage Ada en un vrai programme utilisable par lordinateur. Le compilateur est aussi charg de vrifier la syntaxe de notre code : est-il bien crit ? Sans
fautes dorthographe ou de grammaire ? Il existe plusieurs compilateurs en Ada, mais
rassurez-vous, quel que soit celui que vous choisirez, les rgles sur le langage Ada seront
toujours les mmes car Ada est un langage norm (il bnficie dune norme internationale assurant que tous les compilateurs Ada respecteront les mmes rgles). Il existe
plusieurs compilateurs Ada (vous en trouverez une liste ladresse suivante : http:
//www.fr.wikibooks.org/wiki/Programmation_Ada#Compilateurs, mais la plupart
sont des programmes payants et propritaires (donc non libre).
Payant ! Ctait pas prvu au contrat a !

Rassurez-vous, nous allons plutt opter pour un compilateur libre et gratuit. Ce compilateur sappelle GNAT et est dvelopp par la socit Adacore. Il a longtemps t
payant mais une version gratuite existe dsormais (GNAT GPL).

LIDE ou EDI
Cela dit, le compilateur nest en dfinitive quun traducteur . Il ne crera pas de
programme votre place, cest--vous dcrire les instructions dans des fichiers textes.
Pour cela, vous pouvez crire vos documents sous lditeur de texte de votre ordinateur
(notepad sous Windows par exemple), ou utiliser un diteur de texte un peu plus
avanc comme Notepad++ (http://www.notepad-plus-plus.org/fr/) qui colorera
votre texte intelligemment en reconnaissant le langage dans lequel vous programmerez
(on appelle a la coloration syntaxique).
Mais le plus simple, cest dutiliser un Environnement de Dveloppement Intgr, EDI
en franais ou IDE en anglais. Pourquoi ? Eh bien parce que ce logiciel vous fournira
un diteur de texte avec coloration syntaxique et surtout vous permettra de compiler
votre texte aisment, sans avoir besoin de chercher o se trouve le compilateur sur votre
ordinateur. De plus, lIDE se chargera de le paramtrer pour vous et vous fournira divers
outils utiles la programmation.
LIDE que nous allons utilis sappelle Adagide et il est gratuit.

Tlcharger et installer Adagide.


Disponible sous Windows uniquement
12

TLCHARGER ET INSTALLER GNAT

Tlchargement
Cliquez ici : http://www.sourceforge.net/projects/adagide/files/adagide-installer/
7.45.0002/ pour tlcharger Adagide dans sa version 7.45 (dernire en date lheure
o jcris ces lignes). Puis cliquez sur adagide-7.45-setup.exe pour lancer le tlchargement de linstallateur.

Installation
Linstallation dAdagide ne devrait pas vous poser de problmes. Il vous suffit de suivre
les diffrentes tapes des figures 2.1 et 2.2 en cliquant sur Suivant . Acceptez, tout
dabord, les termes de la licence et ensuite, lcran Setup Type , choisissez Typical .

Figure 2.1 Termes de la licence

Tlcharger et installer GNAT


Disponible sous Windows et Linux

Tlchargement
Pour tlcharger le compilateur GNAT GPL, rendez-vous sur le site dAdacore en
cliquant ici : http://www.libre.adacore.com/libre/download2.
Si vous tes sous Windows, vrifiez que le systme dexploitation est bien x-86 Windows ( la ligne Select your platform ), puis cliquez sur GNAT_GPL et s13

CHAPITRE 2. LES LOGICIELS NCESSAIRES

Figure 2.2 Prfrez linstallation Typical


lectionnez gnat-gpl-2013-i686-pc-mingw32-bin.exe . Enfin, tout en bas de la page,
cliquez sur le bouton Download selected files .
Pour les utilisateurs de Linux, le systme dexploitation devrait tre x86 - Linux (ou
x86_64 - Linux si votre processeur est en 64 bits). De la mme manire que sous
Windows, cliquez sur GNAT_GPL et slectionnez gnat-gpl-2013-i686-gnu-linuxlibc2.3-bin.tar.gz . Pour finir, cliquez sur le bouton Download selected files en bas
de page.

Installation

Linstallation est un poil plus complique (mme si a ne casse pas trois pattes un
canard, comme dirait ma grand-mre ). Le fichier tlcharg sappelle AdaCore.tar
et ce nest pas un programme dinstallation, seulement un fichier compress. Pour
le dcompresser, utilisez un logiciel comme Winrar ou 7zip (http://www.clubic.com/
telecharger-fiche9632-winrar.html, http://www.clubic.com/telecharger-fiche11161html\url). Sous Linux, vous pouvez procder graphiquement ou par la console. Dans
le second cas, vous devrez taper la commande suivante :
tar - xvzf gnat - gpl -2013 - i686 - gnu - linux - libc2 .3 - bin . tar . gz

Sous Windows, vous obtiendrez ainsi le fameux fichier dinstallation gnat-gpl-2013i686-pc-mingw32-bin.exe . Ouvrez-le et suivez les diffrentes tapes. Je vous conseille
de slectionner loption Install for all users , notamment si votre ordinateur dispose
de plusieurs sessions, moins que vous ne vouliez tre le seul pouvoir programmer
en Ada.
14

TLCHARGER ET INSTALLER GPS


Sous Linux, linstallation se fera via le fichier script appel doinstall . Celui-ci vous
demandera si vous souhaitez installer GNAT et si oui, o. Vous naurez qu appuyer
deux fois sur Entre
 puis rpondre Yes aux deux dernires questions en appuyant

sur les touches Y ou y .
 

Tlcharger et installer GPS


GPS est un autre IDE, mais dvelopp par AdaCore. Il est un peu plus compliqu
et javoue avoir eu du mal linstaller sur mon PC Windows personnel (mme sil
fonctionne sur mon PC Windows de bureau ou sous mon PC Ubuntu ). GPS est conu
pour grer des projets importants composs de nombreux fichiers, mais Adagide a
lavantage dtre simple et performant, je le prfrerai donc GPS pour ce cours. Mais
si vous souhaitez tout de mme utilisez GPS, sachez qu cette tape du chapitre. . . il
est dj install ! Cet IDE est en effet livr avec GNAT, vous pourrez donc le tester
si la curiosit vous prend : il suffit daller dans le menu Dmarrer > Programmes >
GNAT > 2010 > GPS.
Si vous tes sous Linux, vous ne pouvez disposer dAdaGide. Vous serez donc
amens utiliser lIDE dAdacore : GPS. Si celui-ci napparat pas dans vos
applications, vous pourrez le lancer en tapant la commande gnat-gps dans
votre console ou bien en lajoutant dans la liste de vos applications.

En rsum
Vous devez disposer dun IDE ou, dfaut, dun diteur de texte afin de rdiger
le code source de votre programme.
Je vous conseille dutiliser lIDE Adagide pour sa simplicit. Noptez pour GPS
que lorsque vous aurez acquis la maturit suffisante.
Vous devez disposer dune version du compilateur GNAT.

15

CHAPITRE 2. LES LOGICIELS NCESSAIRES

16

Chapitre

Notre premier programme en Ada


Difficult :
Bon, nous avons install notre IDE, Adagide, notre compilateur, GNAT. Je sens que vous
commencez perdre patience. Nous allons immdiatement pouvoir nous atteler notre
premier programme en Ada. Il sagira de crer un programme qui nous salue. Bon, ce nest
srement pas folichon comme objectif, mais cest un dbut. Voyons ici la dmarche que
nous allons suivre.
1. Tout dabord, nous allons faire un petit tour dhorizon des logiciels Adagide et GPS.
Je vous rassure, ce sera rapide.
2. Puis nous crirons notre premier programme et nous le testerons.
3. Enfin, nous essaierons de dcrypter notre premier programme
4. Avant de commencer le prochain chapitre je vous proposerai quelques petits supplments et exercices pour vous entraner.
Toujours pas dcourag ? Alors au travail !

17

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA

Dcouvrir son IDE en quelques secondes


Soyons rapide avec Adagide
Pour pouvoir programmer, nous avons dj expliqu que nous aurons besoin de lIDE
Adagide. Donc, lancez Adagide ! Vous voil donc face une fentre (voir la figure 3.1)
tout ce quil y a de plus. . . austre. Mais cela na que peu dimportance pour nous.

Figure 3.1 Votre IDE : Adagide


Tout dabord, nous allons crer un nouveau document. Pour cela, cliquez sur File >
New ou sur licne Nouveau indiqu sur la figure prcdente. Vous pourrez galement
lavenir ouvrir un document existant en cliquant sur File > Open ou sur licne
Ouvrir .
Pour lheure, nous allons enregistrer notre document. Cliquez sur File > Save as ou
sur licne enregistrer . Je vous conseille de crer un rpertoire que nous nommerons
Hello , et dans ce rpertoire nous allons enregistrer notre document (appelons-le
galement Hello ). Vous remarquerez que lextension propose est adb. Notre code
en Ada portera le nom de Hello.adb ; par la suite nous verrons quoi peut servir
lextension ads qui est galement propose.
Bien entendu, il est possible dcrire dans la fentre principale, la taille du texte pouvant tre modifie en cliquant sur le bouton taille . En revanche, il est impossible
dcrire dans la partie basse de la fentre, celle-ci tant rserve au compilateur. Cest
ici que le compilateur affichera les informations qui vous seront ncessaires : chec de
18

DCOUVRIR SON IDE EN QUELQUES SECONDES


la compilation, russite de la compilation, lignes inutiles . . .

Pour les utilisateurs de GPS (plus long)


Le logiciel GPS est un peu plus compliqu que Adagide. Il est toutefois mieux adapt
de gros projets, ce qui explique quil soit plus complexe et donc moins adapt pour
ce cours. Lorsque vous lancez GPS, une fentre devrait vous demander ce que vous
souhaitez faire (voir figure 3.2).

Figure 3.2 Fentre daccueil du logiciel GPS


Pour lheure, choisissez Create new project with wizard et cliquez sur OK . Choisissez loption Single project puis cliquez sur Forward . Choisissez un nom pour
votre projet (jai choisi HelloWorld ) et prcisez un rpertoire o lenregistrer (je vous
conseille de crer un rpertoire spcifique par projet). Cliquez ensuite sur Apply sans
renseigner les autres informations. Vous devriez arriver la figure 3.3.
La fentre est dcoupe en quatre zones. La premire ne nous intresse pas pour linstant. La seconde, juste en dessous, affichera les fichiers et programmes de votre projet.
Pour lheure vous naurez quun seul fichier donc cela ne comportera pas dintrt pour
les premiers cours. Son utilit se rvlera partir de la Partie III. La troisime zone
de la fentre est la zone principale, celle o vous rdigerez votre programme. Enfin, la
quatrime zone est celle rserve au compilateur et la console. Vous comprendrez son
utilit bientt.
Commenons donc par crer un nouveau fichier en cliquant sur licne Nouveau
ou sur File > New. Sauvegardez tout de suite votre fichier en cliquant soit sur licne
Enregistrer soit sur File > Save As. Votre fichier devra sappeler Hello.adb (mme
si vous navez pas besoin dcrire lextension). Comme je vous le disais plus haut, il
existe galement un fichier .ads que nous verrons plus tard et que le logiciel GPS gre
19

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA

Figure 3.3 Fentre de projet avec GPS


aussi. Pour ouvrir un document, vous pourrez utiliser licne Ouvrir ou sur File >
Open.
Voila pour lessentiel, toutefois, je vais vous demander deffectuer deux petites manipulations avant daller plus loin afin dajouter deux icnes importantes. Cliquez sur le
menu Build > Settings > Targets. Une fentre devrait souvrir (voir la figure 3.4).
gauche, dans la section Project, cliquez sur Build <current file> et cochez la case
In the toolbar. Puis, dans la section Run, cliquez sur Custom et cochez la case In the
toolbar. Enfin, cliquez sur le bouton Apply. Deux icnes devraient sajouter, comme
celles des figures 3.5 et 3.6 (retenez-les bien).

Notre premier programme


Un petit copier-coller !
Maintenant que nous avons rapidement pris connaissance du logiciel, il est temps de
nous lancer corps et me dans la cration de notre magnifique programme Hello !
Nous allons pour cela effectuer une opration de haute voltige : un copier-coller ! Voici le
code dun programme. Ne cherchez pas comprendre pour linstant, je vous expliquerai
par la suite. Pour lheure, slectionnez le texte, copiez-le et collez-le dans votre fichier
Hello.adb sans poser de questions.
1
2

20

with ada . text_io ;


use ada . text_io ;

NOTRE PREMIER PROGRAMME

Figure 3.4 Fentre de configuration

Figure 3.5 Construire

Figure 3.6 Excuter

21

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA


3

procedure Hello is
-- partie r serv e aux d clarations
begin
put ( " Salut tout le monde ! " ) ; -- on affiche un message
end Hello ;

4
5
6
7
8

Vous remarquerez que certains mots sont automatiquement colors, je vous en ai dj


parl, cest la coloration syntaxique. Elle permet de faire ressortir certains mot-cls
(Adagide colore par dfaut en bleu les mots rservs au langage Ada) ou certains
types de texte.
Avant daller plus loin, si vous tes sous Adagide (GPS effectuera cette manuvre tout
seul le moment venu), il serait bon de cliquer sur licne Reformat , il est simple
trouver, cest celui avec un grand R dessin dessus. Cela va mettre votre code en
forme : la ligne entre is et begin et la ligne entre begin et end vont tre avances
laide dune tabulation (trois espaces sous adagide). On appelle a lindentation.
Ca na lair de rien mais cest trs important car lavenir votre code pourra stendre
sur plusieurs pages, il est donc important quil soit le plus lisible possible. Lorsque
lon rdige un roman, on cre des paragraphes, des chapitres. . . en programmation on
indente son texte.

Compiler, crer. . . lancer !


Maintenant que votre texte est crit et mis en forme, il est temps de le faire fonctionner.
Nous allons donc utiliser le compilateur.
Pour cela, rien de plus simple, cliquez sur

licne Compiler (ou appuyer sur F2 avec Adagide, ou encore Compile > Compile
File). Le compilateur vrifiera la syntaxe de votre code. Sil ny a pas derreur (et il ne
devrait pas y en avoir), le compilateur devrait vous indiquer (dans la fentre du bas)
Completed successfully .
Mais ce stade, votre programme nexiste pas encore, vous devez
 construire lexcutable. Pour cela cliquez sur licne construire (ou appuyez sur F3 avec Adagide ou
cliquez sur Compile > Build ).
Pour les utilisateurs de GPS, les manipulations tant un peu longues, je vous conseille
dutiliser les icnes (cest bien pour cela que je vous ai fait faire une petite manipulation prliminaire). Lorsque vous cliquerez sur licne Construire
, une fentre peut

souvrir. Ne vous souciez pas des paramtres et cliquez sur OK .

Votre fichier excutable est dsormais cr. Vous pouvez soit aller le chercher dans le
rpertoire Hello que
 vous avez cr tout lheure, soit cliquer sur licne Excuter
(ou appuyer sur F4  avec Adagide ou cliquer sur Run > Execute). Sous GPS, une
fentre souvrira. Ne cochez aucune case et, si la barre de texte est vide, crivez-y le
nom de votre programme : Hello (sans extension !). Puis cliquez sur OK .
Avec Adagide, vous devriez ainsi obtenir une magnifique fentre noire vous indiquant :
Salut tout le monde !

22

MON DIEU, QUAI-JE FAIT ?


(Sous GPS, ces actions apparatront dans la fentre du compilateur, cest--dire la
quatrime zone de la fentre)
Euh. . . comment dire. . ., cest tout ? Jai lu tous ces chapitres pour voir a ? !

Ne perdez pas patience. Le chemin sera long avant que vous ne puissiez crer un programme digne dintrt. Prenons le temps de dcortiquer ce que nous avons copi pour
mieux comprendre.

Mon dieu, quai-je fait ?


Si nous jetons un il notre code nous pouvons nous rendre compte quil peut se
dcomposer en deux parties majeures (voir figure 3.7) : une sorte de titre (avec with
et use) et un gros paragraphe (commenant par le mot procedure).

Figure 3.7 Organisation de votre code

Le corps du programme : la procdure Hello


Le titre tant un peu compliqu expliquer, nous allons commencer par nous intresser
au gros paragraphe : la procdure appele Hello. Elle peut se dcomposer elle-mme
en deux parties comme sur la figure prcdente.
Les trois bornes de la procdure
Pour linstant, disons que le terme de procdure est un synonyme de programme .
Notre paragraphe commence donc par lintroduction suivante : PROCEDURE Hello IS.
Cette phrase permet de donner un nom notre procdure et indique le dbut du texte
la concernant. Remarquez que les mots procedure et is sont colors en bleu : ce sont
des mots rservs par le langage Ada, ils ont donc un sens trs prcis et ne peuvent
tre utiliss nimporte comment.
Deuxime borne de notre procdure : le terme begin. Cest lui aussi un mot rserv.
Il indique au compilateur le dbut des instructions que lordinateur devra excuter.
Troisime borne : le mot end, ou plus exactement la phrase : END Hello ; . Cette
phrase indique au compilateur la fin de la procdure Hello. noter que contrairement
23

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA


aux deux bornes prcdentes, cette phrase se termine par un point-virgule. Si vous
oubliez de lcrire, le compilateur vous avertira : missing " ;" ! Notez que les points
virgule indiquent au compilateur la fin dune instruction : ici, cest la fin de votre
procdure.
La partie Dclaration
Les trois bornes vues prcdemment dlimitent deux zones. La premire, celle comprise entre is et begin, est rserve aux dclarations. Nous verrons dans les prochains
chapitres de quoi il retourne. Sachez pour linstant que votre programme peut avoir
besoin de mmoire supplmentaire pour fonctionner (et nous aurons trs vite besoin de
davantage de mmoire) et que cest dans cette partie que seffectueront les demandes
de rquisition de mmoire. Pour lheure, il ny a rien.
Comment a il ny a rien ? Il y a bien quelque chose dcrit : partie
rserve aux dclarations
Eh bien non. Pour le compilateur, il ny a rien ! Cette ligne verte commence par deux
tirets. Cela signifie quil sagit dun commentaire, cest--dire dun texte qui ne sera
pas pris en compte par le compilateur. Quel intrt dcrire du texte sil nest pas pris
en compte par le compilateur ? Eh bien cela permet dapporter des annotations votre
code. Si vous relisez un code crit par quelquun dautre ou par vous mme il y a
longtemps, vous risquez davoir du mal le comprendre. Les commentaires sont donc
l pour expliquer ce que fait le programme, quoi servent telle ou telle ligne, etc. Un
bon code est dj un code bien comment (et bien indent galement).
La partie Action et notre premire instruction
Puis, aprs le BEGIN, vient la partie la plus intressante, celle o lon indique au programme ce quil doit faire, autrement dit, cest l que nous crirons les diffrentes
instructions.
Linstruction cite ici est : Put("Salut tout le monde!") ; . Cest assez simple
comprendre : linstruction Put() indique lordinateur quil doit afficher quelque chose.
Et entre les parenthses, on indique ce quil faut afficher. Il est possible dafficher un
nombre (par exemple, Put(13) ; affichera le nombre 13) comme du texte, mais le
texte doit tre crit entre guillemets.
Remarquez l encore que linstruction se termine par un point-virgule. Dailleurs, toutes
les instructions devront se terminer par un point virgule, quelques exceptions prs
(souvenez-vous de IS et BEGIN). Le texte qui suit est bien entendu un commentaire et
tout ce qui suit les double-tirets ne sera pas pris en compte par le compilateur.
Il est bien sr possible dafficher autre chose que "salut tout le monde !" ou deffectuer dautres actions. Toutefois, lorsque toutes vos actions ont t crites, noubliez
pas dindiquer au compilateur que vous avez atteint la fin du programme en utilisant
24

MON DIEU, QUAI-JE FAIT ?


le mot-cl END.

Les Packages avec With et Use


Le mot-cl WITH
Revenons maintenant au titre. Pourquoi cet intitul ? Et dailleurs pourquoi lcrire
deux fois ? Je vous propose de le supprimer pour mieux comprendre. Compiler nouveau votre code.
Argh ! ! a ne marche plus ! Jai pleins de messages rouge ! Tout est fichu !

En effet, le code nest plus correct et ce nest pas la peine dessayer de reconstruire notre
programme. Mais lisons tout dabord les avertissements du compilateur : Put is
undefined.
Cela signifie tout simplement que le compilateur ne connat plus linstruction Put() !
En effet, le compilateur ne connat que trs peu de mots et dinstructions : les mots
rservs et puis, cest presque tout. Pour aller plus loin, il a besoin de fichiers qui
contiennent davantage dinstructions comme linstruction Put() par exemple, qui se
situe dans un fichier appel Ada.Text_IO. Ces fichiers portent le nom de package en
Ada (paquetages en franais). Dans dautres langages, on parlerait de librairies.
Nous reviendrons plus en dtail sur les packages durant la troisime et la quatrime partie de ce tutoriel. Donc si cette sous-partie vous semble complique,
rassurez-vous, je vous indiquerai toujours les packages crire en dbut de
code.
Le compilateur nous dit galement : possible missing WITH Ada.Text_IO ;
USE Ada.Text_IO ; . Traduction : ce serait bien de remettre les lignes que vous
avez supprim tout lheure ! coutons-le mais ne rcrivons que la premire ligne (au
tout dbut) :
1

WITH Ada . Text_IO ;

Le mot-cl USE
Ressayons de compiler.
Jai un nouveau problme. Le compilateur mindique : Put is not visible,
plus plein dautres insultes incomprhensibles. Apparemment, il connait mon
instruction Put() mais il narrive plus la voir ? !
Exactement. Et les lignes qui suivent indiquent que dans le package ada.Text_IO, il
25

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA


y a plusieurs rfrences linstruction Put(). En fait, il est possible aprs linstruction
with dcrire de nombreux packages, autant que vous voulez mme (nous verrons un
exemple juste aprs). Mais il peut exister (et il existe) plusieurs instructions portant
le nom Put(), du coup le compilateur ne sait pas do vient cette instruction. De
laquelle sagit-il ? Une solution est de remplacer Put() par Ada.Text_IO.Put() ! Cest
compliqu, cest long crire et nous aurons tout le loisir de comprendre tout cela plus
tard. Donc une faon de spargner ces difficults, cest dcrire notre instruction Use
au tout dbut, juste aprs linstruction with.
Ces deux lignes (appeles Context Clause en Ada ou Clauses de contexte en Franais)
sont donc indispensables et vous serez souvent amens rcrire les mmes (vous
bnirez bientt linventeur du copier-coller). Ne croyez pas que cette lourdeur soit
spcifique Ada. Tout langage a besoin de packages ou librairies annexes, tout nest
pas prdfini et heureusement pour nous : cest ce que lon appelle la modularit. Au
final, voici la structure de notre programme et de tout programme Ada (voir la figure
3.8).

Figure 3.8 La structure du programme

Avec plusieurs packages


Jai essay de bidouiller le code pour quil affiche 13 comme tu le disais
dans un exemple. Mais le compilateur rle encore : warning : no entities of
Text_Io are referenced, no candidate match the actuals :. . . Bref,
rien ne va plus. Faut-il un autre package ?
Exactement ! Ada.Text_IO est un package cr pour grer le texte comme son nom
lindique (Text = texte et IO = In Out, entre et sortie). Pour afficher un nombre entier
(ici 13), il faut utiliser le package Ada.Integer_Text_IO. Par exemple :
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO ;


USE Ada . Text_IO , Ada . Integer_Text_IO ;

Les instructions WITH et USE sont termines par un point-virgule, en revanche


les deux packages sont simplement spars par une virgule !
Pour plus de claret, il est galement possible dcrire ceci :
1

26

WITH Ada . Text_IO ,

EXERCICES
2
3
4

Ada . Integer_Text_IO ;
USE Ada . Text_IO ,
Ada . Integer_Text_IO ;

ou ceci :
1
2

WITH Ada . Text_IO ;


WITH Ada . Integer_Text_IO ;

USE Ada . Text_IO ;


USE Ada . Integer_Text_IO ;

Et oui ! Bien Prsenter son code, cest trs important !

Une dernire remarque qui a son importance


Vous devez savoir avant de vous lancer laventure que le langage Ada nest pas case
sensitive , comme diraient nos amis anglais. Cela signifie quil ne tient pas compte de
la casse. Autrement dit, que vous criviez en majuscule ou en minuscule, cela revient
au mme : BEGIN, Begin, begin ou bEgIn seront compris de la mme manire par le
compilateur. Toutefois, Ada sait faire la diffrence lorsquil faut. Si vous modifiez la
casse dans la phrase afficher "Salut tout le monde !", laffichage en sera modifi.
De mme, une tabulation ou un espace seront pris en compte de la mme faon. Et que
vous tapiez un, deux ou trois espaces ne changera rien.

Exercices
Exercice 1
Ce premier programme est peut-tre simple, mais voici quelques exercices pour nous
amuser encore. Nous avons vu linstruction Put() qui affiche du texte, en voici maintenant une nouvelle : New_line. Elle permet de retourner la ligne. Noubliez pas le
point virgule la fin.
nonc
crire un programme affichant ceci :
Coucou
tout le
monde
! ! !

Solution
1
2

With ada . text_io ;


use ada . text_io ;

27

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA


3
4

procedure Hello2 is

5
6
7
8
9
10
11

begin
Put ( " Coucou " ) ; New_line ;
Put ( " tout le " ) ; New_line ;
Put ( " monde " ) ; New_line ;
Put ( " ! ! ! " ) ;
end Hello2 ;

Exercice 2
Troisime instruction : Put_line(). Elle fonctionne comme Put(), sauf quelle cre automatiquement un retour la ligne la fin.
nonc
Mme exercice que prcdemment, mais sans utiliser New_line.
Solution
1
2

With ada . text_io ;


use ada . text_io ;

3
4

procedure Hello3 is

5
6
7
8
9
10
11

begin
Put_line ( " Coucou " ) ;
Put_line ( " tout le " ) ;
Put_line ( " monde " ) ;
Put ( " ! ! ! " ) ;
est arriv la fin
end Hello3 ;

-- pas besoin de put_line ici , on

Exercice 3
nonc
Pourquoi ne pas crer des programmes affichant autre chose que "coucou tout le
monde !".
Attention, vous navez pas le droit aux caractres accentus. Uniquement les
caractres anglo-saxons si vous ne voulez pas avoir de drles de surprises.
Donc pas de , , , , , , . . .
Vous avez dsormais cr votre premier programme. Certes, il nest pas rvolutionnaire,
mais cest un dbut. Nous allons pouvoir quitter la premire partie de ce tutoriel et
28

EXERCICES
entrer dans les bases de la programmation en Ada. La prochaine partie nous permettra
de manipuler des nombres, deffectuer des oprations, de saisir des valeurs au clavier
. . . peu peu nous allons apprendre crer des programmes de plus en plus complexes.

En rsum
Tout programme dbute par les clauses de contexte : laide des mots cls WITH
et USE, vous devez lister les packages ncessaires.
Les instructions Put(), Put_line() et New_line() sont accessibles avec le package
Ada.Text_IO. Elles permettent respectivement dcrire du texte, dcrire une
ligne et de retourner la ligne.
Les instructions doivent sachever par un point virgule.
Prenez soin de bien prsenter votre code. Pour cela, indentez-le en utilisant la
tabulation et commentez-le en crivant quelques indication aprs un double-tiret.
Les mots rservs, comme BEGIN, END, PROCEDURE, WITH ou USE, ont un sens bien
prcis et ne pourront pas tre utiliss pour autre chose que ce pour quoi ils sont
prvus.

29

CHAPITRE 3. NOTRE PREMIER PROGRAMME EN ADA

30

Deuxime partie

Ada, notions essentielles

31

Chapitre

Variables I : Typage et affectation


Difficult :
Nous avons dors et dj ralis notre premier programme. Mais chacun comprend vite les
limites de notre programme "Hello". dire vrai, un tel programme napporte absolument
rien. Ce qui serait intressant, ce serait que lutilisateur de notre programme puisse entrer
des informations que lordinateur lui demanderait.

33

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


Par exemple :
Quel ge avez - vous ? _

Et l nous pourrions entrer 25, 37 ou 71. Le programme pourrait ensuite se charger


denregistrer cette information dans un fichier, de nous avertir si nous sommes considrs comme majeur dans ltat du Minnesota ou encore de nous donner notre ge en
2050. . . Autant dapplications que nous ne pourrons pas raliser si lge que lutilisateur
indiquera nest pas enregistr dans une zone de la mmoire.
Et cest l que les variables interviennent ! Attention, ce chapitre nest peut-tre pas
exaltant mais il est absolument ncessaire la comprhension des prochains.

Dclaration de variables
Nous allons reprendre lexemple de lintroduction. Nous voulons indiquer un ge
notre programme. Nous aurons donc besoin dun espace mmoire capable de stocker
un nombre entier. Il faudrait effectuer une demande dallocation dune zone mmoire
et retenir ladresse mmoire qui nous a t attribue et la taille de la zone alloue.
Euh. . . ctait pas marqu Niveau facile ?

Pas dinquitude. Tout cela, cest ce quil aurait fallu faire (entre autre) si nous navions
pas utilis un langage comme Ada. Cest ce quon appelle un langage de haut niveau,
cest--dire que lon naura pas besoin de se casser la tte pour faire tout cela. Il suffira
de dire Je veux de la mmoire ! .
Toutefois, lordinateur a tout de mme besoin de connatre la place mmoire dont vous
aurez besoin et cela dpend du type dinformation que lon souhaite y enregistrer. Ici,
nous voulons enregistrer un nombre entier que nous appelons age. Le mot age est le
nom de notre variable, cest--dire lendroit de la mmoire o seront enregistres nos
informations. Pour faire tout cela, il faut dclarer notre variable (comme on le ferait
la douane, cela permet de savoir qui vous tes et comment vous retrouver). Une
dclaration se fait toujours de la faon suivante :

NOM_DE_LA_VARIABLE : TYPE ;
Il est possible de dclarer plusieurs variables dun coup de la manire suivante :

NOM_DE_LA_VARIABLE1 , NOM_DE_LA_VARIABLE2 : TYPE ;


Il est aussi possible, aussitt la variable dclare, de lui affecter une valeur laide du
symbole := . On crira alors :

NOM_DE_LA_VARIABLE : TYPE := VALEUR ;


34

DIFFRENTS TYPES

O dois-je faire cette dclaration ? En prfecture ?

Vous vous souvenez notre premier programme ? Je vous avais parl dune zone rserve
aux dclarations (entre is et begin). Et bien cest ici que nous dclarerons notre
variable.
1
2
3
4
5
6

Procedure VotreAge is
N OM _ D E_ L A_ VARIABLE1 : TYPE1 := VALEUR ; -- zone r serv e aux
d clarations
N OM _ D E_ L A_ VARIABLE2 : TYPE2 ;
begin
Put ( " Quel age avez - vous ? " ) ;
end ;

Bien, il est temps de rentrer dans le vif du sujet : comment dclarer notre variable ge ?

Diffrents types
Les types Integer et Natural
Dfinition
Le type Integer est rserv aux nombres entiers relatifs. Pour ceux qui ne se souviendraient pas de leur cours de mathmatiques, un entier est un nombre qui na pas de
chiffres aprs la virgule part 0 . Relatif signifie que ces entiers peuvent tre positifs
(avec un signe +) ou ngatifs (avec un signe -).
Le type Natural est rserv aux nombres entiers naturels (cest--dire positifs ou nul).
Il est donc impossible, si vous dclarez votre variable comme un Natural, que celle-ci
soit ngative (ou alors vous planterez votre programme). Pour tre plus prcis, cest un
sous-type du type Integer. Quelle importance ? Eh bien, cela signifie que lon pourra
mlanger les natural et les Integer sans risquer le plantage : nous pourrons les
additionner entre eux, les soustraire, les multiplier. . . ce qui nest pas possible avec
dautres types. La seule restriction est que notre natural ne devienne pas ngatif.

Valeurs possibles
Si N est une variable de type Integer, elle peut prendre les valeurs suivantes : 0 ; 1 ;
2 ; 3 ; 4... 2 147 483 647 -1 ; -2 ; -3... - 2 147 483 648
35

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


On remarquera quil y a moins de positifs que de ngatifs. Cela tient une
raison toute simple, cest que 0 est compt comme un positif et pas comme
un ngatif. Comme le nombre doctets ncessaires lenregistrement dun
nombre est limit (ici 31 bits pour la partie numrique et 1 bit pour le signe
soit 32 bits = 4 octets), cela implique que lon peut aller moins loin dans
les positifs que dans les ngatifs .
Si N est une variable de type Natural, elle peut prendre les valeurs suivantes : 0 ; 1 ;
2 ; 3 ... 2 147 483 647
On remarque quil ne sagit l que dune restriction du type Integer. Comme je vous
lai dit, Natural est un sous-type de Integer (not subtype en Ada). Il ny a donc pas
davantage de Natural que dInteger positifs ou nuls.
Il existe galement un type Positive, semblable aux Natural mais pour lequel
0 est exclu.

Exemple
Nous souhaiterions crer un programme qui affiche votre ge. Au dbut de notre code,
nous devrons donc crire :
1
2
3
4

Procedure VotreAge is
age : Integer := 27 ;
begin
...

Ou bien, sans affecter de valeur :


1
2
3
4

Procedure VotreAge is
age : Integer ;
begin
...

Si nous voulons pouvoir afficher des Integer ou des Natural, il faudra ajouter
Ada.Integer_Text_IO dans la listes des packages. Ce package contient une instruction
Put() prvue spcifiquement pour les nombres entiers. Attention, il existe donc deux
instructions Put() diffrentes : une pour le texte et une pour les entiers ! Une dans le
package Ada.Text_IO et une dans Ada.Integer_Text_IO.
Ralisons maintenant ledit programme et voyons le rsultat :
1
2

WITH Ada . Text_IO ;


WITH Ada . Integer_Text_IO ;

3
4
5
6
7

36

PROCEDURE VotreAge IS
age : Integer := 27 ;
BEGIN
Put ( " Vous avez " ) ;

USE Ada . Text_IO


USE Ada . Integer_Text_IO ;

DIFFRENTS TYPES
8
9
10

Put ( age ) ;
Put ( " ans . " ) ;
END VotreAge ;
Vous avez

27 ans .

Pourquoi tout cet espace avant 27 ?

Par dfaut, linstruction Ada.Integer_Text_IO.Put() (cest son nom complet) rserve


toujours la mme place pour afficher des entiers, quils soient petits ou grands. Mais il
est possible de spcifier la taille de cet emplacement. Pour cela, il y a possibilit dajouter des options notre instruction Put() (on parle plus exactement de paramtres).
Juste aprs age, il vous suffit dcrire une virgule suivie de Width => ### . Vous
remplacerez les dises par le nombre de chiffres afficher. Ce paramtre Width est un
mot anglais qui signifie Largeur . Par exemple :
1
2

WITH Ada . Text_IO ;


WITH Ada . Integer_Text_IO ;

USE Ada . Text_IO


USE Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10

PROCEDURE VotreAge IS
age : Integer := 27 ;
BEGIN
Put ( " Vous avez " ) ;
Put ( age , Width = > 0 ) ;
Put ( " ans . " ) ;
END VotreAge ;

En inscrivant Width => 0 , on indique que lon ne veut afficher aucun chiffre. Mais
le langage Ada tant bien conu, il affichera tout de mme les chiffres rellement utiles,
mais pas un de plus ! Ce qui nous donnera :
Vous avez 27 ans .

Le type Float
Dfinition
Le type float est charg de reprsenter les nombres dcimaux. Dans les faits, il nen
reprsente en fait quune partie (comme les Integer ne couvrent pas tous les nombres
entiers).
Autant Natural = Naturel ; Integer = Entier, a semblait vident, autant l :
Float = Dcimal ! ? ! Je vois mal la traduction.
37

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


Retenez quen ralit il ne sagit pas de dcimaux mais de nombre dits
virgule flottante . Nous nentrerons pas dans le dtail pour linstant, mais
lorsque lordinateur enregistre un nombre en virgule flottante , il enregistre
le signe, un nombre entier (les chiffres de notre nombre) plus un autre nombre
qui indiquera lemplacement de la virgule. Plus de dtails seront donns dans
la quatrime partie.

Valeurs possibles
Si X est une variable de type float, elle peut prendre les valeurs suivantes : 0.0 ; 1.0 ;
2.0... 3,40282E38 (un nombre avec 39 chiffres) -1.0 ; -2.0... -3,40282E38 (un
nombre avec 39 chiffres) 1.5 ; - 2.07 ; 3.141592. . .
Vous remarquerez deux choses : tout dabord 1, 2, 3. . . sont nots (et doivent
tre nots) 1.0, 2.0, 3.0. . . Ensuite, le nombre Pi nexiste pas mme sil existe
le nombre 3.141592 (une valeur approche). Le type float ne contient donc
pas les rels mais des dcimaux (indication pour les matheux. )

Exemple
Voil ce que donnerait le dbut dun programme demandant votre taille et votre poids :
1
2
3
4
5

Procedure TaillePoids is
Taille : Float := 1 . 85 ;
Poids : Float := 63 . 0 ;
begin
...

Ou bien, sans affecter de valeur et en dclarant les deux variables en mme temps :
1
2
3
4

Procedure TaillePoids is
Taille , Poids : Float ;
begin
...

Et si vous souhaitez afficher des variables de type Float, il faudra ajouter Ada.Float_Text
_IO dans la listes des packages. Une instruction Put() spcifique aux Float y est enregistre. Prenez lexemple suivant :
1
2

WITH Ada . Text_IO ;


WITH Ada . Float_Text_IO ;

3
4
5
6
7

38

Procedure TaillePoids is
Taille : Float := 1 . 85 ;
Poids : Float := 63 . 0 ;
begin

USE Ada . Text_IO ;


USE Ada . Float_Text_IO ;

DIFFRENTS TYPES
8
9
10
11
12
13
14

Put ( " Votre taille est de " ) ;


Ada . Text_IO )
Put ( Taille ) ;
Ada . Float_Text_IO )
New_line ;
Put ( " Votre poids est de " ) ;
Ada . Text_IO )
Put ( Poids ) ;
Ada . Float_Text_IO )
New_line ;
End TaillePoids ;

-- On affiche du texte ( package


-- On affiche un Float ( package
-- On affiche du texte ( package
-- On affiche un Float ( package

Ce programme est cens afficher une taille (1m85) et un poids (63kg). Mais contrairement toute attente, voici le rsultat :
Votre taille est de 1.85000 E +00
Votre poids est de 6.30000 E +01

Quest-ce que cest que ce charabia ? Cest moi de remettre les chiffres dans
lordre ?
En fait, les nombres virgule flottante sont enregistrs dans lordinateur en criture
scientifique, cest--dire sous la forme 1, 85 = 1, 85 100 et 63 = 6, 3 101 (plus
de dtail seront donns au chapitre 23). Le E majuscule remplace ici la multiplication
par une puissance de 10. Je me doute que vous prfreriez vous passez de cette criture
scientifique ainsi que de tous les 0 inutiles aprs la virgule. Pour cela, il suffit dutiliser
deux paramtres, Exp et Aft. Exp est le diminutif dexposant (ou puissance si vous
prfrez) ; il suffit dindiquer 0 pour que celui-ci ne soit pas affich. Aft est labrviation
du mot anglais After , qui signifie Aprs ; celui-ci permet de prciser le nombre
minimum de chiffres dsirs aprs la virgule. Si vous indiquez 0, Ada naffichera de
chiffres aprs la virgule que si ncessaire. Modifions donc le code prcdent et observons
le rsultat :
1
2

WITH Ada . Text_IO ;


WITH Ada . Float_Text_IO ;

USE Ada . Text_IO ;


USE Ada . Float_Text_IO ;

3
4
5
6
7
8
9
10
11
12
13
14

Procedure TaillePoids is
Taille : Float := 1 . 85 ;
Poids : Float := 63 . 0 ;
begin
Put ( " Votre taille est de " ) ;
Put ( Taille , Exp = > 0 , Aft = > 0 ) ;
New_line ;
Put ( " Votre poids est de " ) ;
Put ( Poids , Exp = > 0 , Aft = > 0 ) ;
New_line ;
End TaillePoids ;

39

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION

Votre taille est de 1.85


Votre poids est de 63

Un paramtre Fore existe galement pour Ada.Float_Text_IO.Put(), linstruction Put() pour les nombres virgule flottante. Cest le diminutif du mot
anglais Before qui signifie Avant . Ce paramtre indique la place ncessaire pour afficher les chiffres avant la virgule. Un quivalent du paramtre
Width pour les entiers, en quelques sortes.

Le type Character
Dfinition
Le type Character est rserv pour les caractres, cest--dire les lettres. Toutefois,
les espaces et les tabulations constituent aussi des caractres de mme que le retour
la ligne, la fin de fichier. . . qui sont des caractres non imprimables. Une variable de
type Character ne peut pas contenir une phrase mais un seul caractre.
Valeurs possibles
Si C est une variable du type Character, elle peut prendre les valeurs suivantes : a,
b, c, z, A, B, C, Z, #, , %. . .
Les lettres doivent tre entoures par deux apostrophes, lespace y-compris.
De plus, a et A sont deux types Character diffrents.

Les lettres accentues (, , . . .) ne sont pas prises en charge. Ce sont les


symboles anglo-saxons qui priment par dfaut !
En ralit, les characters ont les valeurs suivantes : 0, 1, 2. . . 255. Les ordinateurs ne
grent pas les lettres, mais les chiffres oui ! chacun de ces nombres est associ un
caractre. Dans certains langages, il est donc possible dcrire a+1 pour obtenir b.
Mais le langage Ada, qui a un typage fort (certains diront excessif), empche ce genre
dcriture : une lettre plus un nombre, cest une aberration en Ada. Ne vous inquitez
pas, nous verrons plus tard les oprations possibles sur les Character.
Exemple
Si nous voulons pouvoir utiliser les Character, il suffit davoir crit Ada.Text_IO dans
la listes des packages :
40

AFFECTATION
1
2

With Ada . Text_IO ;


Use Ada . Text_IO ;

Et voil pour le dbut dun programme qui demanderait votre initiale :


1
2
3
4

Procedure Initiales is
Ini : Character := 'K ' ;
begin
...

-- Comme Kaji9 : -)

Affectation
Maintenant que lon sait dclarer les principaux types de variables, voyons comment
leur attribuer une valeur (on dit quon leur affecte une valeur). En effet, nous avons
rquisitionn une zone de la mmoire de notre ordinateur mais il ny a encore rien
dcrit dedans. Pour cela, revenons notre programme VotreAge :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7

Procedure VotreAge is
Age : integer ;
Begin
...

Il y a deux personnes qui peuvent affecter une valeur la variable Age : vous (le
programmeur) ou lutilisateur (peut-tre vous aussi, mais pas dans le mme rle).

Affectation par le programmeur (ou le programme)


On va utiliser le symbole := vu auparavant, mais cette fois, aprs le Begin.
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8

Procedure VotreAge is
Age : integer ;
-- On d clare notre variable Age , mais
elle n ' a aucune valeur
Begin
Age := 27 ;
-- On affecte une valeur la variable Age
End VotreAge ;

Ce programme se contentera daffecter 27 une variable Age.

Affectation par lutilisateur


Nous savons comment poser une question lutilisateur avec linstruction Put(). Pour
rcuprer sa rponse, on utilisera linstruction Get().
41

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10
11

Procedure VotreAge is
Age : integer ;
Begin
Put ( " Quel age avez - vous ? " ) ;
demande l ' ge .
Get ( Age ) ;
ponse de l ' utilisateur
Skip_line ;
Put ( " Ah , vous avez " ) ; Put ( Age ) ;
affichant la r ponse
End VotreAge ;

-- on est poli , on
-- Puis , on saisit la r
-- ???
-- on fait une phrase

Cest pas trop compliqu. Sauf que, cest quoi ce Skip_line ?

Linstruction Skip_line
Skip_line est une instruction que je vous conseille fortement dcrire aprs chaque
utilisation de linstruction Get(). Pour comprendre son utilit, reprenons lexemple du
programme TaillePoids :
1
2

with Ada . Text_IO , Ada . Float_Text_IO ;


use Ada . Text_IO , Ada . Float_Text_IO ;

3
4
5
6
7
8
9
10
11

Procedure TaillePoids is
Taille , Poids : float ;
Begin
Put ( " Quelle est votre taille ? " ) ;
Get ( Taille ) ;
Put ( " Vous mesurez " ) ;
Put ( Taille ) ;
Put ( " m . " ) ;

12
13
14

Put ( " Quel est votre poids ? " ) ;


Get ( Poids ) ;

15
16
17
18

Put ( " Vous pesez " ) ;


Put ( Poids ) ;
Put ( " kg . " ) ;

19
20

End TaillePoids ;

Puis voyons ce quil se passe dans la console.


42

AFFECTATION

Quelle est votre taille ? 1.8 R


Vous mesurez 1.8 m .
Quel est votre poids ?
raised ADA . IO_EXCEPTIONS . DATA_ERROR : a - tiinio . adb :89
instantiated at a - inteio . ads :18

Notre utilisateur a tap 1.8R au lieu de 1.85 (erreur de frappe). Logiquement, le programme plante. Mais regardons en dtail ce quil sest pass. La taille enregistre est
1.8, le R na pas t pris en compte. Alors pourquoi le programme ne nous laisse-t-il
pas entrer une nouvelle taille ? Pourquoi dtecte-t-il une erreur alors que nous navons
entr aucun poids ? Et qui plus est : pourquoi ne la dtecte-t-il pas plus tt ?
En fait, en tapant 1.8R nous avons envoy dans le buffer (la mmoire tampon) 1.8 puis
R puis le Entre (qui constitue galement un caractre). Linstruction Get(Taille)
sest empare de 1.8, mais le R est rest en mmoire tampon puisqu lvidence, il
ne faisait pas partie du nombre demand. Par consquent, linstruction Get(Poids) a
regard dans le buffer ce quil y avait rcuprer : la lettre R que vous aviez tap par
erreur ! Linstruction Get(Poids) sest donc empare du R qui est un character et non
un Float. Do lerreur.
Et Skip_line dans tout a ?

Et bien linstruction Skip_line aurait vit cette erreur, car Get(Taille) aurait saisit
1.8 pour la taille (comme tout lheure) et Skip_line aurait ensuite vid la mmoire
tampon : plus de R ou de touche "entre" en mmoire. Nous aurions ainsi pu saisir
notre poids tranquillement.
Attention ! Skip_line nest pas fait pour grer des erreurs. Il vide seulement
la mmoire tampon (appele buffer), mais pensez lcrire aprs chaque
instruction Get() !
Voici le code, corrig avec Skip_line :
1
2

with Ada . Text_IO , Ada . Float_Text_IO ;


use Ada . Text_IO , Ada . Float_Text_IO ;

3
4
5
6
7
8
9
10
11

Procedure TaillePoids is
Taille , Poids : float ;
Begin
Put ( " Quelle est votre taille ? " ) ;
Get ( Taille ) ; Skip_line ;
Put ( " Vous mesurez " ) ;
Put ( Taille ) ;
Put ( " m . " ) ;

12
13

Put ( " Quel est votre poids ? " ) ;

43

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


14
15
16
17

Get ( Poids ) ; Skip_line ;


Put ( " Vous pesez " ) ;
Put ( Poids ) ;
Put ( " kg . " ) ;

18
19

End TaillePoids ;

Complments
Cette dernire partie est plus technique. Si vous voulez viter les confusions, je vous
conseille de relire ce qui prcde et de faire des exemples (cest en forgeant que lon
devient forgeron ). Toutefois, je vous invite revenir lire ce qui suit, aussitt que vous
matriserez les notions de types car les constantes et les attributs (surtout les attributs)
nous serons forts utiles par la suite.

Constantes
Une variable peut. . . varier. tonnant non ? Ce nest pas parce que vous lavez initialise
15 quelle ne peut pas tre modifie plus tard pour valoir, par exemple, 18, bien au
contraire. Cest dailleurs l tout leur intrt.
1
2
3

N := 10 ;
N := 13 ;
N := 102 ;

-- la variable N prend la valeur 10


-- Puis la valeur 13
-- Et enfin la valeur 102 !

Mais il est parfois intressant de fixer leur valeur une fois pour toute. La variable ne
varie alors plus, elle devient une constante et doit tre dclare de la manire suivante :
1

MaVariable : Constant Integer := 15 ;

Nous avons ainsi une variable MaVariable qui vaudra toujours 15. Tentez seulement de
modifier cette valeur et vous entranerez un plantage de votre programme (ou plutt,
votre compilateur refusera de compiler votre code).
Mais quoi servent les constantes ?

Quelques exemples simples. Une variable peut contenir le titre de votre nouveau jeu
ou les dialogues des personnages. Mieux vaudrait que le compilateur vous prvienne si
ces variables sont malencontreusement modifies. Autre exemple :
1

PI : Constant Float := 3 . 1415926535 ;

Chacun comprend que si la valeur de ma variable PI est modifie, tous mes calculs
daire de disque, de primtre de cercle ou de volume de boule. . . seront faux. Donc
PAS TOUCHE MA CONSTANTE ! ! !
44

COMPLMENTS

Attributs
Comment obtenir le plus petit et le plus grand Integer ? Comment savoir quel est le
93me character ? Grce aux attributs, bien sr. Ce sont des sortes dinstructions qui
sappliquent aux types.
Avant den voir quelques uns, voici un petit rappel de vos cours danglais : Repeat
after me. It is Susans dog signifie le chien de Susan . Ainsi, le petit mot " s"
exprime lide dappartenance et il faut penser inverser lordre des noms par rapport
au franais. Pourquoi ce rappel ? Eh bien parce que les attributs sutilisent de la mme
manire :
1
2
3
4

N := integer ' first ;


M := integer ' last ;
C := character ' val ( 93 ) ;
character
P := character ' pos ( 'f ') ;
character 'f '

--N sera le premier des integer


--M sera le dernier des integer
--C sera la 93 me valeur des
--P aura pour valeur la position du

N, M et P doivent tre dclars comme des Integer, alors que C est un


character !
Nous reverrons plus abondamment les attributs dans les chapitres suivants. Commencez
dj par vous faire la main sur ces quatre l : quel est le plus petit float ? Le plus grand ?
Quelle est la position de a et du A ?

Bloc de dclaration
Lorsque vous dclarez une variable ou une constante, cela se fait toujours au sein de la
zone rserve aux dclarations : entre les mots cls IS et BEGIN. Comme nous lavons
dit, cest ce moment que votre programme va demander lordinateur sil peut lui
emprunter de la mmoire.
Mais quand notre programme rend-il la mmoire emprunte ?

La mmoire est libre une fois le programme rendu linstruction END Bidule ;
(o Bidule est bien sr le nom de votre programme). Et jusqu ce moment l, deux
variables ne pourront porter le mme nom.
Comment je fais si jai besoin dune variable supplmentaire en cour de route ?
Et si une variable ne me sert plus rien au bout de 2 ou 3 ligne ?
La rgle de base reste la mme : il faut tout dclarer ds le dpart et la mmoire
45

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION


rquisitionne ne sera rendue qu la fin du programme. Cest une obligation en Ada
et vous verrez, si vous vous essayez dautres langages, que cest une bonne pratique
qui structure clairement votre code et vite bon nombre de problmes. Toutefois, il
peut arriver (nous nous en rendrons compte avec le chapitre 11) que nous ne puissions
dclarer une variable ds le dpart. Ou bien, une variable peut tout fait navoir quun
rle minime jouer et il est alors intelligent de la dtruire une fois inutile afin de dgager
de la mmoire. Ada a prvu cette ventualit en proposant des blocs de dclaration.
Pour ouvrir un bloc de dclaration, vous devez utiliser le mot cl DECLARE. Vous pourrez ensuite dclarer vos variables supplmentaires. Une fois les dclarations termines,
vous indiquerez la reprise dactivit par le mot BEGIN. Les variables supplmentaires
seront finalement dtruites par lajout dun nouveau END. Un exemple pour mieux
comprendre :
1
2

PROCEDURE Mon_Programme IS
BEGIN

3
4

DECLARE

5
6
7
8
9

M a _ V a r i a b l e _ S u p p l e m e n t a i r e : Integer ;
BEGIN
-- Instructions quelconques faisant intervenir
Ma_Variable_Supplementaire
END ; -- Fin du bloc de d claration

10
11

END Mon_Programme ;

Pour plus de clart, il est galement possible de nommer ce bloc de dclaration de la


mme faon que lon dclare une variable :
1
2

PROCEDURE Mon_Programme IS
BEGIN

3
4
5
6
7
8

Bloc_Declaration : DECLARE
M a _ V a r i a b l e _ S u p p l e m e n t a i r e : Integer ;
BEGIN
-- Instructions quelconques faisant intervenir
Ma_Variable_Supplementaire
END Bloc_Declaration ; -- Fin du bloc de d claration

9
10

END Mon_Programme ;

La variable supplmentaire est ainsi cre la ligne 5 et pourra tre utilise entre les
lignes 6 et 8, mais pas au-del. Elle sera dtruite ds que le programme arrivera
linstruction END Bloc_Declaration ; . Il est donc interdit que la variable supplmentaire porte le mme nom que les variables de dpart, mais il est possible de crer
deux variables de mme nom si elles sont des blocs de dclaration distincts :
1
2
3

46

PROCEDURE Mon_Programme IS
BEGIN

COMPLMENTS
4
5
6
7
8

Bloc_Numero1 : DECLARE
X : Float ;
BEGIN
-- Instructions quelconques faisant intervenir X
END Bloc_Numero1 ;

9
10
11
12
13
14

Bloc_Numero2 : DECLARE
X : Float ;
BEGIN
-- Instructions quelconques faisant intervenir X
END Bloc_Numero2 ;

15
16

END Mon_Programme ;

On comprend sur lexemple prcdent que la premire variable X, cre la ligne 5,


meurt la ligne 8. Il est donc possible den crer une nouvelle la ligne 11 et portant
le mme nom. Si le bloc de dclaration ne vous sera pas utile pour linstant (on peut
souvent sen passer en organisant mieux son code), il nous servira tout de mme trs
bientt.
Nous savons maintenant dclarer une variable et lui affecter une valeur. Nous avons vu
galement quelques types de variables. Nous serons amens par la suite voir dautres
types plus complexes (tableaux, chanes de caractres, classes, pointeurs. . .). Avant
cela, nous devrons voir un second chapitre sur les variables. Cest en effet intressant
de les avoir enregistres en mmoire, mais il serait bien plus intressant de pouvoir les
modifier laide de quelques formules mathmatiques simples.

En rsum
Pour enregistrer vos rsultats, vous devez dclarer des variables qui correspondent
des emplacements en mmoire. La dclaration se fait entre les mots cls IS et
BEGIN.
Toute variable dclare doit avoir un type bien prcis. Nous avons vu 3 grandes familles de types : les entiers (Integer ou Natural), les flottants qui correspondent
aux nombres dcimaux (Float) et les caractres (Character).
Lassignation dune valeur une variable peut se faire de deux faons : soit en
laissant le soin lutilisateur de choisir soit en codant cette valeur en utilisant
lopration daffectation ( := ). Les deux mthodes ne sont pas exclusives et
vous pouvez bien sr mixez les deux.
Si certaines variables ne devraient jamais tre modifies, dclarez-les plutt comme
des constantes avec le terme CONSTANT. En cas derreur de votre part, le compilateur vous lindiquera, vitant ainsi le plantage de votre programme.

47

CHAPITRE 4. VARIABLES I : TYPAGE ET AFFECTATION

48

Chapitre

Variables II : Oprations
Difficult :
Nous allons, dans ce chapitre, aborder les oprations que lon peut effectuer sur les diffrents
types de variables : Integer, Natural, Float et Character. En effet, il ne suffit pas de crer
des variables et de leur affecter une valeur ; encore faut-il quelles servent quelque chose,
que lon effectue quelques oprations avec elles sans quoi, le compilateur GNAT risque de
nous renvoyer le message suivant :
Warning : variable Machin is never read and never assigned
Autrement dit, votre variable ne sert rien !
Un dernier rappel : toutes les oprations daffectation devront tre crites, je le rappelle,
entre BEGIN et END.

49

CHAPITRE 5. VARIABLES II : OPRATIONS

Oprations sur les Integer et les Natural


Voici quelques oprations de base.
Symbole
+
*
/
mod
**

Opration
Addition
Soustraction
Multiplication
Division
euclidienne
(sans
nombre virgule)
Modulo ("reste" de la division
euclidienne)
Puissance

Exemple
N := 3 + 4
N := 5 - 4
N := 3 * 4
N := 7 / 2
3.5)
N := 7 mod

(rsultat 7)
(rsultat 1)
(rsultat 12)
(rsultat 3 et pas
2 (rsultat 1)

N := 5**2 (rsultat 25 car 5*5


= 25)

Je pense que les exemples abords prcdemment parlent deux-mmes, mais ils sont
trs faciles. Voici quelques exemples un peu plus complexes :
1
2

Get ( N ) ; skip_line ; -- on saisit deux variables de type Integer


Get ( M ) ; skip_line ;

3
4
5

Resultat := N + M + 1 ; -- La variable Resultat prend la valeur


r sultant
-- de l ' addition des deux variables N et
M

Au lieu deffectuer un calcul avec des valeurs connues, il est possible de faire des calculs
avec des variables dont on ne connait pas, a priori, leur valeur.
Pour que votre programme soit compil, il faut ABSOLUMENT que Resultat,
N et M soient tous des Integer ! Il est toutefois possible de mlanger des
Integer et des Natural dans un mme calcul car les Natural sont un sous-type
des Integer.
Voici une autre utilisation possible de ces oprations :
1
2

N := 5 + 5 * 2 ;
M := 15 - ( 3 + 2 ) ;

Combien vaudront N e M selon vous ? Si vous me rpondez 20 et 14 alors je vais devoir


vous rappeler les rgles de priorit en Mathmatiques. Ada respecte ces priorits. Dans
le premier cas, Ada commence par effectuer la multiplication avant dadditionner : N
vaut donc 15. Dans le second cas, les parenthses ont la priorit donc M vaut 10. Un
dernier exemple :
1
2

50

Get ( Var ) ; skip_line ; -- on saisit une variable de type integer


Var := Var + 1 ;

OPRATIONS SUR LES FLOAT

Comment ? Var est gal Var + 1 ? ! ?

NON ! Le symbole nest pas = mais := ! Ce nest pas un symbole dgalit mais daffectation ! Supposons que Var soit gal 5. Lordinateur commencera par calculer Var + 1
qui vaudra 6. Le code ci-dessus affectera donc 6 la variable Var, qui valait auparavant
5. On augmente donc la valeur de la variable dune unit, cest lincrmentation. Ne
vous avais-je pas dit que les variables variaient ?

Oprations sur les float


Oprations lmentaires
Voici quelques oprations sur les float.
Symbole
+

Opration
Addition

Soustraction

Multiplication

Division dcimale

**

Puissance

ABS

Valeur absolue

Exemple
X := 3.0 + 4.1 (rsultat
7.1)
X := 5.8 - 4.3 (rsultat
1.5)
X := 3.5 * 4.0 (rsultat
14)
X := 7.0 / 2.0 (rsultat
3.5)
X := 36.0**0.5 (rsultat
6, la puissance 0.5 quivaut la racine carre)
X := ABS(-8.7) (rsultat
8.7)

Globalement, on retrouve les mmes oprations quavec les Integer et les Natural.
Attention toutefois ne pas additionner, soustraire ou multiplier. . . un Integer et un
Float. Si le symbole de laddition est le mme pour les deux types, il sagit dans les
faits de deux instructions distinctes : lune pour les Integer, lautre pour les Float.

Ne faites pas derreur de casting

Mais comment je fais si jai besoin dadditionner un Float et un Integer ?

Cest trs simple, vous devrez effectuer des conversions (aussi appeles cast). Les
51

CHAPITRE 5. VARIABLES II : OPRATIONS


conversions de types se font trs simplement en Ada : il suffit dencadrer le nombre ou
le calcul convertir avec des parenthses et dcrire le type dsir devant ces mmes
parenthses. Voici un exemple :
1
2
3
4
5
6
7
8
9

Procedure addition is
M : integer := 5 ;
X : float := 4 . 5 ;
Res_int : integer ;
Res_float : float ;
Begin
Res_int := M + Integer ( X ) ;
Res_float := Float ( M ) + X ;
End addition ;

Put ( Res_int ) ;
Put ( Res_float ) ;

Linstruction Integer(X) permet dobtenir la valeur entire de la variable X. Comme


X vaut 4.5, Integer(X) donnera comme rsultat 4. Linstruction Float(M) permet
dobtenir lcriture dcimale de la variable M. Comme M vaut 5, Float(M) donnera
comme rsultat 5.0.

Oprations mathmatiques
Jaurais besoin de faire des calculs plus pousss, existe-t-il des oprations
comme le sinus ou le logarithme ?
Par dfaut, ces oprations nexistent pas dans le langage Ada. Mais il existe des packages vous permettant tout de mme de les utiliser. Ainsi, si au tout dbut de votre
programme vous ajoutez le package Ada.Numerics, vous aurez alors la possibilit dutiliser les constantes et e (e tant la constante de Neper). Exemple :
1
2

P := 2 * Pi * 10 ;
rayon10
put ( e ** 2 ) ;

-- Calcule le p rim tre d ' un cercle de


-- Calcule et affiche e au carr

Mais le package Ada.Numerics.Elementary_Functions vous sera plus utile encore car


il contient les fonctions trigonomtriques et logarithmiques essentielles. Vous trouverez
entre autres :
A ces oprations sajoutent galement les fonctions hyperboliques : le cosinus hyperbolique (not cosh), le sinus hyperbolique (not sinh), la tangente hyperbolique (not
tanh) et leurs rciproques, larccosinus hyperbolique (not arccosh), larcsinus hyperbolique (not arcsinh) et larctangente hyperbolique (not arctanh). Celles-ci sutilisent
de la mme manire que leurs homologues trigonomtriques.
52

OPRATIONS SUR LES CHARACTER


Symbole
Sqrt( )

Opration
Racine carre

Log( )
Exp( )

Logarithme nprien
Exponentielle

Cos( )
Sin( )
Tan( )
Arccos( )

Cosinus
Sinus
Tangente
Arc Cosinus

Arcsin( )

Arc Sinus

Arctan( )

Arc Tangente

Exemple
X := Sqrt(36.0) (rsultat 6.0
car 6 6 = 36)
X := Log(e) (rsultat 1.0)
X := Exp(1.0) (rsultat environ
2.71828 soit la constante de Neper)
X := cos(pi) (rsultat -1.0)
X := sin(pi) (rsultat 0.0)
X := tan(pi) (rsultat 0.0)
X := arccos(1.0)
(rsultat
0.0)
X := arcsin(0.0)
(rsultat
0.0)
X := arctan(0.0)
(rsultat
0.0)

Oprations sur les character


Nous avons dj parl dans la partie prcdente des attributs. Nous aurons encore
besoin deux dans cette partie pour effectuer des oprations sur les character. Voici
un tableau rcapitulatif de quelques attributs applicables au type character (noubliez
pas dcrire lapostrophe !) :
Attribut
first
last
pos(#)

val(#)

succ(#)
pred(#)

Explication
Renvoie le premier de tous les caractres.
Renvoie le dernier de tous les caractres.
Renvoie la position du caractre
remplaant #. Attention, le rsultat est un Integer.
Renvoie le #me caractre. Attention, le symbole # doit tre
remplac par un Integer
Renvoie le character suivant
Renvoie le character prcdent

Exemple
c:=characterfirst ;
c:=characterlast ;
n := characterpos(z) ;

c:=characterval(165) ;

c:=charactersucc(c) ;
c:=characterpred(c) ;

Nous avons vu au chapitre prcdent que certains langages autorisaient lcriture du


calcul a + 1 pour obtenir le character b. Pratique et rapide ! Seul souci, on mlange deux types distincts : Character et Integer. Ce genre dcriture est donc prohibe
en Ada. On utilisera donc les attributs de la manire suivante :
1

character ' val ( character ' pos ( 'a ') + 2 ) ;

53

CHAPITRE 5. VARIABLES II : OPRATIONS


Explication : Ada suit le principe de la priorit des parenthses donc les calculs seront
effectus dans lordre suivant :
characterpos(a) va renvoyer le numro de a, sa position dans la liste des
characters disponibles.
Puis on effectue laddition (Position de a + 2). Ce qui renvoie un nombre entier
correspondant la position du c.
Enfin, characterval() transformera le rsultat obtenu en character : on devrait
obtenir ainsi le character c !
Qui plus est, cette opration aurait pu tre faite laide de lattribut succ :
1

character ' succ ( character ' succ ( 'a ') ) ;

Si ces critures vous semblent compliques, rassurez-vous, nous ne les utiliserons pas
tout de suite et nous les reverrons plus en dtail dans le chapitre sur les fonctions.

Exercices
Pour mettre tout de suite en pratique ce que nous venons de voir, voici quelques
exercices dapplication.

Exercice 1
nonc
Rdigez un programme appel Multiple qui demande lutilisateur de saisir un nombre
entier et lui retourne son double, son triple et son quintuple. Jajoute une difficult :
vous naurez le droit qu deux multiplications. Pas une de plus !
Solution
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO ;


USE Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8
9

PROCEDURE Multiple IS
N : Integer ;
Double , Triple : Integer ;
BEGIN
Put ( " Saisissez un nombre entier : " ) ;
Get ( N ) ; Skip_Line ;

10
11
12
13

54

Double := 2 * N ;
Triple := 3 * N ;

EXERCICES
14
15
16

Put ( " Le double de " ) ; Put ( N ) ; Put ( " est " ) ; Put ( Double ) ;
New_Line ;
Put ( " Le triple de " ) ; Put ( N ) ; Put ( " est " ) ; Put ( Triple ) ;
New_Line ;
Put ( " Le quintuple de " ) ; Put ( N ) ; Put ( " est " ) ; Put ( Double
+ Triple ) ;

17
18

END Multiple ;

Exercice 2
nonc
Rdigez un programme appel Euclide qui demande deux nombres entiers lutilisateur
puis renvoie le quotient et le reste de leur division euclidienne (cest--dire la division
entire telle que vous lavez apprise en primaire).
Solution
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO ;


USE Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10

PROCEDURE Euclide IS
N , M : Integer ;
BEGIN
Put ( " Saisissez le dividende : " ) ;
Get ( N ) ; Skip_Line ;
Put ( " Saisissez le diviseur : " ) ;
Get ( M ) ; Skip_Line ;

11
12
13
14
15

Put ( " La division de " ) ; Put ( N ) ;


Put ( " par " ) ; Put ( M ) ;
Put ( " a pour quotient " ) ; Put ( N / M ) ;
Put ( " et pour reste " ) ; Put ( N mod M ) ;

16
17

END Euclide ;

Exercice 3
nonc
Rdigez un programme appel Cercle qui demande lutilisateur de saisir la longueur
dun rayon dun cercle et qui affichera le primtre et laire de ce cercle. Contrainte :
le rayon du cercle sera un nombre entier !
Le primtre du cercle se calcule en multipliant le rayon par 2 et par Pi. Laire
du disque se calcule en levant le rayon au carr et en le multipliant par Pi.
Pi vaut environ 3,1415926535.
55

CHAPITRE 5. VARIABLES II : OPRATIONS


Solution
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO , Ada . Float_Text_IO ;


USE Ada . Text_IO , Ada . Integer_Text_IO , Ada . Float_Text_IO ;

3
4
5
6
7
8
9

PROCEDURE Cercle IS
R : Integer ;
PI : constant float := 3 . 1415926 ;
BEGIN
Put ( " Saisissez le rayon du cercle : " ) ;
Get ( R ) ; Skip_Line ;

10
11
12
13

Put ( " Un cercle de rayon " ) ; Put ( R ) ;


Put ( " a pour perimetre " ) ; Put ( 2 . 0 * PI * float ( R ) ) ;
Put ( " et pour aire " ) ; Put ( float ( R ** 2 ) * Pi ) ;

14
15

END Cercle ;

Exercice 4
nonc
Rdigez un programme appel Lettres qui demande lutilisateur de saisir deux lettres
minuscules et qui affichera leur majuscule ainsi que la lettre se trouvant au milieu
des deux. dfaut, sil y a deux lettres, le programme choisira la plus petite .
Pour obtenir la lettre majuscule, il suffit de lui soustraire 32 (avec les
prcautions dusage bien sr).

Solution
1
2

WITH Ada . Text_IO ;


USE Ada . Text_IO ;

3
4
5

PROCEDURE Lettres IS
C1 , C2 , C3 : character ;

6
7
8
9
10

BEGIN
Put ( " Saisissez une premiere lettre minuscule : " ) ;
Get ( C1 ) ; Skip_Line ;
Put ( " Sa majuscule est " ) ; Put ( Character ' Val ( Character ' Pos (
C1 ) - 32 ) ) ; New_Line ;

11
12
13
14

56

Put ( " Saisissez une deuxieme lettre minuscule : " ) ;


Get ( C2 ) ; Skip_Line ;
Put ( " Sa majuscule est " ) ; Put ( Character ' Val ( Character ' Pos (
C2 ) - 32 ) ) ; New_Line ;

EXERCICES
15
16

C3 := character ' val (( character ' pos ( C1 ) + character ' pos ( C2 ) ) /


2) ;

17
18

Put ( " La lettre du milieu est " ) ; Put ( C3 ) ;

19
20

END Lettres ;

Nous voil dsormais arms pour effectuer des calculs, simples ou complexes, sur les
diffrents types de variables connus pour lheure. Nous aurons loccasion de les rutiliser
par la suite et de comprendre toute leur utilit, notamment pour lopration MOD, qui
doit pour linstant vous sembler bien inutile (et pourtant elle sera plus utile que vous
ne le pensez).Le prochain chapitre devrait nous permettre dapporter plus de varit
encore nos programmes en proposant des choix.

En rsum
Les quatre oprations de base sont accessibles sur le pav numrique de votre
clavier : +, -, * et /.
Ada respecte les priorits mathmatiques et notamment les parenthses.
Ada est un langage fort typage , ce qui implique limpossibilit de mlanger
des types distincts comme additionner un Integer et un Float. Toutefois, des
conversions sont possibles.

57

CHAPITRE 5. VARIABLES II : OPRATIONS

58

Chapitre

Les conditions I
Difficult :
Lobjectif premier dun programme informatique est dautomatiser des tches (souvent rptitives) mais noublions pas quil va sadresser des utilisateurs humains qui auront envie
ou besoin deffectuer des choix que votre programme ne peut anticiper. Nos programmes
doivent donc en tenir compte et grer ces choix : SI lutilisateur fait tel choix ALORS
faire ceci SINON faire cela .
Nous profiterons galement de ce chapitre pour voir diffrents oprateurs permettant de
comparer nos variables. Le programme tant suffisamment charg, ce chapitre est scind en
deux parties pour plus de clart. Dans la seconde partie nous aborderons lalgbre boolenne,
terme effrayant mais qui recouvre des rgles de logique qui nous permettront de grer des
conditions plus complexes.

59

CHAPITRE 6. LES CONDITIONS I

Conditions simples avec IF


Un dbut en douceur
Nous allons raliser ici, un programme qui demande son utilisateur sil dispose de
plusieurs ordinateurs. Cette question nappellera que deux choix possibles : Oui ou
Non. Le programme renverra un message lcran qui dpendra de la rponse obtenue.
Ce programme peut se dcomposer de la manire suivante :
1
2
3
4

AFFICHER la question : " Avez - vous plusieurs ordinateurs ? "


SAISIR la r ponse ( Oui ou Non )
SI la r ponse est oui
ALORS AFFICHER : " Vous avez bien de la chance . "

Nous enregistrerons la rponse dans une variable Reponse qui sera de type Character.
Lutilisateur devra taper "o", pour oui, et "n", pour non. Linstruction SI se note IF
en Ada et linstruction ALORS se note quant elle THEN. Do le code suivant :
1
2
3
4
5
6
7
8
9

PROCEDURE Questionnaire IS
Reponse : Character := 'n ' ; -- on d finit Reponse et on lui
attribue
-- par d faut la valeur n ( pour
non )
BEGIN
Put ( " Avez - vous plusieurs ordinateurs ? ( o / n ) " ) ;
Get ( Reponse ) ; Skip_line ;
-- On saisit la r ponse et
on vide la m moire tampon
IF Reponse = 'o '
-- SI Reponse est gal o
THEN Put ( " Vous avez bien de la chance . " ) ; -- ALORS on
affiche la phrase .
END IF ;

10
11

END Questionnaire ;

Mais pourquoi tu as crit = et non pas := comme avant ?

Tout simplement parce que le symbole := est utilis pour affecter une valeur une
variable. Si nous avions crit Reponse := o ; , alors nous aurions modifier la
valeur de Reponse en lui attribuant la valeur o. Or, ici nous posons une question au
programme : Reponse est-il bien gal o ? Le programme se chargera de rpondre
vrai ou faux. Cest pourquoi, nous utiliserons ici le symbole =. Dailleurs, aprs une
instruction IF, le compilateur refuserait que vous utilisiez le symbole :=.
Autres remarques, linstruction IF doit tre clture par une instruction END IF qui
marquera la fin des actions excuter. Et sil ne faut pas de ; la fin de linstruction
IF, il en faut bien aprs END IF !
60

CONDITIONS MULTIPLES AVEC IF


Mais ce programme prsente de grosses lacunes. Que se passe-t-il si lutilisateur rpond
non (n) ? Eh bien pour linstant, cest trs simple : rien.

Une premire alternative


Nous allons donc complter notre code de la manire suivante :
1
2
3
4
5

AFFICHER la question : " Avez - vous plusieurs ordinateurs ? "


SAISIR la r ponse ( Oui ou Non )
SI la r ponse est oui
ALORS AFFICHER : " Vous avez bien de la chance . "
SINON AFFICHER : " Ha ... Dommage . "

Linstruction SINON se traduit par ELSE et nous permettra de varier un peu notre
programme. Au lieu davoir le code suivant :
1
2
3

if Reponse = 'o '


then Put ( " Vous avez bien de la chance . " ) ;
end if ;

Nous allons donc crire :


1
2
3
4

if Reponse = 'o '


then Put ( " Vous avez bien de la chance . " ) ;
else Put ( " Ha ... Dommage . " ) ;
end if ;

Dsormais, si lutilisateur rpond non (n), il recevra tout de mme une rponse, diffrente qui plus est !
Et que se passerait-il si lutilisateur tapait une autre lettre, comme z par
exemple ?
Pour avoir la rponse reprenons notre code ! Celui-ci ne teste quune seule galit :
la variable Reponse est-elle gale o ? Donc si Reponse vaut z, le programme
apportera la mme rponse que si elle vaut n, a, @ ou autre ! Dit diffremment,
nous navons pas envisag tous les cas. Lutilisateur peut tout fait rpondre autre
chose que oui ou non. Et l, vous vous demandez sil nexisterait pas une troisime
instruction aprs THEN et ELSE. Eh bien non. Pour envisager davantage de cas, il va
falloir ruser.

Conditions multiples avec IF


Lastuce (pas si complique dailleurs) est de raliser plusieurs tests :
1
2

SI la r ponse est oui


ALORS AFFICHER ( " Vous avez bien de la chance " )

61

CHAPITRE 6. LES CONDITIONS I


3
4
5

SINON SI la r ponse est non


ALORS AFFICHER ( " Ah ... Dommage . " )
SINON Afficher ( "C ' est pas une r ponse a ! " )

Il est en effet possible dimbriquer plusieurs instructions IF les unes dans les autres.
1
2
3
4
5
6
7

if Reponse = 'o '


then Put ( " Vous avez bien de la chance " ) ; -- cas o la r ponse
est oui
else if Reponse = 'n '
then Put ( " Ah ... Dommage . " ) ; -- cas o la r ponse est non
else Put ( "C ' est pas une r ponse a ! " ) ; -- cas o la r
ponse est ... autre chose
end if ;
end if ;

Et voil, le tour est jou ! Allez, plus compliqu maintenant. Si lutilisateur rpond
p (pour ptt bin que oui, ptt bin que non ) on affichera le message suivant :
Reponses normandes non valides . vous de jouer !
1
2
3
4
5
6
7
8
9
10

if Reponse = 'o '


then Put ( " Vous avez bien de la chance " ) ;
else if Reponse = 'n '
then Put ( " Ah ... Dommage . " ) ;
else if Reponse = 'p '
then Put ( " Reponses normandes non valides " ) ;
else Put ( "C ' est pas une r ponse a ! " ) ;
end if ;
end if ;
end if ;

Alors vous avez russi ? Bien jou ! Regardez toutefois le code que je vous propose.
chaque nouveau THEN/ELSE, jai ajout une tabulation (ou 3 espaces avec Adagide).
Nous avons dj vu cette pratique, elle sappelle lindentation. Elle nest pas anodine
car elle vous permettra de proposer un code ar, organis et donc facilement lisible
par vous ou un tiers. Prenez cette habitude le plus tt possible ! Cest un conseil que
je ne cesserai de vous rpter.
Toutefois, vous avez d vous rendre compte que cela devient vite fatiguant dcrire
plusieurs END IF et daugmenter de plus en plus le nombre de tabulations pour avoir
un code bien indent. Heureusement les instructions ELSE IF peuvent tre remplaces
par une autre : ELSIF !
Et a change quoi ? ( part une lettre en moins)

Et bien, observez par vous-mme :


1
2

62

if Reponse = 'o '


then Put ( " Vous avez bien de la chance " ) ;

CONDITIONS MULTIPLES AVEC CASE


3
4
5
6
7
8

elsif Reponse = 'n '


then Put ( " Ah ... Dommage . " ) ;
elsif Reponse = 'p '
then Put ( " Reponses normandes non valides " ) ;
else Put ( "C ' est pas une r ponse a ! " ) ;
end if ;

Plus besoin de multiplier les END IF. Du coup, chaque instruction ELSIF se prsente
comme un prolongement du IF initial, limitant ainsi lindentation. Mais il y a une
instruction encore plus lisible et particulirement approprie pour des choix multiples :
la condition multiple avec CASE.

Conditions multiples avec CASE


Tester de nombreux cas
Cette nouvelle instruction sappelle CASE. Comme son nom lindique (si vous tes un
peu anglophone), elle permet de traiter diffrents CAS (= case). Alors pour la beaut
de lart, nous allons ajouter un cas supplmentaire. Si lutilisateur appuie sur f (pour
I dont speak French = Je ne parle pas franais pour les anglophobes), alors. . .
1
2
3
4
5
6
7

case Reponse is -- on indique que l ' on va regarder les diff


rents cas possibles pour Reponse
when 'o ' = > Put ( " Vous avez bien de la chance . " ) ;
-- si oui
when 'n ' = > Put ( " Ah ... dommage . " ) ;
-- si non
when 'p ' = > Put ( " Reponses normandes non valides " ) ;
-- si peut - tre
when 'f ' = > Put ( "J ' aurais pas du apprendre l ' allemand ... " ) ;
-- si " not French "
when others = > Put ( "C ' est pas une reponse . " ) ;
-- si autre chose
end case ; -- on termine notre instruction comme avec if !

Cette nouvelle instruction a des avantages : plus compacte, elle est donc plus claire,
plus lisible et surtout plus rapide taper lorsque les choix savrent nombreux. Elle
prsente toutefois des limites que nous verrons dans les prochaines parties.
Pour linstant regardons la structure. La portion de code ci-dessus (on parlera de bloc)
est introduite de la mme manire que la procdure de votre programme : PROCEDURE
Questionnaire IS. Elle se termine par END CASE. Les flches sont composes du signe
gal (=) suivi du signe suprieur (>). Rappelons enfin que le mot WHEN signifie
QUAND et que le mot OTHERS signifie AUTRE (nutilisez linstruction WHEN OTHERS
quaprs avoir trait tous les cas dsirs et afin de traiter tous les autres cas possibles).
Vous avez bien compris ? Alors rien ne vous empche de trouver dautres rponses
farfelues pour vous exercer. Pensez toutefois indiquer ces nouvelles rponses possibles
63

CHAPITRE 6. LES CONDITIONS I


lutilisateur. Je vous invite reprendre les parties prcdentes si vous avez encore
quelques doutes car la partie suivante sera un peu plus complique.
En effet, nous pensions avoir fini notre programme, que nous avions vus tous les choix
possibles, etc. Et bien non. Que se passera-t-il si lutilisateur tape O au lieu de o,
ou bien N au lieu de n ? Pour lordinateur, un n minuscule et un N majuscule
sont deux valeurs diffrentes. Il considrera que cest une mauvaise rponse et rpondra
donc "Cest pas une reponse" !
On va devoir crer 4 nouveaux cas pour les majuscules ?

Non, rassurez-vous. Nous allons toutefois devoir faire un peu de logique : cest ce quon
appelle lalgbre boolenne, mais nous ne laborderons pas dans ce chapitre. Si certaines
choses ne sont pas claires, je vous conseille de relire ce chapitre car le suivant savrera
plus compliqu.

Ne rien faire
Une instruction CASE doit vrifier toutes les valeurs que peut prendre la variable teste. Dans notre exemple, la variable Reponse peut prendre une trs grande quantit
de valeurs allant de larobase au symbole dollar. Pour ne pas coder tous ces tests manuellement, Ada a prvu linstruction OTHERS qui regroupe tout ce qui na pas t
test.
Mais si je ne veux rien faire de particulier pour ces autres cas ?

Le langage Ada a tout prvu. Il existe une instruction pour ne rien faire : NULL. Il
suffira ainsi dcrire WHEN OTHERS => NULL ; la fin pour rgler ce menu problme.
Linstruction NULL peut jouer dautres rles, notamment avec les pointeurs,
voir chapitre 17.

Les oprateurs de comparaison et dappartenance


Les oprateurs de comparaison
Tous nos tests jusque l se contentaient de vrifier une galit. Or, il est possible
dutiliser dautres symboles mathmatiques appels oprateurs de comparaison.
En voici un liste.
64

LES OPRATEURS DE COMPARAISON ET DAPPARTENANCE


Oprateur
=
/=
<
>
<=
>=

Signification
est gal
est diffrent de
est plus petit que
est plus grand que
est plus petit ou gal
est plus grand ou gal

Ainsi, notre programme initial peut-tre modifi de la manire suivante :


1
2
3
4
5

AFFICHER " Combien


SAISIR la r ponse
SI la r ponse est
ALORS AFFICHER
SINON AFFICHER

d ' ordinateurs avez - vous ? "


sup rieure ou gal 2
" G nial "
"C ' est toujours ca "

Do le programme suivant :
1
2
3

Procedure Questionnaire2 is
Reponse : integer := 0 ; -- on d finit la r ponse et on l '
initialise 0
begin

4
5
6
7
8
9
10

Put ( " Combien d ' ordinateurs avez - vous ? " ) ;


Get ( Reponse ) ; Skip_line ;
if Reponse >= 2
then Put ( " Genial ! " ) ;
else Put ( "C ' est toujours ca " ) ;
end if ;

11
12

end Questionnaire2 ;

noter que la question pose (Reponse est-elle suprieure ou gale 2) , aussi appele
prdicat peut tre remplace par :
1
2
3
4

if Reponse > 1
then Put ( " Genial ! " ) ;
else Put ( "C ' est toujours ca " ) ;
end if ;

Ce nouveau prdicat aura la mme conclusion. De mme, en inversant les instructions


suivantes THEN et ELSE et en crivant les codes suivants :
1
2
3
4

if Reponse <= 1
then Put ( "C ' est toujours ca " ) ;
else Put ( " Genial ! " ) ;
end if ;

ou
1

if Reponse < 2

65

CHAPITRE 6. LES CONDITIONS I


2
3
4

then Put ( "C ' est toujours ca " ) ;


else Put ( " Genial ! " ) ;
end if ;

. . . On obtiendra galement la mme chose.


Rgle de logique : le contraire de < nest pas > mais bien >= .
Inversement, le contraire de > est <= !

Ces diffrents symboles (>, <, >= et <=) ne peuvent tre utiliss lors dune
instruction CASE. Pensez-y !

Loprateur dappartenance
Je dois enfin vous rvler un dernier test possible : le test dappartenance. Nous avons
vu quil tait interdit de mlanger des variables de type Integer et de type Float, moins
deffectuer un cast (une conversion). En revanche, je vous ai dit ds le dpart que cette
interdiction ne valait pas pour les Natural et les Integer, car le type natural nest
finalement quun sous-type dInteger (un produit driv en quelque sorte). Voil qui
fait notre affaire : si nous avons une variable N de type Integer et que nous souhaitons
tester si elle est positive ou nulle, il suffira de savoir si elle fait partie du sous-type des
Natural laide du mot-cl IN et de lattribut range. Au lieu dcrire :
1
2

IF N >= 0
THEN ...

Il sera possible dcrire :


1
2

IF N IN Natural ' range


THEN ...

Petite traduction en Franais : Si N est dans lintervalle des Natural . Si N vaut


-4, ce prdicat vaudra FALSE. De mme, il est possible deffectuer un test pour des
intervalles plus restrictifs encore. Si nous souhaitons savoir si N est compris entre 13
et 32, il suffira dcrire :
1
2

IF N IN 13 .. 32
THEN ...

Attention, jai bien crit deux points entre 13 et 32 ! Et pas trois points !

66

POUR LES UTILISATEURS DE LA NORME ADA2012

Pour les utilisateurs de la norme Ada2012


Les expressions IF
Si vous disposez dun compilateur GNAT rcent, celui-ci doit alors rpondre la toute
dernire norme du langage, appele Ada2012. Si ce nest pas le cas, ce qui suit ne
vous concerne pas. Cette norme nest pas un nouveau langage mais simplement
une mise jour, une volution. Entre autres amliorations, la norme Ada2012 sest
inspire des langages de programmation dits fonctionnels pour proposer le concept
dexpression. Ces langages (comme F#, Haskell ou OCaml) sont rputs pour donner
naissance des codes trs compacts et minimalistes.
Lide des expressions est de permettre dutiliser les instructions IF au sein mme
des affectations. Imaginons que suite la rponse de lutilisateur vous souhaitiez renseigner une autre variable pour abonder votre fichier client. Appelons cette variable
Information et dclarons-la de type Integer. Si lutilisateur a un ordinateur, Information vaudra 1 et 0 sinon. Voici comment nous procderions avec le langage Ada dans
ses 83, 95 ou 2005 :
1
2
3
4

IF Reponse = 'o '


THEN Information := 1 ;
ELSE Information := 0 ;
END IF ;

Et voyez comment la norme Ada2013 nous permet de condenser notre code :


1

Information := ( IF Reponse = 'o ' THEN 1 ELSE 0 ) ;

Notez la disparition des points virgules et des deux affectations. Lexpression permet
daffecter notre variable la valeur 1 ou 0 en fonction du rsultat du test. Nous pouvons
galement utiliser cette mthode pour laffichage :
1

Put ( IF Reponse = 'o ' THEN " Vous avez bien de la chance . " ELSE
" Ah ... Dommage . " ) ;

Et, luxe suprme, il est mme possible dutiliser ELSIF, ELSE IF ou THEN IF ! Mais pour
ces deux derniers, il faudra faire usage de parenthses comme sur lexemple ci-dessous :
1
2
3

Information :=
THEN 0 ELSE
-- OU
Information :=
THEN 0 ELSE

( IF Reponse = 'o ' THEN 1 ELSIF Reponse = 'n '


2) ;
( IF Reponse = 'o ' THEN 1 ELSE ( IF Reponse = 'n '
2)) ;

Les expressions CASE


Mais cette importante mise jour de la norme Ada ne sarrte pas l. La mme possibilit est offerte avec linstruction CASE ! Voici ce que cela donnerait :
67

CHAPITRE 6. LES CONDITIONS I


1
2
3
4

Information := ( CASE Reponse IS


WHEN 'o ' = > 1
WHEN 'n ' = > 0
WHEN OTHERS = > 2 ) ;

Il est mme possible de combiner CASE et IF dans une mme expression ! Attention
toutefois ne pas vous perdre dans des tests trop compliqus : les expressions sont
faites pour condenser le code et pas pour lalourdir.
Noubliez pas galement quau final, vos tests doivent renvoyer la valeur
affecter votre variable.

Toujours plus complexe


Pour terminer, sachez que si votre variable a dj t initialise, il est mme possible
de lutiliser au sein mme de son expression. Regardez par exemple ce code :
1
2

Get ( x ) ; skip_line ;
x := ( if x >= 0 then x else -x ) ;

Lutilisateur saisit une valeur de x dont on ne connat pas le signe. La seconde ligne a
pour but de la rendre positive : si x est dj positive ou nulle, on lui affecte sa propre
valeur, sinon on lui affecte la valeur -x qui sera ncessairement positive.
Nous avons fait un premier tour des conditions en Ada. Llment le plus important est
la suite dinstruction IF/THEN/ELSE, mais noubliez pas quil existe linstruction CASE
pour simplifier votre code. Avec ces quelques instructions en tte, vos programmes
devraient dores et dj prendre un peu de profondeur.

En rsum
Linstruction IF permet de tester une galit la fois (ou une galit). Pour
effectuer plusieurs tests, vous devrez imbriquer plusieurs IF aprs des instructions
THEN ou ELSE.
Pour effectuer un grand nombre dactions diffrentes, il vaudra souvent mieux
utiliser linstruction CASE. Toutefois, elle ne permettra pas de tester des ingalits.
Une instruction CASE doit proposer une action pour toutes les valeurs possibles
de la variable teste. Si vous souhaitez effectuer la mme action pour tous les cas
inutiles , utilisez linstruction WHEN OTHERS.

68

Chapitre

Les conditions II : les boolens


Difficult :
Nous avons vu, dans le chapitre prcdent, lexistence des instructions IF/ELSIF/CASE.
Mais nous sommes tombs sur un os : nos codes ne prenaient pas en compte les majuscules.
Je vous propose donc de repartir du dbut pour simplifier votre travail de comprhesion.
Notre code se rsumera donc la version suivante.
1
2
3
4

if Reponse = 'o '


then Put ( " Vous avez bien de la chance . " ) ;
else Put ( " Ah ... Dommage . " ) ;
end if ;

Voyons maintenant comment grer les majuscules sans nous rpter.

69

CHAPITRE 7. LES CONDITIONS II : LES BOOLENS

Introduction aux boolens


Un bref historique
Lalgbre boolenne ou algbre de Boole (du nom du Mathmaticien George Boole) est
une branche des mathmatiques traitant des oprations logiques : on ne manipule plus
des nombres mais des propositions qui peuvent tre vraies ou fausses. Elle a t trs
utile en lectronique (cest grce elle que nos btes ordinateurs savent aujourdhui
additionner, multiplier. . .) et va nous servir aujourdhui en programmation.
Mais. . . euh. . . Je suis nul(le) en Maths.

Pas de problme ! Il ne sagit que de logique donc il ny aura pas de calcul (au sens
habituel du terme). De plus, nous ne ferons queffleurer ce pan des Mathmatiques.

Quest-ce quun boolen ?


Tout dabord, lorsque lon crit :
1

if Reponse = 'o '

. . ., il ny a que deux alternatives possibles : ou cette affirmation est VRAIE (Reponse


vaut bien o) ou bien elle est FAUSSE (Reponse vaut autre chose). Ces deux alternatives (VRAI/FAUX) sont les deux seules valeurs utilises en algbre boolenne. En
Ada, elles se notent TRUE (VRAI) et FALSE (FAUX). Un boolen est donc un objet qui
vaut soit VRAI, soit FAUX. En Ada, cela se dclare de la manire suivante :
1
2

A : boolean := TRUE ;
B : boolean := FALSE ;

Il est galement possible daffecter nos variables boolennes le rsultat dun prdicat.
1

A := ( Reponse = 'o ')

Hein ? La variable A prend la valeur contenue dans Reponse ou bien il prend


la valeur o ? Et puis, ctait pas sens valoir TRUE ou FALSE ?
La variable A prendra bien comme rsultat TRUE ou FALSE et srement pas o ! Tout
dpend de lgalit entre parenthses : si Reponse est bien gal o, alors la variable
A vaudra TRUE, sinon il vaudra FALSE. Il sera ensuite possible dutiliser notre boolen
A dans nos conditions de la faon suivante :
1
2
3

70

if A
then ...
end if ;

LA NGATION AVEC NOT


Cela signifie Si A est vrai alors. . . . La variable A remplacera ainsi notre galit.
Par consquent, il est gnralement judicieux de nommer les boolens laide dadjectifs qualificatifs comme Actif, Present, Majeur ou Est_Majeur . . . Linstruction IF
Majeur . . . se comprend ainsi plus aisment que IF Majeur = TRUE . . .

Les oprateurs boolen


Voici les quelques oprations boolennes que nous allons aborder :
Oprateur
Not

Traduction littrale
Non

Or

Ou

And

Et

Xor

Ou exclusif

Signification
Not A : il faut que A ne soit pas
vrai
A or B : il faut que A ou B soit
vrai
A And B : il faut que A et B
soient vrais
A xor B : il faut que A ou B
soit vrai mais pas les deux.

Ce ne sont pas les seuls oprateurs existant en algbre boolenne, mais ce sont ceux
que vous serez amens utiliser en Ada.

La ngation avec Not


Revenons notre programme. Nous voudrions inverser les deux instructions daffichage
de la manire suivante :
1
2
3
4

if Reponse = 'o '


then Put ( " Ah ... Dommage . " ) ;
else Put ( " Vous avez bien de la chance . " ) ;
end if ;

Chacun comprend que notre programme ne rpond plus correctement : si lutilisateur


a plusieurs ordinateurs, le programme lui rpond dommage ! ? Nous devons donc
changer la condition : le programme doit afficher Dommage seulement SI Reponse ne
vaut pas o ! La ngation dune instruction boolenne se fait laide de linstruction
NOT. Do le code suivant :
1
2
3
4

if Not Reponse = 'o '


-- si la r ponse
n ' est pas Oui
then Put ( " Ah ... Dommage . " ) ;
-- alors on
affiche Dommage
else Put ( " Vous avez bien de la chance . " ) ; -- sinon on
affiche le second message
end if ;

71

CHAPITRE 7. LES CONDITIONS II : LES BOOLENS


Le programme fait ainsi de nouveau ce quon lui demande. Retenez cette astuce car il
arrive souvent que lon se trompe dans la condition ou lordre des instructions entre
THEN et ELSE, notamment quand les conditions deviennent plus complexes. De plus,
vous pouvez tre amens ne tester que le cas ou une condition ne serait pas remplie,
par exemple : IF NOT Fichier_Existant . . .
Enfin, nous ne pouvons passer aux oprations suivantes sans faire un peu dalgbre
boolenne. Voici quelques rsultats basiques auxquels je vous invite rflchir :
Si A est vrai, alors Non A est faux. Si A = true alors Not A = false.
Si A est faux, alors Non A est vrai. Si A = false alors Not A = true.
Quelle que soit la valeur du boolen A, Non Non A = A (Not not A = A)

Les oprations Or et And


Linstruction OR
Nous avons vu dans la partie prcdente que le code suivant ne grait pas les majuscules :
1
2
3
4

if Reponse = 'o '


then Put ( " Vous avez bien de la chance . " ) ;
else Put ( " Ah ... Dommage . " ) ;
end if ;

Il faudrait poser la condition SI la rponse est o ou O . Cest--dire quil suffirait


que lune des deux conditions soit remplie pour que le programme affiche que vous avez
de la chance. Pour cela, on utilise linstruction OR. Pour rappel, OR signifie OU
en Franais.
1
2
3
4

if Reponse = 'o ' or Reponse = 'O '


then Put ( " Vous avez bien de la chance . " ) ;
else Put ( " Ah ... Dommage . " ) ;
end if ;

Encore un peu dalgbre boolenne : si A et B sont deux propositions (vraies ou fausses,


peu importe), linstruction A ou B ne sera vraie que dans les trois cas suivants :
si A est vrai
si B est vrai
si les deux sont vrais

Linstruction AND
Nouveau jeu ! Inversons, comme dans la partie prcdente, les instructions daffichage.
Nous devrions alors crire une ngation :
72

LOPRATION XOR (OPTIONNEL)


1
2
3
4

if not ( Reponse = 'o ' or Reponse = 'O ')


then Put ( " Ah ... Dommage . " ) ;
else Put ( " Vous avez bien de la chance . " ) ;
end if ;

Vous vous souvenez de la partie sur linstruction NOT ? Nous avons fait la mme chose :
puisque lon a invers les instructions daffichage, nous crivons une ngation dans le
prdicat. Mais cette ngation peut aussi scrire :
1
2
3
4

if not Reponse = 'o ' and not Reponse = 'O '


then Put ( " Ah ... Dommage . " ) ;
else Put ( " Vous avez bien de la chance . " ) ;
end if ;

Loprateur AND signifie ET. Il implique que les deux conditions doivent tre remplies
en mme temps : Reponse ne vaut pas o ET Reponse ne vaut pas non plus O.
Si une seule des deux conditions ntait pas remplie, lensemble serait faux. Pratiquons
encore un peu dalgbre boolenne pour mieux comprendre :
si A est VRAI et B est FAUX alors A et B est FAUX
si A est VRAI et B est VRAI alors A et B est VRAI
si A est FAUX et B est FAUX alors A et B est FAUX
De manire schmatique, retenez que la ngation dun OU revient signifier ET. Le
contraire de VRAI OU VRAI est donc FAUX ET FAUX. Bon, je crois quil est temps
darrter car certains mninges doivent commencer griller. Je vous invite donc
mditer ce que vous venez de lire.

Lopration XOR (optionnel)


Il nous reste une opration voir, lopration XOR. Nous ne nous attarderons pas dessus
car je prfrerais que vous reteniez dj ce que nous venons de voir sur NOT, OR et AND.
Toutefois, si vous voulez en savoir plus, rappelons que la phrase A ou B sera vraie
dans trois cas :
si A est vrai
si B est vrai
si A et B sont vrais tous les deux
Or, il existe des cas o il ne faut pas que les deux conditions soient vraies en mme
temps. Au lieu dcrire (A ou B) et pas (A et B) , il existe lopration OU EXCLUSIF
(XOR). Pour que A XOR B soit vrai, il faut que :
A soit vrai
ou que B soit vrai
mais pas les deux en mme temps !
73

CHAPITRE 7. LES CONDITIONS II : LES BOOLENS


Prenons un exemple. Vous rdigez un programme destination des ngociants (en
fruits et lgumes ou en matriaux de construction, peu importe). Ceux-ci ont besoin
de nouer des contacts avec diverses entreprises, quelles soient productrices ou consommatrices de biens. Votre programme a pour but de rechercher de nouveaux clients
ou fournisseurs ; il doit donc disposer de deux variables boolennes : Producteur et
Consommateur qui indique le statut dune entreprise. Il dispose galement de deux
instructions : Faire_Affaire et Laisser_Tomber.
Voici la premire ide que nous pourrions avoir (attention, elle comporte une
erreur) :

1
2
3
4

IF Producteur OR Consommateur
THEN Faire_Affaire ;
ELSE Laisser_Tomber ;
END IF ;

On ne contacte une entreprise que si elle est productrice de bien OU consommatrice.


Sauf que ce code possde une faille : si lentreprise a fait le choix de produire elle-mme
les biens quelle consomme, quel intrt avons-nous jouer les ngociants ? Il faut donc
retirer le cas o une entreprise est la fois productrice et consommatrice. Cest l que
XOR entre en scne :
1
2
3
4

IF Producteur XOR Consommateur


-- on ne retient plus les
entreprises en circuit interne
THEN Faire_Affaire ;
ELSE Laisser_Tomber ;
END IF ;

Exercice
Revenons notre problme initial : maintenant que nous avons vu les oprateurs boolens, vous pourriez reprendre votre programme nomm Questionnaire pour quil puisse
grer aussi bien les majuscules que les minuscules. Vous allez pouvoir complter le code
du prcdent chapitre mais attention, il ne doit pas y avoir de redondance ! Hors de
question dcrire plusieurs fois la mme instruction. Vous allez devoir utiliser lun des
oprateurs boolens vus dans ce chapitre. Oui, mais lequel ? quelle opration cela
correspond-il : OU, ET, NON ?
Voici une premire solution avec des ELSIF :
1
2
3
4
5
6

74

if Reponse = 'o ' or Reponse = 'O '


then Put ( " Vous avez bien de la chance . " ) ;
elsif Reponse = 'n ' or Reponse = 'N '
then Put ( " Ah ... dommage . " ) ;
elsif Reponse = 'p ' or Reponse = 'P '
then Put ( " Reponses normandes non valides " ) ;

PRIORITS BOOLENNES ET ORDRE DE TEST (SUPPLMENT)


7
8
9
10

elsif Reponse = 'f ' or Reponse = 'F '


then Put ( "J ' aurais pas du apprendre l ' allemand ... " ) ;
else Put ( "C ' est pas une reponse . " ) ;
end if ;

Comme vous avez du vous en douter, il suffit juste de complter les blocs IF/ELSIF avec
une instruction OR. Il est vident que votre variable rponse ne peut tre en majuscule
ET en minuscule en mme temps.
Voici une seconde solution avec un CASE (un poil plus dur car la solution propose
comporte une astuce) :
1
2
3
4
5
6
7

case Reponse is
when 'o ' | 'O ' = > Put ( " Vous avez bien de la chance . " ) ;
-- si oui
when 'n ' | 'N ' = > Put ( " Ah ... dommage . " ) ;
-- si non
when 'p ' | 'P ' = > Put ( " Reponses normandes non valides " ) ;
-- si peut - tre
when 'f ' | 'F ' = > Put ( "J ' aurais pas du apprendre l ' allemand
... " ) ; -- si " not French "
when others = > Put ( "C ' est pas une reponse . " ) ;
-- si autre chose
end case ;

Durant vos essais, vous avez du remarquer que lors de linstruction WHEN il ntait pas
possible de faire appel aux oprateurs
boolens.
Pour
 remplacer linstruction OR, il


suffit dutiliser le symbole | ( Alt gr + le chiffre 6 ). Ce symbole se nomme Pipe


(prononcez Pape , cest de lAnglais). Oui, je sais vous ne pouviez pas le trouver
par vous-mme.

Priorits boolennes et ordre de test (Supplment)


Cette ultime partie doit tre vue comme un complment. Elle est plutt thorique et
sadresse ceux qui matrisent ce qui prcde (ou croient le matriser ). Si vous dbutez,
jetez-y tout de mme un il pour avoir une ide du sujet, mais ne vous affolez pas si
vous ne comprenez rien ; cela ne vous empchera pas de suivre la suite de ce cours et
vous pourrez revenir dessus plus tard, lorsque vous aurez gagn en exprience.

Priorits avec les boolens


Nous avons dj voqu, dans le chapitre 5, la question des priorits dans un calcul.
Par exemple, dans lopration 10 + 5 3 le rsultat est. . .
Euh. . . 45 ?

75

CHAPITRE 7. LES CONDITIONS II : LES BOOLENS


Argh ! Non ! Le rsultat est 25 ! En effet, on dit que la multiplication est prioritaire sur
laddition, ce qui signifie quelle doit tre faite en premier (sauf indication contraire
laide de parenthses qui, elles, sont prioritaires sur tout le monde). Et bien des
rgles de priorits existent galement en algbre boolenne. Dcouvrons-les travers
ces quelques exemples de la vie courante.
je viendrai si Albert ET Bernard viennent OU si Clara vient
Cela revient crire : A AND B OR C . Mais que doit-on comprendre ? (A AND
B) OR C ou alors A AND (B OR C) ? Quelle opration est prioritaire AND ou OR ?
Rflchissons notre exemple, si Clara est la seule venir, tant pis pour Albert et
Bernard, je viens.
Dans le cas (A AND B) OR C : les parenthses mobligent commencer par
le AND. Or, A et B sont faux tous les deux (Albert et Bernard ne viennent pas)
donc (A AND B) est faux. Nous finissons avec le OR : la premire partie est fausse
mais C est vrai (Clara vient pour notre plus grand bonheur), donc le rsultat est
Vrai ! Je viens ! A priori, cette criture correspondrait.
Dans le cas A AND (B OR C) : les parenthses mobligent commencer par
le OR. B est faux (Bernard ne vient pas) mais C est vrai (Clara vient ) donc (B
OR C) est vrai. Nous finissons avec le AND : la deuxime partie est vraie et A est
faux(Albert ne vient pas), donc le rsultat est. . . Faux ! Je suis sens ne pas venir !
Apparemment, cette criture ne correspond pas ce que lon tait sens obtenir.
Conclusion ? Il faut commencer par le AND. Si nous enlevons les parenthses, alors AND
est prioritaire sur OR. Le ET peut tre compar la multiplication ; le OU peut tre

compar laddition.

Pour ceux qui feraient (ou auraient fait) des probabilits au lyce, le ET peuttre assimil lintersection \cap et donc la multiplication, tandis que le OU
peut-tre assimil lunion \cup et donc laddition. Gardez ces liens en tte,
cela vous facilitera lapprentissage des formules ou de lalgbre boolenne.

Et XOR et NOT ?

XOR a la mme priorit que OR, ce qui est logique. Pour NOT, retenez quil ne sapplique
qu un seul boolen, il est donc prioritaire sur les autres oprations. Si vous voulez
quil sapplique toute une expression, il faudra user des parenthses.

Ordre de test
Lintrt de IF, nest pas seulement de distinguer diffrents cas pour appliquer diffrents
traitements. Il est galement utile pour viter des erreurs qui pourraient engendrer un
plantage en bonne et due forme de votre programme. Par exemple, prenons une variable
76

PRIORITS BOOLENNES ET ORDRE DE TEST (SUPPLMENT)


n de type natural : elle ne peut pas (et ne doit pas) tre ngative. Donc avant de faire
une soustraction, on sassure quelle est suffisamment grande :
1
2
3

if n >= 5
then n := n - 5 ;
end if ;

Ce que nous allons voir ici a notamment pour intrt dviter des tests qui engendreraient des erreurs. Nous reviendrons dessus durant les chapitres 11 et 17 pour illustrer
mon propos. Nous resterons ici dans la thorie. Supposons que nous voulions ordonner
nos tests :
Vrifie si A est vrai ET SEULEMENT SI CEST VRAI, vrifie si B est vrai
Quel intrt ? Je ne rentrerai pas dans un exemple en programmation, mais prenons
un exemple de la vie courante. Que vaut-il mieux faire ?
Vrifier si ma sur qui est ceinture noire de judo nest pas sous la douche ET vrifier
si il y a encore de leau chaude. ou Vrifier si ma sur qui est ceinture noire de judo est
sous la douche ET SEULEMENT SI ELLE NEST PAS SOUS LA DOUCHE vrifier
si il y a encore de leau chaude.
Les vicieux tenteront la premire solution et finiront avec une pomme de douche greffe
la place de loreille. Les prudents opteront pour la deuxime solution. Autrement dit,
en programmation, la premire mthode peut occasionner un plantage, pas la seconde.
Maintenant voyons comment implmenter cela en Ada :
1
2
3
4

if A = true AND THEN B = true


crire "= true "
THEN ...
ELSE ...
END IF ;

-- remarque : il n ' est pas utile d '

Ainsi, on ne testera le prdicat B=true que si auparavant A tait vrai. Autre


possibilit :
Vrifie si A est vrai OU SI CE NEST PAS VRAI, vrifie si B est vrai
Exemple de la vie courante. Vaut-il mieux. . .
Vrifier si il y a des steaks hachs dans le frigo ou chez le voisin ? ou Vrifier si il y
a des steaks hachs dans le frigo ou, SI VRAIMENT ON A TOUT FINI HIER aller
vrifier chez le voisin ?
Je ne sais pas vous, mais avant daller demander au voisin, je regarde si je nai pas ce
quil faut dans mon frigo. Idem en programmation, si lon peut conomiser du tempsprocesseur et de la mmoire, on ne sen prive pas. Donc pour implmenter cela, nous
crirons :
1
2
3
4

if A = true OR ELSE B = true


crire "= true "
THEN ...
ELSE ...
END IF ;

-- remarque : il n ' est pas utile d '

77

CHAPITRE 7. LES CONDITIONS II : LES BOOLENS

Petite traduction pour les ternels anglophobes : AND THEN = et alors /


OR ELSE = et sinon
Les mots rservs THEN et ELSE peuvent donc, sils sont combins respectivement avec
AND et OR, tre utiliss au moment du prdicat. Cela vous montre toute la souplesse du
langage Ada cache sous lapparente rigidit.
Lalgbre boolenne peut vite savrer complique, alors nhsitez pas faire des schmas ou des tableaux si vous commencez vous perdre dans vos conditions : quand
on dbute, bon nombre de bogues proviennent de conditions mal ficeles. Malgr cette
complexit, retenez lexistence de NOT/OR/AND. Ces trois oprateurs vous serviront rgulirement et permettront deffectuer des tests plus complexes.

En rsum
Une variable de type Boolean ne peut prendre que deux valeurs : TRUE ou FALSE.
Ces valeurs peuvent tre affectes votre variable soit directement, soit par un
prdicat (dont la valeur est en gnral inconnue du programmeur).
Loprateur OR exige quau moins lun des deux prdicats ou boolens soit vrai.
Loprateur AND exige que les deux prdicats ou boolens soient vrais. Loprateur
XOR exige quun seul des deux prdicats ou boolens soit vrai.
Les ngations (NOT) sont manier avec prudence et parcimonie car elles conduisent
trs vite des formules boolennes complexes. Ainsi, nous avons vu que NOT(A
OR B) tait identique (NOT A) AND (NOT B) ou encore que (A OR B)
AND NOT(A AND B) pouvait se simplifier en A XOR B
Rappelez-vous que AND est prioritaire sur OR. En cas de doute, mieux vaut utiliser
trop de parenthses que de risquer le bogue.

78

Chapitre

Les boucles
Difficult :
Nous avons termin le chapitre sur les conditions qui tait quelque peu ardu. Maintenant
nous allons nous intresser un nouveau problme : nos programmes auront souvent besoin
de rpter plusieurs fois la mme action et il est hors de question de jouer du copier/coller,
dautant plus que le nombre de rptitions ne sera pas ncessairement connu lavance !
Notre code doit rester propre et clair.

79

CHAPITRE 8. LES BOUCLES


Cest ce pourquoi ont t cres les boucles. Il sagit dune nouvelle instruction qui
va rpter autant de fois quon le souhaite une mme opration. Intress ? Pour comprendre tout cela, nous allons crer un programme appel Ligne qui afficherait une ligne
de # (leur nombre tant choisi par lutilisateur). La structure de notre programme
devrait donc ressembler cela :
1
2

WITH Ada . Text_IO ;


WITH Ada . Integer_Text_IO ;

USE Ada . Text_IO ;


USE Ada . Integer_Text_IO ;

3
4
5
6

Procedure Ligne is
Nb : integer ;
begin

7
8
9

Put ( " Combien de di ses voulez - vous afficher ? " ) ;


Get ( Nb ) ; Skip_line ;

10
11

-- Bloc d ' affichage

12
13

end Ligne ;

La boucle Loop simple


Principe gnral
Avec ce que nous avons vu pour linstant, nous aurions tendance crire notre bloc
daffichage ainsi :
1
2
3
4
5
6
7
8
9

case Nb is
when 0 = > Null ; -- ne rien faire
when 1 = > Put ( " # " ) ;
when 2 = > Put ( " ## " ) ;
when 3 = > Put ( " ### " ) ;
when 4 = > Put ( " #### " ) ;
when 5 = > Put ( " ##### " ) ;
when others = > Put ( " ###### " ) ;
end case ;

Le souci cest que lutilisateur peut trs bien demander 21 dises ou 24 et que nous
navons pas tellement envie dcrire tous les cas possibles et imaginables. Nous allons
donc prendre le parti de nafficher les # que un par un, et de rpter laction autant
de fois quil le faudra. Pour cela, nous allons utiliser linstruction LOOP que nous crirons
dans le bloc daffichage.
1
2
3

80

LOOP
--d but des instructions qu ' il faut r p ter
Put ( '# ') ;
END LOOP ;
-- indique la fin des instruction r p ter

LA BOUCLE LOOP SIMPLE


Ceci dfinit une boucle qui rptera linfini tout ce qui est crit entre LOOP et END
LOOP. Plus exactement, on parle de boucle itrative, litration tant ici synonyme
de rptition. Gnial non ?

Arrter une itration


Et elle sarrte quand ta boucle ?

Euh. . . en effet, si vous avez tester ce code, vous avez du vous rendre compte que cela ne
sarrte jamais (ou presque). Ce qui est crit au-dessus est une boucle infinie ! Cest
la grosse erreur de dbutant. :- (Bien que les boucles infinies ne soient pas toujours
inutiles.) Pour corriger cela, nous allons dclarer une variable appele compteur.
1

Compteur : integer := 0 ;

Cette variable vaut 0 pour linstant mais nous laugmenterons de 1 chaque fois que
nous afficherons un dise, et ce, jusqu ce quelle soit gale la variable Nb o est enregistre le nombre de dises voulus. On dit que lon incrmente la variable Compteur.
Voici donc deux corrections possibles :
1
2
3
4
5
6
7
1
2
3
4
5
6

loop
if Compteur = Nb
then exit ;
-- si on a d j affich assez de di ses ,
alors on " casse " la boucle
else Put ( '# ') ; -- sinon on affiche un di se et on incr
mente Compteur
Compteur := Compteur + 1 ;
end if ;
end loop ;
loop
exit when Compteur = Nb ; -- comme tout l ' heure , on sort si
on a affich assez de di ses
Put ( '# ') ;
-- on peut tranquillement afficher
un di se , car si le nombre
--d ' affichages tait atteint , la
boucle serait d j cass e
Compteur := Compteur + 1 ; -- et on n ' oublie pas d ' incr menter
notre compteur
end loop ;

Linstruction EXIT permet de casser la boucle et ainsi dy mettre fin. Dans ce cas, il
est important de faire le test de sortie de boucle avant dafficher et dincrmenter, afin
de permettre de tracer une ligne de zros dises. Cest aussi pour cela que Compteur
est initialis 0 et pas 1. Si lon exige laffichage dau moins un dise, alors lordre
dans la boucle peut tre modifi. Toutefois, vous devez rflchir lordre dans lequel les
81

CHAPITRE 8. LES BOUCLES


oprations seront effectues : il est rarement anodin de dcaler linstruction de sortie
dune boucle de quelques lignes.
Voici un exemple si lutilisateur tape 3 :
Valeur de Compteur
0
1
2
3

Test de sortie
Ngatif
Ngatif
Ngatif
Positif

Affichage
#
#
#

Incrmentation
+1
+1
+1

STOP

STOP

On affichera bien 3 dises (pas un de moins ou de plus). Vous voyez alors que si le test
de sortie tait effectu aprs laffichage (ce qui reviendrait inverser les deux colonnes
du milieu), nous obtiendrions laffichage dun quatrime #. De mme, si la variable
compteur tait initialis 1 (ce qui reviendrait supprimer la ligne Compteur=0),
nous naurions que deux # affichs. Prenez le temps de rflchir ou tester les valeurs
extrmes de votre compteur : 0,1, la valeur maximale si elle existe. . .
Le second code a, ici, ma prfrence IF, ELSIF, CASE. . . Il nest dailleurs pas impossible dcrire plusieurs instructions donnant lieu linstruction EXIT. Une autre variante
(plus compacte) serait de ne pas utiliser de variable Compteur et de dcrmenter la
variable Nb au fur et mesure (dcrmenter = soustraire 1 chaque tour de boucle) :
1
2
3
4
5

loop
exit when Nb = 0 ;
Nb := Nb - 1 ;
Put ( '# ') ;
!
end loop ;

-- Si Nb vaut 0 on arr te tout


-- On n ' oublie pas de d cr menter ...
-- ... et surtout d ' afficher notre di se

Cette dernire mthode est encore plus efficace. Noubliez pas que lorsque vous crez
une variable, elle va monopoliser de lespace mmoire. Si vous pouvez vous passer
dune dentre elles, cest autant despace mmoire qui sera dgag pour des tches plus
importantes de votre programme (ou dun autre). Qui plus est, votre code gagnera
galement en lisibilit.

Nommer une boucle


Lorsque votre code sera plus complexe et comportera de nombreuses boucles de plusieurs dizaines ou centaines de lignes chacune, il sera srement ncessaire de nommer
vos boucles pour vous y retrouver. Cela se fait en Ada de la mme faon quune dclaration : devant le mot cl LOOP, il vous suffit dcrire le nom de votre boucle suivi de
deux points. En revanche, il faudra galement nommer votre boucle lors du END LOOP
et ventuellement aprs linstruction EXIT.
1
2
3

82

Ma_Boucle : loop
exit Ma_Boucle when Nb = 0 ;
nommer la boucle ici
Nb := Nb - 1 ;

-- On nomme la boucle
-- Il n ' est pas obligatoire de

LA BOUCLE WHILE
4
5

Put ( '# ') ;


end loop Ma_Boucle ;
obligatoire de la nommer ici

-- En revanche il est

Pour ne pas confondre les noms de boucles avec les noms de variables, il est
bon dtablir une nomenclature. Par exemple, vous pouvez introduire systmatiquement les noms de boucle par un B_ (B_Affichage pour Boucle
daffichage ) ou un L_ (L_Affichage pour LOOP daffichage ).

La boucle While
Mais il y a mieux encore. Pour nous viter dexcuter un test de sortie au milieu de
notre boucle, il existe une variante notre instruction LOOP : WHILE ... LOOP ! En
Anglais, while de nombreux sens, mais dans ce cas de figure il signifie tant que ;
cette boucle se rptera donc tant que le prdicat inscrit dans les pointills sera vrifi.
Voici un exemple.
1
2
3
4

while Compteur /= Nb loop


Compteur := Compteur + 1 ;
Put ( '# ') ;
end loop ;

Contrairement tout lheure, le prdicat est Compteur /= Nb et plus


Compteur = Nb . WHILE indique la condition, non pas de sortie, mais de
perptuation de la boucle. Cest gnralement une cause de lchec dune
boucle ou de la cration dune boucle infinie.
Autre possibilit : dcrmenter Nb afin de ne pas dclarer de variable Compteur.
1
2
3
4

while Nb /= 0 loop
Nb := Nb - 1 ;
Put ( '# ') ;
end loop ;

Enfin, il est possible, comme avec les conditions IF, dutiliser lalgbre boolenne et
les oprateurs NOT, AND, OR (voire XOR). Cela vitera lusage dinstructions EXIT pour
grer des cas de sortie de boucle supplmentaires. Si vous navez plus que de vagues
souvenirs de ces oprateurs, rvisez le chapitre 7.

La boucle For
Les programmeurs sont des gens plutt fainants : sils peuvent limiter les oprations
effectuer, ils les limitent ! Et a tombe bien : nous aussi ! Cest pourquoi ils ont invent
83

CHAPITRE 8. LES BOUCLES


une autre variante de LOOP qui leur permet de ne pas avoir incrmenter eux mme et
de spargner la cration dune variable Compteur : la boucle FOR ... LOOP ! Lexemple
en image :
1
2
3

for i in 1 .. Nb loop
put ( " # " ) ;
end loop ;

a cest du condens, hein ? Mais que fait cette boucle ? Elle se contente dafficher des
# autant de fois quil y a de nombres entre 1 et Nb (les nombres 1 et Nb tant inclus).
Notez bien lcriture 1..Nb (avec seulement deux points, pas trois), elle indique un
intervalle entier, cest dire une liste de nombres compris entre deux valeurs.
Ce nest pas une boucle infinie ?

Et non ! Car elle utilise une variable qui lui sert de compteur : i. Ce qui prsente
plusieurs avantages : tout dabord cette variable i na pas besoin dtre dclare, elle
est automatiquement cre puis dtruite pour cette boucle (et uniquement pour cette
boucle, i sera inutilisable en dehors). Pas besoin non plus de linitialiser ou de lincrmenter (cest--dire crire une ligne i := i +1 ) la boucle sen charge toute seule,
contrairement beaucoup de langage dailleurs (comme le C). Pas besoin enfin deffectuer des tests avec IF, WHEN ou CASE qui alourdissent le code. Je parle parfois pour la
boucle FOR de boucle-compteur.
Vous vous demandez comment tout cela fonctionne ? Cest simple. Lorsque lon crit
FOR i IN 1..Nb LOOP :
FOR indique le type de boucle
i est le nom de la variable remplaant Compteur (je nai pas rutilis la variable
Compteur car celle-ci ncessitait dtre pralablement dclare, ce qui nest pas
le cas de i, je le rappelle).
IN 1..Nb indique lintervalle dans lequel la variable i va varier. Elle commencera avec la valeur 1, puis la fin de la boucle elle prendra la valeur 2, puis
3. . . jusqu la valeur Nb avec laquelle elle effectuera la dernire boucle. (Pour
les matheux : je mets Intervalle entre guillemets car il sagit dun intervalle
form dentiers seulement et non de nombres rels).
LOOP indique enfin le dbut de la boucle
Tu nous as dit de vrifier les valeurs extrmes, mais que se passera-t-il si Nb
vaut 0 ?
Cest simple, il ne se passera rien. La boucle ne commencera jamais. En effet, lintervalle 1..Nb est ce que lon appelle un integerrange en Ada et les bornes 1 et
Nb ne sont pas interchangeables dans un integerrange (intervalle fait dentiers). La
84

LES ANTIQUITS : LINSTRUCTION GOTO


borne infrieure est forcment 1, la borne suprieur est forcment Nb. Si Nb vaut 0,
alors la boucle FOR passera en revue tous les nombres entiers (integer) suprieurs 1
et infrieurs 0. Comme il nen nexiste aucun, le travail sera vite fini et le programme
nentrera jamais dans la boucle.
Quant au cas Nb vaudrait 1, lintegerrange 1..1 contient un nombre : 1 ! Donc la
boucle FOR ne fera quun seul tour de piste ! Magique non ?
Et si je voulais faire le dcompte en sens inverse ? En dcrmentant plutt
quen incrmentant ?
En effet, comme vous lavez srement compris, la boucle FOR parcourt lintervalle 1..Nb
en incrmentant sa variable i, jamais en la dcrmentant. Ce qui fait que si Nb est plus
petit que 1, alors la boucle naura pas lieu. Si vous souhaitez faire linverse , parcourir
lintervalle en dcrmentant, cest possible, il suffira de le spcifier avec linstruction
REVERSE. Cela indiquera que vous souhaitez parcourir lintervalle rebours . Voici
un exemple :
1
2
3

for i in reverse 1 .. nb loop


...
end loop ;

Attention, si Nb est plus petit que 1, la boucle ne sexcutera toujours pas !

Les antiquits : linstruction goto


Nous allons, dans cette partie, remonter aux origines des boucles et voir une instruction
qui existait bien avant lapparition des LOOP. Cest un hritage danciens langages,
qui nest gure utilis aujourdhui. Cest donc une sance darchologie (voire de
palontologie) informatique laquelle je vous invite dans cette partie, en allant la
dcouverte de linstruction GOTO (ou gotosaurus).
Cette instruction GOTO est la contraction des deux mots anglais go to qui signifient
aller . Elle va renvoyer le programme une balise que le programmeur aura plac
en amont dans son code. Cette balise est un mot quelconque que lon aura crit entre
chevrons . . . . Par exemple :
1
2
3

<< debut > >


Put ( '# ') ;
goto debut ;
debut > >"

-- on place une balise appel e " debut "


-- cela signifie " maintenant tu retournes <<

Bien sr il est prfrable dutiliser une condition pour viter de faire une boucle infinie :
1
2
3
4

<< debut > >


if Compteur <= Nb
then Put ( '# ') ;
Compteur := Compteur + 1 ;

85

CHAPITRE 8. LES BOUCLES


5
6

goto debut ;
end if ;

Cette mthode est trs archaque. Elle tait prsente en Basic en 1963 et fut trs
critique car elle gnrait des codes de pitre qualit. noter que dans le dernier
exemple linstruction GOTO se trouve au beau milieu de notre bloc IF ! Difficile de voir
rapidement o seront les instructions rpter, dautant plus quil nexiste pas de END
GOTO : cette instruction ne fonctionne pas par bloc ce qui dgrade la lisibilit du code.
Cette mthode est donc proscrire autant que possible. Prfrez-lui une belle boucle
LOOP, WHILE ou FOR (quoique GOTO mait dj sauv la mise deux ou trois fois :- ).

Boucles imbriques
Nous voudrions maintenant crer un programme appel Carre qui afficherait un carr
de # et non plus seulement une ligne.

Mthode avec une seule boucle (plutt mathmatique)


Une premire possibilit est de se casser la tte. Si on veut un carr de ct n, alors cela
veut dire que le carr aura n lignes et n colonnes, soit un total de n x n = n2 dises.
Sur un exemple : un carr de ct 5 aura 5 lignes comprenant chacune 5
dises soit un total de 5 x 5 = 25 dises. On dit que 25 est le carr de 5 et
on note 52 (cest la multiplication de 5 par lui-mme). De mme, 32 = 9, 62
= 36, 102 = 100. . .
Donc nous pourrions utiliser une boucle allant de 1 Nb2 (de 1 25 pour notre exemple)
et qui afficherait un retour la ligne de temps en temps.
Pour un carr de ct 5, il faut aller la ligne au bout de 5, 10, 15, 20. . .
dises soit aprs chaque multiple de 5. Pour savoir si un nombre X est multiple
de 5, nous utiliserons le calcul X mod 5 qui donne le reste de la division
de X par 5. Si X est un multiple de 5, ce calcul doit donner 0.
Il faudra donc une boucle du type FOR i IN 1..Nb**2 plus un test du type IF i
MOD Nb = 0 dans la boucle pour savoir si lon doit aller la ligne. vous dessayer,
je vous fournis une solution possible ci-dessous :
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6
7

86

procedure Carre is
Nb : integer ;
Begin

BOUCLES IMBRIQUES
8
9

Put ( " Quel est le cote de votre carre ? " ) ;


Get ( Nb ) ; Skip_line ;

10
11
12
13
14
15
16
17

for i in 1 .. Nb ** 2 loop -- Nb ** 2 est la notation du carr , on


peut aussi crire Nb * Nb
Put ( '# ') ;
-- on affiche le di se
if i mod Nb = 0
-- Si i est multiple de Nb = Si on a
d j affich toute une ligne
then New_line ;
-- On retourne la ligne
end if ;
end loop ;
end Carre ;

Vous avez :
compris ? Trs bien, vous devez avoir lesprit scientifique.
absolument rien compris ? Pas grave car nous allons justement ne pas faire cela !
Cest en effet un peu compliqu et ce nest pas non plus naturel comme faon de
coder ni reproductible.

Mthode avec deux boucles (plus naturelle)


La mthode prcdente est quelque peu tire par les cheveux mais constitue toutefois
un bon exercice pour manipuler les oprations puissance (Nb**2) et modulo (i MOD
Nb). Voici une mthode plus intuitive, elle consiste imbriquer deux boucles lune dans
lautre de la manire suivante :
1
2
3
4

Boucle1
Boucle2
fin Boucle2
fin Boucle1

La Boucle2 sera charge dafficher les dises dune seule ligne. La Boucle1 sera charge
de faire dfiler les lignes. chaque itration, elle relancera la Boucle2 et donc affichera
une nouvelle ligne. Do le code suivant :
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6

procedure Carre is
Nb : integer ;
Begin

7
8
9

Put ( " Quel est le cote de votre carre ? " ) ;


Get ( Nb ) ; Skip_line ;

10
11
12

for i in 1 .. Nb loop
les lignes
for j in 1 .. Nb loop
ligne

-- Boucle charg e d ' afficher toutes


-- Boucle charg e d ' afficher une

87

CHAPITRE 8. LES BOUCLES


13
14
15
16

Put ( '# ') ;


end loop ;
New_line ;
-- Apr s avoir affich une ligne ,
pensez retourner la ligne
end loop ;

17
18

end Carre ;

Et voil le travail ! Cest simple, clair et efficace. Pas besoin den crire des pleines
pages ou davoir fait Math Sup. Retenez bien cette pratique, car elle nous sera trs
utile lavenir, notamment lorsque nous verrons les tableaux.
Il est possible dinsrer des instructions EXIT dans des boucles WHILE ou FOR,
mme si elles sont imbriques. Toutefois, si linstruction exit est place dans
la boucle n2, elle ne donnera lordre de sortir que pour la boucle n2, pas
pour la n1. Pour sortir des boucles n1 et n2 en mme temps (voir mme
dune boucle n3), lastuce consiste nommer la premire boucle comme
nous lavons vu au dbut de ce chapitre, par exemple B_Numero1, et crire
linstruction EXIT B_Numero1.

Pourquoi utilises-tu des lettres i, j. . . et pas des noms de variable plus explicites ?
Cette habitude nest pas une obligation, libre vous de donner vos variables de
boucles les noms que vous souhaitez. Voici toutefois trois raisons, assez personnelles,
pour lesquelles je conserverai cette habitude tout au long de ce tutoriel :

1. Raison 1 : la variable i ou j a un temps de vie trs court (restreint la seule


boucle FOR) elle ne sera pas rutilise plus tard. Je devrais donc russir me
souvenir de sa signification jusquau END LOOP.
2. Raison 2 : la variable i ou j servira trs souvent comme indice dun tableau.
Exemple : tableau(i) est la i-me valeur du tableau. Nous verrons cela dans un
prochain chapitre, mais vous pouvez comprendre quil est plus simple dcrire
tableau(i) que tableau(Nom_de_variable_long_et_complique).
3. Raison 3 : jai suivi une formation Mathmatiques-Informatique. Et en Maths
on a pour habitude dutiliser la variable i lorsque lon traite dobjets ayant un
comportement similaire aux boucles. Et vous avez srement remarqu que nous
faisons rgulirement appel aux Mathmatiques pour programmer. Par exemple,
lorsquun mathmaticien veut additionner
PN les carrs de tous les nombres compris
entre 1 et N, il crit gnralement : i=1 i2 .
88

EXERCICES

Exercices
Voil voil. Si maintenant vous voulez vous exercer avec des boucles imbriques, voici
quelques ides dexercices. Je ne propose pas de solution cette fois. vous de vous
creuser les mninges.

Exercice 1
Affichez un carr creux :
######
#
#
#
#
#
#
#
#
######

Exercice 2
Affichez un triangle rectangle :
#
##
###
####
#####
######

Exercice 3
Affichez un triangle isocle, quilatral, un rectangle. . . ou toute autre figure.

Exercice 4
Rdigez un programme testant si un nombre N est multiple de 2, 3, 4. . . N-1. Pensez
lopration mod !

Exercice 5
Rdigez un programme qui demande lutilisateur de saisir une somme S, un pourcentage P et un nombre dannes T. Ce programme affichera les intrts accumuls sur
cette somme S avec un taux dintrt P ainsi que la somme finale, tous les ans jusqu
la t-ime anne.
89

CHAPITRE 8. LES BOUCLES


Calculer un intrt P% sur une somme S revient effectuer lopration
S*P/100. La somme finale est obtenue en ajoutant les intrts la somme
initiale ou en effectuant lopration S*(100+P)/100.

Exercice 6
Crez un programme qui calcule les termes de la Suite de Fibonacci. En voici les
premiers termes : 0 ; 1 ; 1 ; 2 ; 3 ; 5 ; 8 ; 13 ; 21 ; 34 ; 55. . . Chaque terme se calcule en
additionnant les deux prcdents, les deux premiers termes tant 0 et 1.
Maintenant que nous avons acquis de bonnes notions sur les boucles et les conditions,
nous allons pouvoir nous intresser aux sous-programmes. Nous reviendrons amplement
sur les boucles lors du chapitre 11. Celui-ci fera appel tout ce que vous avez dj vu
sur le typage des donnes (integer, float. . .), les conditions mais aussi et surtout sur
les boucles qui rvleront alors toute leur utilit (notamment la boucle FOR). Ce sera
galement loccasion de dcouvrir une autre version de la boucle FOR que je ne peux
encore vous rvler.

En rsum
Lorsquil est ncessaire de rpter plusieurs fois des instructions vous devrez faire
appel aux boucles.
Si vous utilisez une boucle simple (LOOP), noubliez pas dinsrer une condition
de sortie avec linstruction EXIT. Vrifiez galement que cette condition peut-tre
remplie afin dviter les boucles infinies.
En rgle gnrale, on utilise plus souvent des boucles WHILE ou FOR afin que la
condition de sortie soit facile retrouver parmi vos lignes de code. La boucle
LOOP sera souvent rserve des conditions de sortie complexes et/ou multiples.
Linstruction EXIT peut tre utilise dans des boucles WHILE ou FOR, mais limitez
cette pratique autant que possible.
De mme quil est possible dimbriquer plusieurs conditions IF, vous pouvez imbriquer plusieurs boucles, de mme nature ou pas. Cette pratique est trs courante
et je vous invite la travailler si vous ntes pas laise avec elle.

90

Chapitre

Procdures et fonctions I
Difficult :
Nous avons vu les variables et leurs diffrents types et oprations, les conditions, les
boucles. . . autant dlments essentiels lAlgorithmique et tout langage, que ce soit
lAda, le C, le C++, le Python. . . Nous allons maintenant voir comment organiser et
hirarchiser notre code laide des fonctions et des procdures (prsentes dans tous les
langages elles aussi). Ces lments nous seront essentiels pour la suite du cours. Mais,
rassurez-vous, ce nest pas bien compliqu (au dbut en tous cas ).

91

CHAPITRE 9. PROCDURES ET FONCTIONS I

Les procdures
Procdure sans paramtre
Vous avez dj vu maintes fois le terme procdure. Allons, souvenez-vous :
1
2

with Ada . Text_IO ;


use Ada . Text_IO ;

3
4
5
6
7
8

procedure Main is
...
begin
...
end Main ;

Et oui ! Votre programme est constitu en fait dune grande procdure (plus les packages
en en-tte, cest vrai ). Donc vous savez dors et dj comment on la dclare :
1
2
3
4
5

Procedure N om _De _Vo tr e_P ro ced ure is


-- Partie pour d clarer les variables
begin
-- Partie ex cut e par le programme
end N o m _ D e _Vo tr e_P ro ced ure ;

Quel est donc lintrt den parler si on sait dj ce que cest ?

Eh bien pour linstant, notre programme ne comporte quune seule procdure qui, au
fil des modifications, ne cessera de sallonger. Et il est fort probable que nous allons
tre amens recopier plusieurs fois les mmes bouts de code. Cest ce moment
quil devient intressant de faire appel de nouvelles procdures afin de clarifier et
de structurer notre code. Pour mieux comprendre, nous allons reprendre lexemple vu
pour les boucles : dessiner un rectangle.
Voici le code de notre programme :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10

92

procedure Figure is
nb_lignes : natural ;
nombre de lignes
begin
Put ( " Combien de lignes voulez - vous dessiner ? " ) ;
demande le nombre de lignes
get ( nb_lignes ) ; skip_line ;
for i in 1 .. nb_lignes loop
ex cute deux boucles pour afficher notre rectangle
for j in 1 .. 5 loop

--- on
-- on

LES PROCDURES
11
12
13
14
15

put ( '# ') ;


end loop ;
new_line ;
end loop ;
end Figure ;

Comme vous pouvez le voir, ce programme Figure propose dafficher un rectangle de


dises. Chaque rectangle comporte 5 colonnes et autant de lignes que lutilisateur le
souhaite.
Combien de lignes voulez - vous dessiner ? 7
#####
#####
#####
#####
#####
#####
#####
_

Nous allons remplacer la deuxime boucle (celle qui affiche les dises) par une procdure
que nous appellerons Affich_Ligne dont voici le code :
1
2
3
4
5
6
7

Procedure Affich_Ligne is
begin
for j in 1 .. 5 loop
put ( '# ') ;
end loop ;
new_line ;
end Affich_Ligne ;

Euh, cest bien gentil tout a, mais je lcris o ?

Cest trs simple. Le code ci-dessus permet de dclarer au compilateur la sous-procdure


Affich_Ligne. Puisquil sagit dune dclaration, il faut crire ce code dans la partie rserve aux dclarations de notre procdure principale (avec la dclaration de la variable
nb_lignes). Voici donc le rsultat :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4

procedure Figure is

5
6
7
8
9

Procedure Affich_Ligne is
begin
for j in 1 .. 5 loop
put ( '# ') ;

93

CHAPITRE 9. PROCDURES ET FONCTIONS I


10
11
12

end loop ;
new_line ;
end Affich_Ligne ;

13
14

nb_lignes : natural ;

15
16
17
18
19
20
21
22

begin
Put ( " Combien de lignes voulez - vous dessiner ? " ) ;
get ( nb_lignes ) ; skip_line ;
for i in 1 .. nb_lignes loop
Affich_Ligne ;
end loop ;
end Figure ;

Vous remarquerez que dans la partie de la procdure principale contenant les instructions, la deuxime boucle, son contenu et le new_line ont disparu et ont laiss la place
un simple Affich_Ligne ; . Cette partie devient ainsi plus lisible et plus compacte.
Mouais. . . Bah, crire tout a pour gagner 3 lignes dans la procdure principale, moi je dis que cest pas folichon.
Nen jetez plus ! >_< En effet, cela ne semble pas tre une opration trs rentable,
mais je vous invite continuer la lecture car vous allez vite comprendre lintrt des
sous-procdures.

Procdure avec un paramtre (ou argument)


Notre programme est pour lheure trs limit : lutilisateur ne peut pas choisir le nombre
de colonnes ! Nous savons facilement ( laide de Put et Get) obtenir un nombre de
colonnes mais comment faire pour que notre procdure Affich_Ligne affiche le nombre
de dises dsir ? Nous ne pouvons pas effectuer la saisie de la variable Nb_Colonnes
dans la procdure Affich_Ligne, car la boucle de la procdure principale demanderait
plusieurs fois le nombre de colonnes dsir.
Ah ! Cest malin ! Avec tes histoires, je me suis cass la tte et en plus je ne
peux plus amlior mon programme !
Attendez ! Il y a une solution ! Nous allons demander le nombre de colonnes en mme
temps que le nombre de lignes et nous allons modifier notre procdure Affich_Ligne
pour quelle puisse crire autant de dises quon le souhaite (pas forcment 5). Voici le
nouveau code de notre sous-procdure :
1
2
3

94

Procedure Affich_Ligne ( nb : natural ) is


begin
for j in 1 .. nb loop

LES PROCDURES
4
5
6
7

put ( '# ') ;


end loop ;
new_line ;
end Affich_Ligne ;

Nous avons remplac j in 1..5 par j in 1..Nb . La variable nb correspondra


bien videmment au nombre de dises dsirs. Mais surtout, le nom de notre procdure
est maintenant suivi de parenthses lintrieur desquelles on a dclar la fameuse
variable nb.
Jai deux questions : Pourquoi on la dclare pas dans la zone prvue cet
effet, entre IS et BEGIN ? Et est-ce normal que ta variable nb nait jamais t
initialise ?
Cette variable nb est ce que lon appelle un paramtre. Elle nest pas dclare dans
la zone prvue cet effet , ni initialise, parce quelle vient de lextrieur . Petite
explication : toute variable dclare entre IS et BEGIN a une dure de vie qui est lie
la dure de vie de la procdure. Lorsque la procdure commence, la variable est cre ;
lorsque la procdure se termine, la variable est dtruite : plus moyen dy accder ! Cela
va mme plus loin ! Les variables dclares entre IS et BEGIN sont inaccessibles en
dehors de leur propre procdure ! Elles nont aucune existence possible en dehors. Le
seul moyen de communication offert est travers ce paramtre : il permet la procdure
principale de copier et denvoyer le contenu dune variable dans la sous-procdure.

Figure 9.1 Les variables ne sont utilisables que dans la procdure o elles sont
dclares
Comme lindique le schma 9.1, il existe deux variables n1 : une pour la procdure
principale et une pour la sous-procdure, mais elles nont rien voir ! Chacune fait sa
petite vie dans sa propre procdure. Il serait bien sr impossible de dclarer dans la
95

CHAPITRE 9. PROCDURES ET FONCTIONS I


mme procdure deux variables de mme nom. Qui plus est, si une variable n2 est
envoye comme paramtre dans une sous-procdure, alors elle formera une nouvelle
variable, appele paramtre, distincte de la variable n2 initiale. La variable n2 et
le paramtre auront la mme valeur mais constitueront deux variables distinctes, la
seconde ntant quune copie de la premire.
Voil pourquoi la variable nb est dclare entre parenthses : cest un paramtre. Quant
sa valeur, elle sera dtermine dans la procdure principale avant dtre envoye
dans la procdure Affich_Ligne() .
Mais revenons notre procdure principale : Figure. Voil ce que devient son code :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4

procedure Figure is

5
6
7
8
9
10
11
12

Procedure Affich_Ligne ( nb : natural ) is


begin
for j in 1 .. nb loop
put ( '# ') ;
end loop ;
new_line ;
end Affich_Ligne ;

13
14

nb_lignes , nb_colonnes : natural ;

15
16

begin

17
18
19
20
21

Put ( " Combien de


get ( nb_lignes )
Put ( " Combien de
get ( nb_colonnes

lignes voulez - vous dessiner ? " ) ;


; skip_line ;
colonnes voulez - vous dessiner ? " ) ;
) ; skip_line ;

22
23
24
25

for i in 1 .. nb_lignes loop


Affich_Ligne ( nb_colonnes ) ;
end loop ;

26
27

end Figure ;

Nous avons ajout deux lignes pour saisir le nombre de colonnes voulues. Linstruction
Affich_ligne est devenue Affich_Ligne(nb_colonnes) . Et voil ! Notre problme
initial est rsolu !

Procdure avec plusieurs paramtres (ou arguments)


Et bah moi je continue penser que ctait bien plus simple sans sousprocdure ! Quel intrt de saucissonner notre code ?
96

LES PROCDURES
Toujours pas convaincu ? Et bien voil qui devrait mettre fin vos rticences. Nous
allons perfectionner notre programme : il proposera dafficher soit un rectangle soit un
triangle rectangle isocle (un demi-carr pour les mathophobes ).
Il va donc falloir purer notre code en crant deux procdures supplmentaires : une
pour crer un rectangle (Affich_Rect) et une pour crer le triangle rectangle isocle (Affich_Tri). Ainsi, notre procdure principale (Figure) se chargera de poser les questions
lutilisateur, puis elle rorientera vers lune ou lautre de ces deux sous-procdures.
La procdure Affich_Rect prendra deux paramtres : le nombre de colonnes et le
nombre de lignes.
1
2
3
4
5
6

Procedure Affich_Rect ( nb_lignes : natural ; nb_colonnes :


natural ) is
begin
for i in 1 .. nb_lignes loop
Affich_Ligne ( nb_colonnes ) ;
end loop ;
end Affich_Rect ;

Ici, les deux paramtres sont spars par un ; ! Cela permet dinsrer un
retour la ligne au milieu des paramtres pour rendre plus lisible notre code.

Pour fonctionner, Affich_Rect a besoin de la procdure Affich_Ligne, il faut


donc que Affich_Ligne soit dclare avant Affich_Rect !
Et maintenant notre procdure Affich_Tri :
1
2
3
4
5
6

Procedure Affich_Tri ( nb : natural ) is


begin
for i in 1 .. nb loop
Affich_Ligne ( i ) ;
end loop ;
end Affich_Tri ;

Lutilit des procdures commence apparatre clairement : il a suffit dcrire Affich_Ligne(i) pour que le programme se charge dafficher une ligne comportant le
nombre de dises voulus, sans que lon ait rcrire notre boucle et se creuser
la tte pour savoir comment la modifier pour quelle naffiche pas toujours le mme
nombre de dises. Alors, convaincu cette fois ? Je pense que oui. Pour les rcalcitrants, je vous conseille dajouter des procdures Affich_Carre, Affich_Tri_Isocele,
Affich_Carre_Vide. . . et quand vous en aurez assez de recopier la boucle daffichage
des dises, alors vous adopterez les procdures.
Bien ! Il nous reste encore modifier notre procdure principale. En voici pour linstant
une partie :
1

begin

97

CHAPITRE 9. PROCDURES ET FONCTIONS I


2
3
4
5
6

Put_line ( " Voulez - vous afficher : " ) ;


propose les deux choix : rectangle ou triangle ?
Put_line ( "1 - Un rectangle ? " ) ;
Put_line ( "2 - Un triangle ? " ) ;
Get ( choix ) ; skip_line ;

-- On

7
8
9

Put ( " Combien de lignes voulez - vous dessiner ? " ) ; -- on


saisit tout de suite le nombre de lignes
get ( nb_lignes ) ; skip_line ;

10
11
12
13
14
15
16

if choix = 1
-- selon
le choix on proc de l ' affichage de la figure
then Put ( " Combien de colonnes voulez - vous dessiner ? " ) ;
get ( nb_colonnes ) ; skip_line ;
Affich_Rect ( nb_lignes , nb_colonnes ) ;
else Affich_Tri ( nb_lignes ) ;
end if ;

17
18

end Figure ;

Notre code est tout de mme beaucoup plus lisible que sil comportait de nombreuses
boucles imbriques !
Mais ? Il y a une erreur ! Pour Affich_Rect, les paramtres doivent tre spars
par un point virgule et pas par une virgule ! La honte !

Et bien non ! Voici une erreur que le compilateur ne vous pardonnerait pas. On
utilise le point virgule ; uniquement lors de la dclaration de la procdure.
Lorsque lon fait appel la procdure pour de vrai , on nutilise que la
virgule , !

Les fonctions
Quest-ce quune fonction ? Cest peu ou prou la mme chose que les procdures. La
diffrence, cest que les fonctions renvoient un rsultat.

Une bte fonction


Pour mieux comprendre, nous allons crer une fonction qui calculera laire de notre
rectangle. On rappelle (au cas o certains auraient oubli leur cours de 6me) que
laire du rectangle se calcule en multipliant sa longueur par sa largeur !
98

LES FONCTIONS
Dclaration
Commenons par dclarer notre fonction (au mme endroit que lon dclare variables
et procdures) :
1
2
3
4
5
6

function A_Rect ( larg : natural ; long : natural ) return natural


is
A : natural ;
begin
A := larg * long ;
return A ;
end A_Rect ;

Globalement, cela ressemble beaucoup une procdure part que lon utilise le mot
FUNCTION au lieu de PROCEDURE. Une nouveaut toutefois : linstruction RETURN. Par
RETURN, il faut comprendre rsultat . Dans la premire ligne, RETURN Natural IS
indique que le rsultat de la fonction sera de type natural. En revanche, le RETURN
A de la 5me ligne a deux actions :
Il stoppe le droulement de la fonction (un peu comme EXIT pour une boucle).
Il renvoie comme rsultat la valeur contenue dans la variable A.
noter quil est possible de condenser le code ainsi :
1
2
3
4

function A_Rect ( larg : natural ; long : natural ) return natural


is
begin
return larg * long ;
end A_Rect ;

Utilisation
Nous allons donc dclarer une variable Aire dans notre procdure principale. Puis, nous
allons changer la condition de la manire suivante :
1

...

2
3
4
5
6
7
8
9
10

...

if choix = 1
then Put ( " Combien de colonnes voulez - vous dessiner ? " ) ;
get ( nb_colonnes ) ; skip_line ;
Affich_Rect ( nb_lignes , nb_colonnes ) ;
Aire := A_Rect ( nb_lignes , nb_colonnes ) ;
Put ( "L ' aire du rectangle est de " ) ; Put ( Aire ) ;
put_line ( " dieses . " ) ;
else Affich_Tri ( nb_lignes ) ;
end if ;

Notre variable Aire va ainsi prendre comme valeur le rsultat de la fonction A_Rect().
Noubliez pas le signe daffectation := ! Dailleurs, cela ne vous rappelle rien ? Souvenezvous des attributs !
99

CHAPITRE 9. PROCDURES ET FONCTIONS I


1

c := character ' val ( 153 ) ;

Ces attributs agissaient la manire des fonctions !

Une fonction un peu moins bte (optionnel)

Et laire du triangle ?

Cest un peu plus compliqu. Normalement, pour calculer laire dun triangle rectangle
isocle, on multiplie les cts de langle droit entre eux puis on divise par 2. Donc pour
un triangle de ct 5, cela fait 5 2 5 = 12, 5 dises ! ? ! Bizarre. Et puis, si lon fait
un exemple :
#
##
###
####
#####

Il y a en fait 15 dises ! ! Pour quelle raison ? Et bien, ce que nous avons dessin nest pas
un vrai triangle. On ne peut avoir de demi-dises, de quart de dises. . . La solution
est donc un peu plus calculatoire. Remarquez qu chaque ligne, le nombre de dises
est incrment (augment de 1). Donc si notre triangle a pour ct N , pour connatre
laire, il faut effectuer le calcul : 1 + 2 + 3 + ... + (N 1) + N . Les points de
suspension remplaant les nombres que je nai pas envie dcrire. Comment trouver le
rsultat ? Astuce de mathmaticien. Posons le calcul dans les deux sens :

Figure 9.2 Calcul de laire du triangle


En additionnant les termes deux deux, on remarque que lon trouve toujours N + 1.
Et comme il y a N termes cela nous fait un total de : N (N + 1). Mais noublions
+1)
pas que nous avons effectuer deux fois notre calcul. Le rsultat est donc : N (N
.
2
Pour un triangle de ct 5, cela fait
notre fonction A_Triangle !
1
2

100

5 6
2

= 15 dises ! Bon, il est temps de crer

function A_Triangle ( N : natural ) return natural is


A : natural ;

LES FONCTIONS
3
4
5
6

begin
A := N * ( N + 1 ) / 2 ;
return A ;
end A_Triangle ;

Et voil ce que donne le code gnral :


1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4

procedure Figure is

5
6
7
8
9
10
11
12

Procedure Affich_Ligne ( nb : natural ) is


begin
for j in 1 .. nb loop
put ( '# ') ;
end loop ;
new_line ;
end Affich_Ligne ;

13
14
15
16
17
18
19

Procedure Affich_Rect ( nb_lignes : natural ; nb_colonnes :


natural ) is
begin
for i in 1 .. nb_lignes loop
Affich_Ligne ( nb_colonnes ) ;
end loop ;
end Affich_Rect ;

20
21
22
23
24
25
26

Procedure Affich_Tri ( nb : natural ) is


begin
for i in 1 .. nb loop
Affich_Ligne ( i ) ;
end loop ;
end Affich_Tri ;

27
28
29
30
31
32
33

function A_Rect ( larg : natural ; long : natural ) return


natural is
A : natural ;
begin
A := larg * long ;
return A ;
end A_Rect ;

34
35
36
37
38
39
40

function A_Triangle ( N : natural ) return natural is


A : natural ;
begin
A := N * ( N + 1 ) / 2 ;
return A ;
end A_Triangle ;

41
42

nb_lignes , nb_colonnes , choix , Aire : natural ;

101

CHAPITRE 9. PROCDURES ET FONCTIONS I


43

begin

44
45

Put_line ( " Voulez - vous afficher : " ) ;


Put_line ( "1 - Un rectangle ? " ) ;
Put_line ( "2 - Un triangle ? " ) ;
Get ( choix ) ; skip_line ;

46
47
48
49
50

Put ( " Combien de lignes voulez - vous dessiner ? " ) ;


get ( nb_lignes ) ; skip_line ;

51
52
53

if choix = 1
then Put ( " Combien de colonnes voulez - vous dessiner ? " ) ;
get ( nb_colonnes ) ; skip_line ;
Affich_Rect ( nb_lignes , nb_colonnes ) ;
Aire := A_Rect ( nb_lignes , nb_colonnes ) ;
Put ( "L ' aire du rectangle est de " ) ; Put ( Aire ) ;
put_line ( " dieses . " ) ;
else Affich_Tri ( nb_lignes ) ;
Aire := A_Triangle ( nb_lignes ) ;
Put ( "L ' aire du triangle est de " ) ; Put ( Aire ) ;
put_line ( " dieses . " ) ;
end if ;

54
55
56
57
58
59
60
61
62
63
64
65

end Figure ;

Bilan
Rendu ce stade, vous avez du remarquer que le code devient trs charg. Voici donc
ce que je vous propose : COMMENTEZ VOTRE CODE ! Exemple :
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4

procedure Figure is

5
6
7
8

-- -- - - -- - - -- - -- PROCEDURES - -- -- - - -- - - -- - -

9
10
11
12
13
14
15
16
17

102

Procedure Affich_Ligne ( nb : natural ) is


begin
for j in 1 .. nb loop
put ( '# ') ;
end loop ;
new_line ;
end Affich_Ligne ;

LES FONCTIONS
18
19
20
21
22
23

Procedure Affich_Rect ( nb_lignes : natural ; nb_colonnes :


natural ) is
begin
for i in 1 .. nb_lignes loop
Affich_Ligne ( nb_colonnes ) ;
end loop ;
end Affich_Rect ;

24
25
26
27
28
29
30

Procedure Affich_Tri ( nb : natural ) is


begin
for i in 1 .. nb loop
Affich_Ligne ( i ) ;
end loop ;
end Affich_Tri ;

31

-- - - -- -- --- --- FONCTIONS - -- - - -- -- --- --

32
33
34
35
36
37
38
39

function A_Rect ( larg : natural ; long : natural ) return


natural is
begin
return larg * long ;
end A_Rect ;

40
41
42
43
44

function A_Triangle ( N : natural ) return natural is


begin
return N * ( N + 1 ) / 2 ;
end A_Triangle ;

45

-- - - -- -- --- --- VARIABLES - -- - - -- -- --- --

46
47
48
49
50

nb_lignes , nb_colonnes , choix : natural ;

51

-- - - - - - - - - - - - - - - - - - - - - - -- PROCEDURE PRINCIPALE - -- - - - - - - - - - - - - - - - - - - - - - -

52
53
54
55
56

begin

57
58
59
60
61
62
63

Put_line ( " Voulez - vous afficher : " ) ;


Put_line ( "1 - Un rectangle ? " ) ;
Put_line ( "2 - Un triangle ? " ) ;
Get ( choix ) ; skip_line ;
Put ( " Combien de lignes voulez - vous dessiner ? " ) ;
get ( nb_lignes ) ; skip_line ;

64
65

if choix = 1

103

CHAPITRE 9. PROCDURES ET FONCTIONS I


then Put ( " Combien de colonnes voulez - vous dessiner ? " ) ;
get ( nb_colonnes ) ; skip_line ;
Affich_Rect ( nb_lignes , nb_colonnes ) ;
Put ( "L ' aire du rectangle est de " ) ; Put ( A_Rect (
nb_lignes , nb_colonnes ) ) ; put_line ( " dieses . " ) ;
else Affich_Tri ( nb_lignes ) ;
Put ( "L ' aire du triangle est de " ) ; Put ( A_Triangle (
nb_lignes ) ) ; put_line ( " dieses . " ) ;
end if ;

66
67
68
69
70
71
72
73
74

end Figure ;

Jutilise, pour ma part, de gros commentaires afin de structurer mon code. Nous verrons bien plus tard comment crer des packages afin de librer notre procdure principale. Autre remarque, dans cet exemple jai condens mon code. Jai notamment
crit : Put(A_Rect(nb_lignes, nb_colonnes)) ; ! Il est en effet possible dimbriquer
plusieurs fonctions (ou/et une procdure) les unes dans les autres pour compacter
votre code. Faites toutefois attention ce que les types soient compatibles !

Prdfinir ses paramtres


Il est possible de donner une valeur par dfaut aux paramtres dune procdure ou
dune fonction. Par exemple :
1

Procedure Affich_Ligne ( nb : natural := 1 ) is

Ainsi, il sera possible de ne pas spcifier de paramtre. En tapant Affich_Ligne , le


compilateur comprendra tout seul Affich_Ligne(1) . Cette astuce est surtout utile
pour des fonctions exigeant de nombreux paramtres dont certains seraient optionnels.
Imaginons par exemple une fonction Affich_Rect dfinie comme suit :
1
2
3
4
5
6

procedure Affich_Rect ( nb_lignes


nb_colonnes
symbole
plein
affich_a
affich_p

:
:
:
:
:
:

natural ;
natural ;
character ;
boolean ;
boolean ;
boolean ) ;

On dit quil sagit due la spcification de la procdure Affich_Rect (dans dautres


langages, comme le C, on parlerait plutt de prototype ). On se moque ici de la faon
dont la procdure est code, on se contente dune simple dclaration. Nous reverrons
les spcifications lors du chapitre 13 et nous expliquerons alors pourquoi le IS a t
bizarrement remplac par un point virgule.
Expliquons plutt les diffrents paramtres :
nb_lignes et nb_colonnes jouent le mme rle que lors des exemples prcdents.
symbole est le caractre quil faut afficher pour constituer le rectangle. Auparavant, ce caractre tait le dise (#).
104

PRDFINIR SES PARAMTRES


plein est un boolen qui indique si le rectangle est creux (plein vaut FALSE) ou
plein (plein vaut TRUE).
affich_a et affich_p sont deux boolens. Si affich_a vaut TRUE, alors le programme affichera laire du rectangle. Sil vaut FALSE, alors le programme naffichera rien. Le paramtre affich_p joue le mme rle mais avec le primtre au
lieu de laire.
Nous avons ainsi une procdure vraiment complte. Le seul inconvnient cest que
lorsque lon veut afficher deux simples rectangles forms de dises, il faut taper :
1
2

Affich_Rect (5 ,8 , '# ' , true , false , false ) ;


Affich_Rect (7 ,3 , '# ' , true , false , false ) ;

Hormis les deux premiers, on ne changera pas souvent les paramtres. La procdure
est complte, mais un peu trop : il faudra retaper souvent la mme chose (#, TRUE,
FALSE, FALSE) en prenant garde de ne pas semmler les pinceaux dans lordre des
variables (trs important lordre des variables !).
Eh bien moi, cest certain ! Je ne ferai jamais de procdure trop complique ! :
Allons ! Cette procdure est trs bien, il faut juste identifier les paramtres qui seront
optionnels et leur donner une valeur par dfaut. Il serait donc plus judicieux de dclarer
notre procdure ainsi :
1
2
3
4
5
6

procedure Affich_Rect ( nb_lignes


nb_colonnes
symbole
plein
affich_a
affich_p

:
:
:
:
:
:

natural ;
natural ;
character
boolean
boolean
boolean

:=
:=
:=
:=

'# ';
true ;
false ;
false ) ;

Ainsi, nous pourrons lappeler plus simplement :


1
2

Affich_Rect (5 , 8 ) ; -- Un simple rectangle 5 par 8


Affich_Rect (7 ,3 , '@ ') ; -- un rectangle 7 par 3 avec des @ la
place des # !

Ah oui, cest effectivement intressant. Mais si je veux un rectangle 5 par 8 classique en affichant le primtre, je peux taper
Affich_Rect(5,8,true) ; ?

NON ! Surtout pas ! Le troisime paramtre est sens tre un caractre, pas
un boolen !
105

CHAPITRE 9. PROCDURES ET FONCTIONS I

Mais je fais quoi alors ?

Cest vrai que dans ce cas, on a un souci. Il nous faut respecter lordre des paramtres
mais certains ne sont pas utiles. Heureusement, il y a un solution !
1

Affich_Rect (5 ,8 , affich_p = > true ) ;

Il suffit de spcifier le paramtre auquel on attribue une valeur ! Attention, on nutilise


pas le symbole := mais la flche => . Ainsi, lordre na plus dimportance, on
pourrait tout aussi bien noter :
1
2

Affich_Rect (5 , affich_p = > true , nb_colonnes = > 8 ) ;


Affich_Rect ( nb_colonnes = > 8 , affich_p = > true , nb_lignes = > 5 )
;

Do lintrt de bien choisir ses noms de variables (et de paramtres en loccurrence).

In, Out, In Out


Nous avons vu prcdemment que les paramtres dune fonction ou dune procdure
ne peuvent pas (et ne doivent pas) tre modifis durant le droulement de la fonction
ou de la procdure. Eh bien. . . cest faux ! Enfin non ! Cest vrai, mais disons quil y a
un moyen de contourner cela pour les procdures.

Paramtres de procdure
Mode in
Lorsque lon crit :
1

procedure Affich_Rect ( nb_lignes : natural , nb_colonnes :


natural ) is

Nous crivons, implicitement, ceci :


1

procedure Affich_Rect ( nb_lignes : in natural , nb_colonnes : in


natural ) is

Les instructions IN indiquent que nos paramtres sont uniquement des paramtres
dentre. Les emplacements-mmoire qui contiennent leur valeur peuvent tre lus mais
il vous est interdit dy crire : ils ne sont accessibles quen lecture. Donc impossible
dcrire nb_lignes := 5 au cours de cette procdure.
Mode out
En revanche, en crivant ceci :
106

IN, OUT, IN OUT


1

procedure Affich_Rect ( nb_lignes : out natural , nb_colonnes :


out natural ) is

. . . vos paramtres deviendront des paramtres de sortie. Il sera possible dcrire dans
les emplacements-mmoire rservs nb_lignes et nb_colonnes ! Gnial non ? Sauf
quil vous sera impossible de lire la valeur initiale, donc impossible deffectuer des tests
par exemple. Vos paramtres ne sont accessibles quen criture.
Mode in out
Heureusement, il est possible de mlanger les deux :
1

procedure Affich_Rect ( nb_lignes : in out natural , nb_colonnes :


in out natural ) is

Nous avons alors des paramtres dentre et de sortie, accessibles en lecture ET en


criture ! Tout nous est permis !
Pourquoi tous les paramtres ne sont-ils pas en mode in out par dfaut ?

Rflchissons notre procdure Affich_Rect : est-il judicieux que nos paramtres


nb_lignes et nb_colonnes soient accessibles en criture ? Je ne pense pas. Notre procdure na pas besoin de les modifier et il ne serait pas bon quelle les modifie. Les
laisser en mode IN, cest prendre lassurance que nos paramtres ne seront pas modifis
(sinon, le rigoureux compilateur GNAT se chargera de vous rappeler lordre ). Et
rciproquement, des paramtres en mode OUT nont pas tre lu.
Un exemple ? Imaginez une procdure dont lun des paramtres sappellerait Ca_Marche
et servirait indiquer si il y a eu un chec du programme ou pas. Notre procdure na
pas lire cette variable, cela naurait pas de sens. Elle doit seulement lui affecter une
valeur. Cest pourquoi vous devrez prendre quelques secondes de rflexion avant de
choisir entre ces diffrents modes et notamment pour le mode IN OUT.

Paramtres de fonction

Pourquoi ne parles-tu que des procdures ? Quen est-il des fonctions ?

Pour les fonctions, tout dpend de votre compilateur. Pour les normes Ada83, Ada95 et
Ada2005, seul le mode IN est accessible pour les fonctions. Ce qui est logique puisquune
fonction renvoie dj un rsultat calcul partir de paramtres. Il ny a donc pas de
raison pour que certains paramtres constituent des rsultats.
107

CHAPITRE 9. PROCDURES ET FONCTIONS I


Mais cela peut parfois se rvler fort restrictif. Cest pourquoi la norme Ada2012 permet, entre autres amliorations, que les fonctions utilisent des paramtres en mode IN
OUT. Le mode OUT nest pas accessible : comment calculer le rsultat si les paramtres
ne sont pas lisibles.
Nous savons maintenant comment mieux organiser nos codes en Ada. Cela nous permettra de crer des programmes de plus en plus complexes sans perdre en lisibilit.
Nous reviendrons plus tard sur les fonctions et procdures pour aborder une mthode
trs utile en programmation : la rcursivit. Mais cette partie tant bien plus complique comprendre, nous allons pour lheure laisser reposer ce que nous venons de
voir.
Lors du prochain chapitre thorique, nous aborderons un nouveau type : les tableaux !
Nous entrerons alors dans un nouvel univers (et une nouvelle partie) : celui des types
composites ! Je conseille donc ceux qui ne seraient pas encore au point de revoir les
chapitres de la seconde partie. Avant tout cela, il est lheure pour vous de mettre en
application ce que nous avons vu. Le chapitre qui suit est un chapitre pratique : un
TP !

En rsum
Structurez votre code source en le dcoupant en sous-programmes, voire en soussous-programmes.
Un sous-programme est gnralement une PROCEDURE. Mais si vous souhaitez
calculer un rsultat, il sera prfrable dutiliser les FUNCTION.
La plupart des sous-programmes utilisent des paramtres : pensez rflchir
leur mode (lecture et/ou criture) et, si besoin, leur attribuer une valeur par
dfaut pour simplifier leur utilisation ultrieure.
Si un sous-programme A a besoin dun sous-programme B, alors il est ncessaire
que B soit dclar avant A, sinon le compilateur ne pourra le voir.

108

Chapitre

10

[TP] Le craps
Difficult :
Bien, jusque l je vous ai guid lors des diffrents chapitres. Je vous ai mme fourni les
solutions des exercices (qui a dit pas toujours ? ). Aprs ces quelques leons de programmation en Ada, il est temps pour vous de raliser un projet un peu plus palpitant que
de dessiner des rectangles avec des dises (#). Voila pourquoi je vous propose de vous
lancer dans la ralisation dun jeu de d :

Le craps !

109

CHAPITRE 10. [TP] LE CRAPS


Il ne sagira pas dun exercice comme les prcdents, car celui-ci fera appel de nombreuses connaissances (toutes vos connaissances, pour tre franc). Il sera galement
plus long et devra tre bien plus complet que les prcdents. Songez seulement tous
les cas quil a fallu envisager lorsque nous avons cr notre programme lors du chapitre
sur les conditions. Eh bien il faudra tre aussi exhaustif, si ce nest davantage.
Rassurez-vous, vous ne serez pas lchez en pleine nature : je vais vous guider. Tout
dabord, nous allons tablir les rgles du jeu et le cahier des charges de notre programme. Puis nous rglerons ensemble certains problmes techniques que vous ne pouvez rsoudre seuls (le problme du hasard notamment). Pour ceux qui auront des
difficults structurer leur code et leur pense, je vous proposerai un plan type. Et
finalement, je vous proposerai une solution possible et quelques ides pour amliorer
notre jeu.

Les rgles du craps


Bon, avant de nous lancer tte baisse, il serait bon de dfinir ce dont on parle. Le
craps se joue avec 2 ds et nous ne nous intressons pas aux faces des ds mais leur
somme ! Si vos ds indiquent un 5 et un 3 alors vous avez obtenu 5+3=8 !Le joueur
mise une certaine somme, puis il lance les ds. Au premier lancer, on dit que le point
est off.
Si le joueur obtient 7 (somme la plus probable) ou 11 (obtenu seulement avec 6
et 5), il remporte sa mise. (Sil avait mis 10$, alors il gagne 10$)
Sil obtient des craps (2, 3 ou 12) alors il perd sa mise.
Sil obtient 4, 5, 6, 8, 9 ou 10, alors le point passe on et les rgles vont changer.
Une fois que le point est on, le joueur doit parvenir refaire le nombre obtenu (4, 5,
6, 8, 9 ou 10). Pour cela, il peut lancer le d autant de fois quil le souhaite, mais il ne
doit pas obtenir de 7 !
Sil parvient refaire le nombre obtenu, le joueur remporte sa mise.
Sil obtient 7 avant davoir refait son nombre, le joueur perd sa mise. On peut
alors hurler : SEVEN OUT ! Hum. . . dsol.
Ce sont l des rgles simplifies car il est possible de parier sur les issues possibles (le
prochain lancer est un 11, le 6 sortira avant le 7, le prochain lancer sera un craps. . .)
permettant de remporter jusqu 15 fois votre mise. Mais nous nous tiendrons pour
linstant aux rgles cites au-dessus.

Cahier des charges


Un programmeur ne part pas laventure sans avoir pris connaissances des exigences
de son client, car le client est roi (et ici, le client cest moi ). Vous devez avoir en tte
les diffrentes contraintes qui vous sont imposes avant de vous lancer :
110

SIMULER LE HASARD (OU PRESQUE)


Tout dabord, les mises se feront en $ et nous naccepterons quun nombre entiers
de dollars (pas de 7$23, on nest pas la boulangerie !)
Le joueur doit pouvoir continuer jouer tant quil na pas atteint les 0$.
Un joueur ne peut pas avoir - 8$ ! ! ! Scores ngatifs interdits. Et pour cela, il
serait bon que le croupier vrifie que personne ne mise plus quil ne peut perdre
(autrement dit, pas de mise sans vrification).
Le joueur ne peut pas quitter le jeu avant que sa mise ait t soit perdue soit
gagne. En revanche, il peut quitter la partie en misant 0$.
Laffichage des rsultats devra tre lisible. Pas de :
+5

mais plutt :
Vous avez gagn 5 $ . Vous avez maintenant 124 $ .

Le programme devra indiquer clairement quel est le point (off ou on, et dans
ce cas, quel est le nombre ?), si vous avez fait un CRAPS ou un SEVEN OUT,
si vous avez gagn, quelle somme vous avez gagn/perdu, ce quil vous reste, les
faces des ds et leur somme. . . Bref, le joueur ne doit pas tre dans linconnu.
Exemple :
Vous avez encore 51 $ . Combien misez vous ? 50
Le premier d donne 1 et le second donne 2. Ce qui nous
fait 3.
C est un CRAPS . Vous avez perdu 50 $ , il ne vous reste
plus que 1 $ .

Votre procdure principale devra tre la plus simple possible, pensez pour cela
utiliser des fonctions et des sous-procdures. Vous tes autoriss pour loccasion
utiliser des variables en mode OUT ou IN OUT.
Votre code devra comporter le moins de tests possibles : chaque fois que vous
testez une galit, vous prenez du temps processeur et de la mmoire, donc soyez
conomes.
Compris ? Alors il nous reste seulement un petit dtail technique rgler : comment
simuler un lancer de d ? (Oui, je sais, ce nest pas vraiment un petit dtail).

Simuler le hasard (ou presque)


Je vous transmets ci-dessous une portion de code qui vous permettra de gnrer un
nombre entre 1 et 6, choisi au hasard (on dit alatoirement).
1

WITH Ada . Numerics . Discrete_Random ;

2
3

PROCEDURE Craps IS

111

CHAPITRE 10. [TP] LE CRAPS


4
5
6
7

SUBTYPE Intervalle IS Integer RANGE 1 .. 6 ;


PACKAGE Aleatoire IS NEW Ada . Numerics . Discrete_Random (
Intervalle ) ;
USE Aleatoire ;

8
9

Hasard

: Generator ;

10
11

...

Comme il est crit ci-dessus, vous aurez besoin du package


Ada.Numerics.Discrete_Random mais vous ne devrez pas crire linstruction USE Ada.Numerics.Discrete_Random !

Bah, pourquoi ?

Malheureusement, je ne peux pas vous expliquer ce code pour linstant car il fait appel
des notions sur les packages, lhritage, la gnricit. . . que nous verrons bien plus
tard. Vous naurez donc qu effectuer un copier-coller. En revanche, je peux vous
expliquer la toute dernire ligne : pour gnrer un nombre alatoire , lordinateur a
besoin dune variable appele gnrateur ou germe. Cest partir de ce gnrateur que
le programme crera de nouveaux nombres. Cest l le sens de cette variable Hasard
(de type generator).
Mais vous devrez initialiser ce gnrateur au dbut de votre procdure principale une
et une seule fois ! Pour cela, il vous suffira dcrire :
1
2
3

BEGIN
reset ( Hasard ) ;
...

Par la suite, pour obtenir un nombre compris entre 1 et 6 alatoirement, vous naurez
crire :
1

MaVariable := random ( Hasard ) ;

Et pourquoi pas une fonction qui renverrait directement un nombre entre 2


et 12 ?
Autant, vous avez les mme chances dobtenir 1, 2, 3, 4, 5 ou 6 en lanant un d (on
dit quil y quiprobabilit) autant vous navez pas la mme chance de faire 7 et 2
avec deux ds. En effet, pour avoir 7, vous pouvez faire 1/6, 2/5, 3/4, 4/3, 5/2 ou 6/1
tandis que pour avoir 2, vous ne pouvez faire que 1/1 ! Or nous voulons que notre jeu
soit alatoire (ou presque, je ne rentre pas dans les dtails mathmatiques), donc nous
112

UN PLAN DE BATAILLE
devrons simuler deux ds et en faire la somme.

Un plan de bataille
Comme promis, pour ceux qui ne sauraient pas comment partir ou qui seraient dcourags, voici un plan de ce que pourrait tre votre programme.
Vous devez rflchir aux fonctions essentielles : que fait un joueur de CRAPS ? Il joue
tant qu il a de largent. Il commence par parier une mise (sil mise 0, cest quil
arrte et quil part avec son argent) puis il attaque la premire manche (la seconde ne
commenant que sous certaines conditions, on peut considrer pour linstant quil ne
fait que jouer la premire manche). Voil donc un code sommaire de notre procdure
principale :
1

RESET ( hasard ) ;

2
3
4
5
6
7
8
9

TANT QUE argent /= 0


|
PARIER
|
SI mise = 0
|
|
ALORS STOP
|
FIN DU SI
|
JOUER LA PREMI RE MANCHE
FIN DE BOUCLE

vous de le traduire en Ada. Vous devriez avoir identifier une boucle WHILE, un IF
et un EXIT. Vous devriez galement avoir compris que nous aurons besoin de deux
fonctions/procdures PARIER et JOUER LA PREMIRE MANCHE (nom revoir
bien entendu). La procdure/fonction PARIER se chargera de demander la mise dsire
et de vrifier que le joueur peut effectivement parier une telle somme :
1
2
3

TANT QUE mise trop importante


|
DEMANDER mise
FIN DE BOUCLE

La fonction/procdure JOUER LA PREMIRE MANCHE se contentera de lancer les


ds, de regarder les rsultats et den tirer les conclusions.
1
2
3
4
5
6
7

LANCER d s
SI les d s valent
|
7 ou 11
=>
|
2 , 3 ou 12
=>
|
autre chose = >
|
FIN DE SI

on gagne
on perd
point := on
MANCHE2 avec valeur du point

Il est possible de raliser une fonction/procdure LANCER qui jettera les ds et


affichera les scores ou bien on pourra crire le code ncessaire dans la fonction/procdure JOUER LA PREMIRE MANCHE. Intressons-nous la fonction/procdure
JOUER LA DEUXIEME MANCHE :
113

CHAPITRE 10. [TP] LE CRAPS


1
2
3
4
5
6

TANT QUE point = on


|
SI les d s valent
|
|
7
= > on perd
|
|
point = > on gagne
|
FIN DE SI
FIN DE BOUCLE

Voil dcompos la structure des diffrentes fonctions/procdures que vous devrez rdiger. Bien sr, je ne vous indique pas si vous devez choisir entre fonction ou procdure,
si vous devez transmettre des paramtres, afficher du texte, choisir entre IF et CASE
. . . Je ne vais pas non plus tout faire.

Une solution
Je vous fournis ci-dessous le code dune solution possible ce TP, ce nest pas la seule
bien sr :
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO , Ada . Numerics .


Discrete_Random ;
USE Ada . Text_IO , Ada . Integer_Text_IO ;

3
4

PROCEDURE Craps IS

5
6
7
8

-- - - - - - - - - - - - - - - - - - - - - - -- PACKAGES N CESSAIRES - -- - - - - - - - - - - - - - - - - - - - - - -

9
10
11
12

SUBTYPE Intervalle IS Integer RANGE 1 .. 6 ;


PACKAGE Aleatoire IS NEW Ada . Numerics . Discrete_Random (
Intervalle ) ;
USE Aleatoire ;

13
14
15
16

-- - - - - - - - - - - - - - - - - - - - - - - - - -- FONCTIONS ET PROC DURES - -- - - - - - - - - - - - - - - - - - - - - - - - - -

17
18
19
20
21
22
23
24
25
26
27
28
29

114

PROCEDURE Lancer (
D1
:
OUT Natural ;
D2
:
OUT Natural ;
Hasard :
Generator ) IS
BEGIN
D1 := Random ( Hasard ) ;
D2 := Random ( Hasard ) ;
Put ( "
Vous obtenez un " ) ; Put ( D1 , 2 ) ;
Put ( " et un " ) ; Put ( D2 , 2 ) ;
put ( " . Soit un total de " ) ; put ( D1 + D2 , 4 ) ; put_line ( " ! "
) ;
END Lancer ;

UNE SOLUTION
30
31
32
33
34
35
36
37
38
39
40
41
42
43

function parier ( argent : natural ) return natural is


mise : natural := 0 ;
begin
Put ( "
Vous disposez de " ) ; Put ( argent ) ; put_line ( " $ . "
) ;
while mise = 0 loop
Put ( "
Combien souhaitez - vous miser ? " ) ;
Get ( mise ) ; skip_line ;
if mise > argent
then Put_line ( "
La maison ne fait pas credit ,
Monsieur . " ) ; mise := 0 ;
else exit ;
end if ;
end loop ;
return mise ;
end parier ;

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

procedure Manche2 ( Hasard : generator ; Mise : in out natural


; Argent : in out natural ; Point : in out natural ) is
D1 , D2 : Natural ;
-- D1 et D2 indiquent les
faces du d 1 et du d 2
begin
while point > 0 loop
Lancer ( D1 , D2 , Hasard ) ;
if D1 + D2 = Point
then Put ( "
Vous empochez " ) ; Put ( Mise , 1 ) ;
put ( " $ . Ce qui vous fait " ) ;
Argent := Argent + Mise ; point := 0 ; mise :=
0 ;
Put ( Argent , 1 ) ; Put_line ( " $ . Bravo . " ) ;
new_line ;
elsif D1 + D2 = 7
then Put ( "
Seven Out ! Vous perdez " ) ; Put (
Mise , 1 ) ; put ( " $ . Plus que " ) ;
Argent := Argent - Mise ; point := 0 ; mise :=
0 ;
Put ( Argent , 1 ) ; Put_line ( " $ . Dommage . " ) ;
new_line ;
end if ;
end loop ;
end Manche2 ;

62
63
64
65
66

procedure Manche1 ( Hasard : generator ; Mise : in out natural


; Argent : in out natural ) is
D1 , D2 : Natural ;
-- D1 et D2 indiquent les
faces du d 1 et du d 2
Point
: Natural
:= 0 ;
--0 pour off , les autres
nombres indiquant que le point est on
begin

115

CHAPITRE 10. [TP] LE CRAPS


Lancer ( D1 , D2 , Hasard ) ;
CASE D1 + D2 IS
WHEN 7 | 11
= > Put ( "
Vous empochez " ) ; Put (
Mise , 1 ) ; put ( " $ . Ce qui vous fait " ) ;
Argent := Argent + Mise ; Mise := 0
;
Put ( Argent , 1 ) ; Put_line ( " $ . Bravo
. " ) ; new_line ;
WHEN 2 | 3 | 12 = > Put ( "
CRAPS ! Vous perdez " ) ;
Put ( Mise , 1 ) ; put ( " $ . Plus que " ) ;
Argent := Argent - Mise ; Mise := 0
;
Put ( Argent , 1 ) ; Put_line ( " $ .
Dommage . " ) ; new_line ;
WHEN OTHERS
= > Point := D1 + D2 ;
Put ( "
Le point est etabli a " )
; Put ( point , 5 ) ; New_line ;
Manche2 ( Hasard , Mise , Argent , Point ) ;
END CASE ;
end Manche1 ;

67
68
69
70
71
72
73
74
75
76
77
78
79
80

-- - - - - -- --- --- VARIABLES - -- - - - - -- --- --

81
82
83
84

Hasard : Generator ;
-- Ce generator nous servira
simuler le hasard
Argent
: Natural := 100 ; -- On commence avec 100$
Mise
: Natural
:= 0 ;
-- montant mis , on l ' initialise
0 pour d buter .

85
86
87
88

-- - - - - - - - - - - - - - - - - - - - - - -- PROC DURE PRINCIPALE - -- - - - - - - - - - - - - - - - - - - - - - BEGIN

89
90
91
92
93

Reset ( Hasard ) ;
Put_Line ( "

94
95

Bienvenue au CRAPS ! ! ! " ) ; New_Line ;

96

while argent > 0 loop


Mise := Parier ( argent ) ;
exit when Mise = 0 ;
Put_line ( "
Les jeux sont faits ! " ) ;
Manche1 ( Hasard , Mise , Argent ) ;
new_line ;
END LOOP ;

97
98
99
100
101
102
103
104

Put ( " Vous repartez avec " ) ; Put ( Argent , 5 ) ; put ( " $ ! " ) ;

105
106
107

END Craps ;

116

UNE SOLUTION
Voici quelques pistes damlioration, certaines ntant ralisables quavec davantage de
connaissances. vous de trouver vos ides, de bidouiller, de tester. . . cest comme a
que vous progresserez. Nayez pas peur de modifier ce que vous avez fait !

En rsum
Pistes damlioration :
Prendre en compte tous les types de paris. Pour davantage de dtails, aller voir les
rgles sur ce site : http://www.argentgagnant.com/craps/jeu-craps.html.
Prendre en compte un parieur extrieur, un second joueur.
Enregistrer les meilleurs scores dans un fichier (pour plus tard)
Proposer un systme de mdaille : celui qui quitte la partie avec moins de 100$
aura la mdaille de rookie , plus de 1000$ la mdaille du Dieu des ds . . .
vous dinventer diffrents sobriquets.
Et que pourrait faire le joueur de tout cet argent gagn ? Sacheter une bire, une
moto, un cigare ? On peut imaginer un systme de rcompense.

117

CHAPITRE 10. [TP] LE CRAPS

118

Troisime partie

Ada, les types composites

119

Chapitre

11

Les tableaux
Difficult :
Comme prvu, nous allons aborder dans ce chapitre un nouveau type de donnes, les
tableaux ou ARRAY en Ada. Ce sera pour nous loccasion daborder et dexpliquer ce que
sont les types composites. Puis nous nous concentrerons sur notre premier type composite,
les tableaux. quoi cela ressemble-t-il en Ada ? Quel intrt faire des tableaux ? Comment
faire des tableaux uni-, bi-, tridimensionnels ? Ce sera galement loccasion de rinvestir ce
que nous avons vu dans la seconde partie, notamment sur les boucles.

121

CHAPITRE 11. LES TABLEAUX

Les types composites, cest quoi ?


Nayez crainte, vous pourrez continuer suivre ce tutoriel, mme si vous navez pas
bien compris ce quest un type composite : toute la partie III y est consacre. En fait,
disons que si les types composites vont nous ouvrir de nombreuses portes qui nous
taient pour linstant fermes, elle ne va pas pour autant rvolutionner notre faon de
coder ni remettre en cause ce que nous avons dj vu.
Pour mieux comprendre, souvenons-nous de notre TP sur le craps. Nous avions beaucoup de variables manipuler et donc transmettre chaque procdure ou fonction.
Par exemple, il nous fallait une variable D1, une variable D2, une variable Hasard et
peut-tre mme une variable SommeDs. Il aurait t plus pratique de ne manipuler
quune seul variable, appele MainJoueur ou Joueur, qui aurait contenu ces 4 variables
(figure 24.1). De mme une variable composite Bourse aurait pu grer la mise et la
somme en dollars dont disposait le joueur.

Figure 11.1 Les quatre variables sont runies en une seule


Pour schmatiser, les types composites ont pour but de nous faire manipuler des objets
plus complexes que de simples variables. Prenons un autre exemple, nous voudrions
crer un programme qui cre des fentres et permet galement de leur donner un
titre, de les agrandir ou rtrcir. . . dans loptique de le rutiliser plus tard pour crer
des programmes plus ambitieux. Nous devrons alors crer notamment les variables
suivantes :
X, Y : position de la fentre sur lcran
Long,Larg : longueur et largeur de la fentre
pais : paisseur des bords de la fentre
Titre : le titre de la fentre
Rfond, Vfond, Bfond : trois variables pour dterminer la couleur du fond de la
fentre (R :rouge ; V : vert ; B : bleu)
Actif : une variable pour savoir si la fentre est slectionne ou non
122

TABLEAUX UNIDIMENSIONELS
Reduit : une variable pour savoir si la fentre est rduite dans la barre des tches
ou non
...
Eh oui, a devient complexe de grer tout a ! Et vous avez du vous rendre compte
lors du TP que cela devient de plus en plus compliqu de grer un grand nombre
de variables (surtout quand les noms sont mal trouvs comme ci-dessus). Il serait plus
simple de dfinir un type composite T_Fentre et de ne manipuler quune seule variable
F. Imaginons galement la tte de nos fonctions :
1
2
3
4
5
6
7
8
9
10
11

CreerFenetre ( X = > 15 ,
Y = > 20 ,
Long = > 300 ,
Larg = > 200 ,
Epais = > 2 ,
Titre = > " Ma fen tre " ,
Rfond = > 20 ,
Vfond = > 20 ,
Bfond = > 20 ,
Actif = > true ,
Reduit = > false ...)

Sans compter que, vous mis part, personne ne connat le nom et la signification des
paramtres moins de navoir lu tout le code (sans moi :- ). Imaginez galement le
chantier si lon dsirait crer une deuxime fentre (il faudra trouver de nouveaux noms
de variable, Youpiiii !) Il serait bien plus simple de navoir qu crire :
1
2

CreerFenetre ( F ) ;
ChangerNom (F , " Ma fen tre " ) ; ...

Nous naurions pas nous soucier de tous ces paramtres et de leur nom. Cest l tout
lintrt des types composites : nous permettre de manipuler des objets trs compliqus
de la manire la plus simple possible.

Tableaux unidimensionels
Problme
Le premier type composite que nous allons voir est le tableau, et pour commencer le
tableau unidimensionnel, appel ARRAY en Ada.
Quel est lintrt de crer des tableaux ? Pas la peine de se compliquer la vie !

Les tableaux nont pas t crs pour nous compliquer la vie . Comme toute chose
en informatique, ils ont t invents pour faire face des problmes concrets.
123

CHAPITRE 11. LES TABLEAUX


Voici un exemple : en vue dun jeu (de d, de carte ou autre) 6 joueurs, on a besoin
denregistrer les scores des diffrents participants. Do le code suivant :
1

...

ScoreJoueur1
ScoreJoueur2
ScoreJoueur3
ScoreJoueur4
ScoreJoueur5
ScoreJoueur6
BEGIN
ScoreJoueur1
ScoreJoueur2
ScoreJoueur3
ScoreJoueur4
ScoreJoueur5
ScoreJoueur6

2
3
4
5
6
7
8
9
10
11
12
13
14

:
:
:
:
:
:

natural
natural
natural
natural
natural
natural

:=
:=
:=
:=
:=
:=

:=
:=
:=
:=
:=
:=

0
0
0
0
0
0

;
;
;
;
;
;

15 ;
45 ;
27 ;
8 ;
19 ;
27 ;

15
16
17
18
19
20
21
22

...

get ( ScoreJoueur1 )
get ( ScoreJoueur2 )
get ( ScoreJoueur3 )
get ( ScoreJoueur4 )
get ( ScoreJoueur5 )
get ( ScoreJoueur6 )

;
;
;
;
;
;

skip_line
skip_line
skip_line
skip_line
skip_line
skip_line

;
;
;
;
;
;

Un peu long et redondant, dautant plus quil vaut mieux ne pas avoir besoin de rajouter
ou denlever un joueur car nous serions obligs de revoir tout notre code ! Il serait
judicieux, pour viter de crer autant de variables, de les classer dans un tableau de la
manire suivante :
15

45

27

19

27

Chaque case correspond au score dun joueur, et nous navons besoin que dune seule
variable composite : Scores de type T_tableau. On dit que ce tableau est unidimensionnel car il a plusieurs colonnes mais une seule ligne !

Cration dun tableau en Ada


Dclaration
Je vous ai dit quun tableau se disait ARRAY en Ada. Mais il serait trop simple
dcrire :

124

Scores : array ;

TABLEAUX UNIDIMENSIONELS
Car il existe plusieurs types de tableaux unidimensionnels : tableaux dinteger, tableaux
de natural, tableaux de float, tableau de boolean, tableau de character. . . Il faut donc
crer un type prcis ! Et cela se fait de la manire suivante :
1

type T_Tableau is array of natural ;

Attention ! Ce code est insuffisant ! ! ! Il faut encore lindexer. Pour lheure,


personne ne peut dire partir de ce code combien de cases contient un objet
de type T_Tableau, ni si la premire case est bien la numro 1 !

HEIN ? ! ? Quest-ce quil raconte l ?

Non je ne suis pas en plein dlire, rassurez-vous. En fait, nous avons oubli lorsque
nous avons crit le tableau de numroter les cases. Le plus vident est de le numroter
ainsi :
n1
15

n2
45

n3
27

n4
8

n5
19

n6
27

Il faudra donc crire le code suivant :


1
2

Type T_Tableau is array ( 1 .. 6 ) of integer ;


Scores : T_Tableau ;

Mais le plus souvent les informaticiens auront lhabitude de numroter ainsi :


n0
15

n1
45

n2
27

n3
8

n4
19

n5
27

Il faudra alors changer notre code de la faon suivante :


1
2

Type T_Tableau is array ( 0 .. 5 ) of integer ;


Scores : T_Tableau ;

La premire case est donc la numro 0 et la 6me case est la numro 5 ! Cest un peu
compliqu, je sais, mais je tiens lvoquer car beaucoup de langages numrotent ainsi
leurs tableaux sans vous laisser le choix (notamment le fameux langage C ). Bien sr,
en Ada il est possible de numroter de 1 6, de 0 5, mais aussi de 15 20, de 7
12. . . il suffit pour cela de changer lintervalle crit dans les parenthses.
Dernier point, il est possible en Ada dutiliser des variables pour dclarer la taille dun
tableau, sauf que ces variables devront tre. . . constantes !
1
2
3

TailleMax : constant natural := 5 ;


Type T_Tableau is array ( 0 .. TailleMax ) of integer ;
Scores : T_Tableau ;

125

CHAPITRE 11. LES TABLEAUX


Il vous sera donc impossible de crer un tableau de taille variable (pour linstant tout
du moins).
Affectation globale
Maintenant que nous avons dclar un tableau dentiers (Scores de type T_Tableau),
encore faut-il lui affecter des valeurs. Cela peut se faire de deux faons distinctes.
Premire faon, la plus directe possible :
1

Scores := ( 15 , 45 , 27 ,8 , 19 , 27 ) ;

Simple, efficace. Chaque case du tableau a dsormais une valeur. Il est galement possible dutiliser des agrgats. De quoi sagit-il ? Le plus simple est de vous en rendre
compte par vous-mme sur un exemple. Nous voudrions que les cases n1 et n3 valent
17 et que les autres valent 0. Nous pouvons crire :
1

Scores := ( 17 ,0 , 17 ,0 ,0 , 0 ) ;

Ou bien utiliser les agrgats :


1

Scores := ( 1 | 3 = > 17 , others = > 0 ) ;

Autrement dit, nous utilisons une sorte de CASE pour dfinir les valeurs de notre tableau.
Autre possibilit, dfinir les trois premires valeurs puis utiliser un intervalle dindices
pour les dernires valeurs.
1

Scores := ( 17 ,0 , 17 , 4 .. 6 = > 0 ) ;

Lennui cest que lon peut tre amen manipuler des tableaux trs trs grands (100
cases), et que cette mthode aura vite ses limites.
Affectation valeur par valeur
Cest pourquoi on utilise souvent la seconde mthode :
1
2
3
4
5
6

Scores ( 1 )
Scores ( 2 )
Scores ( 3 )
Scores ( 4 )
Scores ( 5 )
Scores ( 6 )

:=
:=
:=
:=
:=
:=

15 ;
45 ;
27 ;
8 ;
19 ;
27 ;

Quand nous crivons Scores(1), Scores(2). . . il ne sagit pas du tableau mais bien des
cases du tableau. Scores(1) est la case numro 1 du tableau Scores. Cette case ragit
donc comme une variable de type Integer.
Euh. . . cest pas pire que la premire mthode a ?

126

TABLEAUX UNIDIMENSIONELS
Utilis tel quel, si a lest. Mais en gnral, on la combine avec une boucle. Disons que
nous allons initialiser notre tableau pour le remplir de 0 sans utiliser les agrgats. Voici
la mthode utiliser :
1
2
3

for i in 1 .. 6 loop
T ( i ) := 0 ;
end loop ;

Faites attention ce que votre boucle ne teste pas T(7) si celui-ci est index
de 1 6 ! Comme T(7) nexiste pas, cela pourrait entraner un plantage de
votre programme.
Lintervalle de dfinition dun type T_Tableau est trs important et doit donc tre
respect. Supposons que nous nayons pas utilis une boucle FOR mais une boucle
WHILE avec une variable i servant de compteur que nous incrmenterons nous-mme de
manire tester galement si la case considre ne vaut pas dj 0 :
1
2
3
4

while i <= 6 and T ( i ) /= 0 loop


T ( i ) := 0 ;
i := i + 1 ;
end loop ;

La portion de code ci-dessus est cense parcourir le tableau et mettre zro toutes les
cases non-nulles du tableau. La boucle est cense sarrter quand i vaut 7. Seulement
que se passe-t-il lorsque i vaut 7 justement ? Linstruction if teste si 7 est plus petit
ou gal 6 (Faux) et si T(7) est diffrent de zro. Or T(7) nexiste pas ! Ce code va
donc planter notre programme. Nous devons tre plus prcis et ne tester T(i) que si
la premire condition est remplie. Ca ne vous rappelle rien ? Alors regardez le code
ci-dessous :
1
2
3
4

while i <= 6 and then T ( i ) /= 0 loop


T ( i ) := 0 ;
i := i + 1 ;
end loop ;

Il faut donc remplacer linstruction AND par AND THEN. Ainsi, si le premier prdicat est
faux, alors le second ne sera pas test. Cette mthode vous revient maintenant ? Nous
lavions aborde lors du chapitre sur les boolens. Peux tre sera-t-il temps dy rejeter
un il si jamais vous aviez lu les supplments en diagonale.

Attributs pour les tableaux


Toutefois, pour parcourir un tableau, la meilleure solution est, de loin, lusage de la
boucle FOR. Et nous pouvons mme faire encore mieux. Comme nous pourrions tre
amens modifier la taille du tableau ou sa numrotation, voici un code plus intressant
encore :
1

for i in T ' range loop

127

CHAPITRE 11. LES TABLEAUX


2
3

T ( i ) := 0 ;
end loop ;

Lattribut Trange indiquera lintervalle de numrotation des cases du tableau : 1..6


pour linstant, ou 0..5 si lon changeait la numrotation par exemple. Remarquez que
lattribut sapplique lobjet T et pas son type T_Tableau. De mme, si vous souhaitez afficher les valeurs contenues dans le tableau, vous devrez crire :
1
2
3

for i in T ' range loop


put ( T ( i ) ) ;
end loop ;

Voici deux autres attributs lis aux tableaux et qui devraient vous servir : Tfirst et
Tlast. Tfirst vous renverra le premier indice du tableau T ; Tlast renverra le dernier indice du tableau. Si les indices de votre tableau vont de 1 6 alors Tfirst
vaudra 1 et Tlast vaudra 6. Si les indices de votre tableau vont de 0 5 alors
Tfirst vaudra 0 et Tlast vaudra 5. Lattribut Trange quant lui, quivaut
crire Tfirst..Tlast.
Quel est lintrt de ces deux attributs puisquon a Trange ?

Il peut arriver (nous aurons le cas dans les exercices suivants), que vos boucles aient
besoin de commencer partir de la deuxime case par exemple ou de sarrter avant
la dernire. Auquel cas, vous pourrez crire :
1
2

for i in T ' first + 1 .. T ' last - k loop


...

Dernier attribut intressant pour les tableaux : Tlength. Cet attribut renverra la
longueur du tableau, cest dire son nombre de cases. Cela vous vitera dcrire :
Tlast - Tfirst +1 ! Par exemple, si nous avons le tableau suivant :
n0
54

n1
98

n2
453

n3
45

Les attributs nous renverront ceci :


Attribut
Trange
Tfirst
T(Tfirst)
Tlast
T(Tlast)
Tlength

128

Rsultat
0..4
0
54
4
32
5

n4
32

TABLEAUX MULTIDIMENSIONELS

Tableaux multidimensionels
Tableaux bidimensionnels
Intrt
Compliquons notre exercice : nous souhaitons enregistrer les scores de 4 quipes de
6 personnes. Nous pourrions faire un tableau unidimensionnel de 24 cases, mais cela
risque dtre compliqu de retrouver le score du 5me joueur de le 2me quipe :- Et
cest l quarrivent les tableaux 2 dimensions ou bidimensionnels ! Nous allons ranger
ces scores dans un tableau de 4 lignes et 6 colonnes.
15
41
12
33

45
5
13
56

27
3
14
33

8
11
19
33

19
3
0
42

27
54
20
65

Le 5me joueur de la 2me quipe a obtenu le score 3 (facile retrouver).


Dclaration
La dclaration se fait trs facilement de la manire suivante :
1

type T_Tableau is array ( 1 .. 4 , 1 .. 6 ) of natural ;

Le premier intervalle correspond au nombre de lignes, le second au nombre de colonnes.


Affectation
Laffectation directe donnerait :
1
2
3
4

Scores := (( 15 , 45 , 27 ,8 , 19 , 27 ) ,
( 41 ,5 ,3 , 11 ,3 , 54 ) ,
( 12 , 13 , 14 , 19 ,0 , 20 ) ,
( 33 , 56 , 33 , 33 , 42 , 65 ) ) ;

On voit alors que cette mthode, si pratique avec des tableaux unidimensionnels devient
trs lourde avec une seconde dimension. Quant laffectation valeur par valeur, elle
exigera dutiliser deux boucles imbriques :
1
2
3
4
5

for i in 1 .. 4 loop
for j in 1 .. 6 loop
T (i , j ) := 0 ;
end loop ;
end loop ;

129

CHAPITRE 11. LES TABLEAUX

Tableaux tridimensionnels et plus


Vous serez parfois amens crer des tableaux 3 dimensions ou plus. La dclaration
se fera tout aussi simplement :
1
2

type T_Tableau3D is array ( 1 .. 2 , 1 .. 4 , 1 .. 3 ) of natural ;


type T_Tableau4D is array ( 1 .. 2 , 1 .. 4 , 1 .. 3 , 1 .. 3 ) of natural ;

Je ne reviens pas sur laffectation qui se fera avec trois boucles FOR imbriques, laffectation directe devenant complique et illisible. Ce qui est plus compliqu, cest de
concevoir ces tableaux. Pour une illustration dun tableau tridimensionnel, voir figure
11.2.

Figure 11.2 Tableau tridimensionnel

Notre tableau est en 3D, cest un pav droit (paralllpipde rectangle) dcoup en
cases cubiques. T(1,4,2) est donc le cube qui se trouve dans la premire ligne, dans la
4me colonne et dans la 2me range . Les choses se corsent encore pour un tableau
en 4 dimensions, il faut simaginer avoir une srie de tableaux en 3D (la quatrime
dimension jouant le rle du temps), comme sur la figure 11.3.
T(1,4,1,2) est donc le cube situ la 1re ligne, 4me colonne, 1re range du 2me
pav droit.
130

TABLEAUX MULTIDIMENSIONELS

Figure 11.3 Tableau quatre dimensions

Et mes attributs ?
Cest bien gentil de ne pas revenir sur laffectation avec les boucles imbriques,
mais moi jai voulu remplir un tableau de type T_Tableau3D avec des 0, en
utilisant des attributs, mais je cherche encore !
Nous avons effectivement un souci. T_Tableau3D est un type de tableau avec 2 lignes,
4 colonnes et 3 ranges, donc quand nous crivons Trange cela correspond lintervalle
1..2 ! Pas moyen davoir les intervalles 1..4 ou 1..3 ? Eh bien si ! Il suffit dindiquer
notre attribut de quelle dimension nous parlons. Pour obtenir lintervalle 1..4 il faut
crire Trange(2) (lintervalle de la seconde dimension ou le second intervalle), pour
obtenir lintervalle 1..3 il faut crire Trange(3) (lintervalle de la troisime dimension
ou le troisime intervalle). Donc Trange signifie en fait Trange(1) !
Attribut
Trange(1)
Trange(2)
Trange(3)
Tfirst(1)
Tfirst(2)
Tfirst(3)
Tlast(1)
Tlast(2)
Tlast(3)
Tlength(1)
Tlength(2)
Tlength(3)

Rsultat
1..2
1..4
1..3
1
1
1
2
4
3
2
4
3

131

CHAPITRE 11. LES TABLEAUX

Et mes agrgats ?
Voici une manire dinitialiser un tableau bidimensionnel de 2 lignes et 4 colonnes :
1

T := ( 1 = > (0 ,0 ,0 , 0 ) , 2 = > (0 ,0 ,0 , 0 ) ) ;

On indique que la ligne 1 est (0,0,0,0) puis mme chose pour la ligne 2. On peut aussi
condenser tout cela :
1

T := ( 1 .. 2 = > (0 ,0 ,0 , 0 ) ) ;

Ou encore :
1

T := ( 1 .. 2 = > ( 1 .. 4 = > 0 ) ) ;

Il devient toutefois clair que les agrgats deviendront vite illisibles ds que nous souhaiterons manipuler des tableaux avec de nombreuses dimensions.
Je nai pas dcrit ici toutes les faons deffectuer des affectations laide
dagrgats. Il est galement possible de naffecter une valeur qu certaines
cases et pas dautres. Vous serez peut-tre amens utiliser ces autres
agrgats. Je vous conseille alors de jeter un il au manuel : cliquer sur Help
> Language RM, la section 4.3.3 : Array Aggregates devrait vous donner des
indications. Et comme on dit gnralement sur les forums : RTFM ! (Read
This F***ing Manual = Lisez ce P***** de Manuel !)

Des tableaux un peu moins contraints


Un type non-contraint ou presque
Vous devriez commencer vous rendre compte que les tableaux apportent de nombreux
avantages par rapport aux simples variables. Toutefois, un problme se pose : ils sont
contraints. Leur taille ne varie jamais, ce qui peut tre embtant. Reprenons lexemple
initial : nous voudrions utiliser un tableau pour enregistrer les scores des quipes.
Seulement nous voudrions galement que notre programme nous laisse libre de choisir
le nombre de joueurs et le nombre dquipes. Comment faire ? Pour lheure, la seule
solution est de construire un type T_Tableau suffisamment grand (500 par 800 par
exemple) pour dcourager quiconque de vouloir dpasser ses capacits (il faut avoir du
temps perdre pour se lancer dans une partie avec plus de 500 quipes de plus de 800
joueurs ). Mais la plupart du temps, 80% des capacits seront gches, entranant une
perte de mmoire considrable ainsi quun gchis de temps processeur. La solution est
de crer un type T_Tableau qui ne soit pas prcontraint. Voici une faon de procder
avec un tableau unidimensionnel :
1

type T_Tableau is array ( integer range < >) of natural ;

Notre type T_Tableau est ainsi un peu plus gnrique.


132

DES TABLEAUX UN PEU MOINS CONTRAINTS

Taurais du commencer par l ! Enfin des tableaux quasi infinis !

Attention ! Ici, cest le type T_Tableau qui nest pas contraint. Mais les objets de
type T_Tableau devront toujours tre contraints et ce, ds la dclaration. Celle-ci
seffectuera ainsi :
1
2
3
4

T : T_Tableau ( 1 .. 8 ) ;
-- OU
N : natural := 1065 ;
Tab : T_Tableau ( 5 .. N ) ;

Affectation par tranche


Do lintrt demployer les attributs dans vos boucles afin de disposer de procdures
et de fonctions gnriques (non soumises une taille prdfinie de tableau). Faites
toutefois attention la taille de vos tableaux, car il vous sera impossible dcrire T :=
Tab ; du fait de la diffrence de taille entre vos deux tableaux Tab et T.
Heureusement, il est possible deffectuer des affectations par tranches (slices en Anglais). Supposons que nous ayons trois tableaux dfinis ainsi :
1
2
3

T : T_Tableau ( 1 .. 10 ) ;
U : T_Tableau ( 1 .. 6 ) ;
V : T_Tableau ( 1 .. 4 ) ;

Il sera possible deffectuer nos affectations de la faon suivante :


1
2

T ( 1 .. 6 ) := U ;
T ( 7 .. 10 ) := V ;

Ou mme, avec les attributs :


1
2

T (T ' first .. T ' first +U ' length - 1 ) := U ;


T (T ' first +U ' length .. T ' first +U ' length +V ' length - 1 ) := V ;

Laffectation par tranche nest pas possible pour des tableaux multidimensionnels !

Dclarer un tableau en cours de programme


Mais cela ne rpond toujours pas la question : Comment crer un tableau dont la
taille est dfinie par lutilisateur ? . Pour cela, vous allez devori sortir temporairement
du cadre des tableaux et vous souvenir de ce dont nous avions parl dans le chapitre
133

CHAPITRE 11. LES TABLEAUX


sur les variables, page 4. Lors des complments, nous avions vu le bloc de dclaration. Linstruction DECLARE va nous permettre de dclarer des variables en cours de
programme, et notamment des tableaux. Voici un exemple :
procedure main is
n : Integer ;
type T_Tableau is array ( integer range < >) of integer ;
begin
Put ( " Quelle est la longueur du tableau ? " ) ;
get ( n ) ; skip_line ;

1
2
3
4
5
6
7

declare
tab : T_Tableau ( 1 .. n ) ;
begin

8
9
10
11

-- instructions ne nous int ressant pas

12
13

end ;

14
15
16

end main ;

Ainsi, nous saisissons une variable n, puis avec DECLARE, nous ouvrons un bloc de
dclaration nous permettant de crer notre tableau tab la longueur souhaite. Puis,
il faut terminer les dclarations et ouvrir un bloc dinstruction avec BEGIN. Ce nouveau
bloc dinstructions se terminera avec le END ;.
Mon objet tab disparatra avec linstruction END ; la fin du bloc DECLARE !
Inutile de lutiliser entre END ; et END main; !

Quelques exercices
Voici quelques exercices pour mettre en application ce que nous venons de voir.

Exercice 1
nonc
Crez un programme Moyenne qui saisit une srie de 8 notes sur 20 et calcule la
moyenne de ces notes. Il est bien sr hors de question de crer 8 variables ni dcrire
une srie de 8 additions car le programme devra pouvoir tre modifi trs simplement
pour pouvoir calculer la moyenne de 7, 9, 15. . . notes, simplement en modifiant une
variable !
Solution
134

QUELQUES EXERCICES
1
2

with Ada . Text_IO , Ada . Integer_Text_IO , Ada . Float_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO , Ada . Float_Text_IO ;

3
4
5
6
7
8

procedure Moyenne is
Nb_Notes : constant integer := 8 ;
donnant la taille des tableaux
type T_Tableau is array ( 1 .. Nb_notes ) of float ;
tableau pour enregistrer les notes
Notes : T_Tableau ;
de notes
Moy : float ;
servant enregistrer la moyenne

-- Constante
-- type
-- tableau
-- variable

9
10
11
12
13
14
15
16
17
18
19
20
21
22

function saisie return T_Tableau is


effectuant la saisie des valeurs d ' un tableau
T : T_Tableau ;
begin
for i in T ' range loop
Put ( " Entrez la " ) ; Put (i , 1 ) ;
if i = 1
then put ( " ere note : " ) ;
else put ( " eme note : " ) ;
end if ;
get ( T ( i ) ) ; skip_line ;
end loop ;
return T ;
end saisie ;

-- fonction

23
24
25
26
27
28
29
30
31

function somme ( T : T_Tableau ) return float is


-- fonction
effectuant la somme des valeurs contenues dans un tableau
S : float := 0 . 0 ;
begin
for i in T ' range loop
S := S + T ( i ) ;
end loop ;
return S ;
end somme ;

32
33
34
35
36
37

begin
dure principale
Notes := saisie ;
Moy := Somme ( Notes ) / float ( Nb_Notes ) ;
Put ( " La moyenne est de " ) ; Put ( Moy ) ;
end Moyenne ;

-- Proc

Jai un problme dans laffichage de mon rsultat ! Le programme affiche


1.24000E+01 au lieu de 12.4 !
135

CHAPITRE 11. LES TABLEAUX


Par dfaut, linstruction Put() affiche les nombres flottants sous forme dune criture
scientifique (le E+01 remplaant une multiplication par dix puissance 1). Pour rgler
ce problme, Vous naurez qu crire :
Put ( Moy ,2 ,2 , 0 ) ;

Que signifient tous ces paramtres ? Cest simple, linstruction put() scrit en ralit
ainsi :
Ada.Float_Text_IO.Put(Item => Moy, Fore => 2, Aft => 2, Exp => 0) ;
Linstruction Put utilise ici est celle provenant du package Ada.Float_Text_IO.
Le paramtre Item est le nombre de type float quil faut afficher.
Le paramtre Fore est le nombre de chiffres placs avant la virgule quil faut
afficher (Fore pour Before = Avant). Cela vite dafficher dimmenses blancs avant
vos nombres.
Le paramtre Aft est le nombre de chiffres placs aprs la virgule quil faut
afficher (Aft pour After = Aprs).
Le paramtre Exp correspond lexposant dans la puissance de 10. Pour tous
ceux qui ne savent pas (ou ne se souviennent pas) ce quest une puissance de 10
ou une criture scientifique, retenez quen mettant 0, vous naurez plus de E la
fin de votre nombre.
En crivant Put(Moy,2,2,0) nous obtiendrons donc un nombre avec 2 chiffres avant la
virgule, 2 aprs et pas de E+01 la fin.

Exercice 2
nonc
Rdigez un programme Pascal qui affichera le triangle de Pascal jusquau rang 10. Voici
les premiers rangs :
1
1
1
1
1
1

1
2 1
3 3 1
4 6 4
5 10 10

1
5

Ce tableau se construit de la manire suivante : pour calculer une valeur du tableau,


il suffit dadditionner celle qui se trouve au dessus et celle qui se trouve au-dessus
gauche. Le triangle de Pascal est trs utile en mathmatiques.
Comme on ne peut crer de tableaux triangulaires , on crera un tableau carr
de taille 10 par 10 rempli de zros que lon naffichera pas. On prendra soin dafficher
les chiffres des units les uns en dessous des autres.
136

QUELQUES EXERCICES
Solution
1
2

with Ada . Text_IO , Ada . Integer_Text_IO ;


use Ada . Text_IO , Ada . Integer_Text_IO ;

3
4
5
6
7

procedure Pascal is
Taille : constant integer := 10 ;
type T_Tableau is array ( 1 .. Taille , 1 .. Taille ) of integer ;
Pascal : T_Tableau ;

8
9
10
11
12
13
14

function init return T_Tableau is


T : T_Tableau ;
begin
T := ( others = > 0 )
return T ;
end init ;

15
16
17
18
19
20
21
22
23
24
25
26

function CreerTriangle ( Tab : T_Tableau ) return T_Tableau is


T : T_Tableau := Tab ;
-- comme on ne peut pas modifier
le param tre , on en cr e une copie
Begin
for i in T ' range loop
T (i , 1 ) := 1 ;
-- le premier nombre vaut
toujours 1
for j in 2 .. i loop
-- on remplit ensuite la ligne
partir du deuxi me nombre
T (i , j ) := T (i -1 , j ) + T (i -1 ,j - 1 ) ;
end loop ;
end loop ;
Return T ;
end CreerTriangle ;

27
28
29
30
31
32
33
34
35
36

Procedure Afficher ( T : T_Tableau ) is


begin
for i in T ' range loop
for j in 1 .. i loop
Put ( T (i , j ) ,4 ) ;
end loop ;
new_line ;
end loop ;
end Afficher ;

37
38
39

begin

40
41
42
43

Pascal := init ;
Pascal := CreerTriangle ( Pascal ) ;
Afficher ( Pascal ) ;

44
45

end Pascal ;

137

CHAPITRE 11. LES TABLEAUX

Pourquoi dans ta fonction CreerTriangle tu nas pas crit a ?

1
2
3
4
5
6
7
8

for i in T ' range loop


for j in 1 .. i loop
-- on remplit ensuite la ligne
partir du deuxi me nombre
if i = 1
then T (i , j ) := 1 ;
else T (i , j ) := T (i -1 , j ) + T (i -1 ,j - 1 ) ;
end if ;
end loop ;
end loop ;

Surtout pas ! Car chaque itration (chaque tour de boucle) il aurait fallu faire un test,
or un test cote de la place en mmoire et surtout du temps processeur. Cela aurait fait
perdre beaucoup defficacit notre programme alors que le cas de figure test (savoir
si lon est dans la premire colonne) est relativement simple grer et ne ncessite pas
de condition. De mme pour laffichage, je nai pas test si les nombres valaient 0, il
suffit darrter la boucle avant de les atteindre, et a, a se calcule facilement.
Il est galement possible pour amliorer ce code, de fusionner les fonctions init et
CreerTriangle en une seule, de la manire suivante :
1
2
3
4
5
6
7
8
9
10
11
12
13

function init return T_Tableau is


Begin
for i in T ' range loop
T (i , 1 ) := 1 ;
-- le premier nombre vaut toujours
1
for j in 2 .. i loop
-- on remplit ensuite la ligne
partir du deuxi me nombre
T (i , j ) := T (i -1 , j ) + T (i -1 ,j - 1 ) ;
end loop ;
for j in i + 1 .. T ' last loop -- on compl te avec des 0
T (i , j ) := 0 ;
end loop ;
end loop ;
Return T ;
end init ;

Exercice 3
nonc
Ralisez un programme TriTableau qui cre un tableau unidimensionnel avec des valeurs entires alatoires, laffiche, trie ses valeurs par ordre croissant et enfin affiche le
tableau une fois tri.
Solution
138

QUELQUES EXERCICES
1
2

With ada . Text_IO , Ada . Integer_Text_IO , Ada . Numerics .


Discrete_Random ;
Use ada . Text_IO , Ada . Integer_Text_IO ;

3
4

Procedure TriTableau is

5
6
7

Taille : constant natural := 12 ;


Type T_Tableau is array ( 1 .. Taille ) of integer ;

8
9

-- init renvoie un tableau valeurs al atoires

10
11
12
13
14
15
16
17
18
19
20
21
22
23

function init return T_Tableau is


T : T_Tableau ;
subtype Intervalle is integer range 1 .. 100 ;
package Aleatoire is new Ada . numerics . discrete_random (
Intervalle ) ;
use Aleatoire ;
Hasard : generator ;
begin
Reset ( Hasard ) ;
for i in T ' range loop
T ( i ) := random ( Hasard ) ;
end loop ;
return T ;
end init ;

24
25

-- Afficher affiche les valeurs d ' un tableau sur une m me


ligne

26
27
28
29
30
31
32

procedure Afficher ( T : T_Tableau ) is


begin
for i in T ' range loop
Put ( T ( i ) ,4 ) ;
end loop ;
end Afficher ;

33
34

-- Echanger est une proc dure qui change deux valeurs : a


vaudra b et b vaudra a

35
36
37
38
39
40
41
42

procedure Echanger ( a : in out integer ; b : in out integer )


is
c : integer ;
begin
c := a ;
a := b ;
b := c ;
end Echanger ;

43
44

-- Rangmin cherche la valeur minimale dans une partie d ' un


tableau ; du rang debut au rang fin

139

CHAPITRE 11. LES TABLEAUX


-- Elle ne renvoie pas le minimum mais son rang , son indice
dans le tableau

45
46

function RangMin ( T : T_Tableau ; debut : integer ; fin :


integer ) return integer is
Rang : integer := debut ;
Min : integer := T ( debut ) ;
begin
for i in debut .. fin loop
if T ( i ) < Min
then Min := T ( i ) ;
Rang := i ;
end if ;
end loop ;
return Rang ;
end RangMin ;

47
48
49
50
51
52
53
54
55
56
57
58
59

-- Trier est une fonction qui renvoie un tableau tri du plus


petit au plus grand
-- Principe , elle cherche la valeur minimale du tableau et l '
change avec la premi re valeur
-- puis elle cherche le minimum dans le tableau partir de
la deuxi me valeur et l ' change
-- avec la premi re valeur , puis elle recommence avec la
troisi me valeur ...

60
61
62
63
64

function Trier ( Tab : T_Tableau ) return T_Tableau is


T : T_Tableau := Tab ;
begin
for i in T ' range loop
Echanger ( T ( i ) ,T ( RangMin (T ,i ,T ' last ) ) ) ;
end loop ;
return T ;
end Trier ;

65
66
67
68
69
70
71
72
73

T : T_Tableau ;

74
75
76
77
78
79
80
81
82

begin
T := init ;
Put_line ( " Le tableau genere par l ' ordinateur est le suivant
:") ;
Afficher ( T ) ; New_line ;
Put_line ( " Voici le tableau , une fois trie : " ) ;
Afficher ( Trier ( T ) ) ;
end TriTableau ;

Lexemple que je vous donne est un algorithme de tri par slection. Cest disons lun
des algorithmes de tri les plus barbares. Cest la mthode la plus logique premire
vue, mais sa complexit nest pas vraiment intressante (par complexit, on entend le
nombre doprations et ditrations ncessaires et pas le fait que vous ayez compris ou
140

POUR LES UTILISATEURS DE LA NORME ADA2012


pas). Nous verrons plus tard dautres algorithmes de tri plus efficaces. En attendant,
si ce code vous semble compliqu, je vous invite lire le tutoriel de K-Phoen (http://
www.siteduzero.com/tutoriel-3-213681-le-tri-par-selection.html) sur le tri
par slection : lui, il a cherch le maximum et pas le minimum, mais le principe est le
mme ; en revanche, son code est en C, mais cela peut tre loccasion de voir par vous
mme les diffrences et ressemblances entre lAda et le C.
Nous reviendrons durant la quatrime partie sur la complexit des algorithmes et nous
en profiterons pour aborder des algorithmes de tris plus puissants. Mais avant cela,
vous avez beaucoup de pain sur la planche !

Pour les utilisateurs de la norme Ada2012


Ce que nous venons de voir est valable pour tous les compilateurs, quelle que soit la
norme Ada utilise. En revanche, ce qui va suivre nest valable que pour les compilateurs
intgrant la norme Ada2012. Comme nous lavions dj vu lors du chapitre sur les
conditions, la nouvelle norme internationale du langage Ada, appele Ada2012, sest
inspire des langages fonctionnels. Cest vrai pour les expressions conditionnelles, mais
cest galement vrai pour les boucles. Ainsi, la boucle FOR a t enrichie de nouvelles
fonctionnalits. La boucle FOR que vous connaissez devrait dsormais tre nomme FOR
IN puisque sajoutent dsormais trois nouvelles boucles : FOR OF, FOR ALL et FOR SOME.

Boucle FOR OF
La boucle FOR OF est un vrai coup de pouce pour les programmeurs. Aussi appele
foreach dans bon nombre de langages, va appliquer vos instructions tous les lments
de votre tableau (of = de) sans que vous nayez vous tracasser de leurs indices.
Prenons un exemple :
1
2
3
4
5
6
7
8

Procedure Augmente is
Type T_Tableau is array ( 1 .. 8 ) of integer ;
T : T_Tableau := (7 ,8 ,5 ,4 ,3 ,6 ,9 , 2 ) ;
begin
for E of T loop
E := E + 1 ;
end loop ;
end Augmente ;

Le programme ci-dessus se contente dincrmenter chacune des valeurs dun tableau.


Mais au lieu dutiliser une boucle FOR IN comme dhabitude nous utilisons une boucle
FOR OF. La variable E na pas besoin dtre dclare, elle est automatiquement cre
par la boucle en fonction du type dlments contenus dans le tableau T. Vous devez
comprendre la phrase FOR E OF T ainsi : Pour chaque lment E de T, faire ceci
ou cela . Lnorme avantage, cest que nous ne risquons plus de nous tromper dans
les indices, Ada prend automatiquement chacun des lments du tableau. Et si jamais
vous aviez un doute sur le typage de E, il est possible de rcrire la boucle ainsi :
1

for E : Integer of T loop

141

CHAPITRE 11. LES TABLEAUX


2
3

E := E + 1 ;
end loop ;

Encore mieux : la mme boucle peut sappliquer des tableaux multidimensionnels.


1
2
3
4
5
6
7
8
9

Procedure Augmente is
Type T_Tableau is array ( 1 .. 13 , 1 .. 15 , 1 .. 32 , 1 .. 9754 ) of
integer ;
T : T_Tableau ;
begin
-- Initialisez cet norme tableau vous - m me ; -)
for E of T loop
E := E + 1 ;
end loop ;
end Augmente ;

Comme vous pouvez le constater, le type T_Tableau ci-dessus est vraiment abominable : trs grand et de dimension 4. Normalement, cela nous obligerait imbriquer
quatre boucles FOR IN avec le risque de mlanger les intervalles. Avec la boucle FOR
OF, nous navons aucun changement apporter et aucun risque prendre : Ada2012
gre tout seul.

Expressions quantifies
Les deux autres boucles (FOR ALL et FOR SOME) ne sont utilisables que pour des expressions, appeles expressions quantifies. Autrement dit, elles permettent de parcourir
notre tableau afin dy effectuer des tests.
Quantificateur universel
Premire expression quantifie : la boucle FOR ALL, aussi appele quantificateur universel. Celle-ci va nous permettre de vrifier que toutes les valeurs dun tableau rpondent un critre prcis. Par exemple, nous souhaiterions savoir si tous les lments
du tableau T sont positifs :
1

...

Tous_Positifs : Boolean ;
BEGIN
...
Tous_Positifs := ( FOR ALL i IN T ' range = > T ( i ) > 0 ) ;
...

2
3
4
5
6

Pour que la variable Tous_Positifs vaille TRUE, tous les lments de T doivent tre
positifs. Si un seul est ngatif ou nul, alors la variable Tous_Positifs vaudra FALSE.
Mais ce code peut encore tre amlior en le combinant avec la boucle FOR OF :
1

...

2
3

142

Tous_Positifs : Boolean ;
BEGIN

POUR LES UTILISATEURS DE LA NORME ADA2012


4
5
6

...
Tous_Positifs := ( FOR ALL e OF T = > e > 0 ) ;
...

Lexpression anglaise for all peut se traduire en pour tout ou Quel que soit , ce
qui est un quantificateur en Mathmatiques not . Exprim laide doprateurs boolens, cela quivaudrait : (T(1)>0) AND (T(2)>0) AND (T(3)>0) AND (T(4)>0)
AND ... .
Quantificateur existentiel
Poussons le vice encore plus loin. Tant que tous les lments ne seront pas positifs,
nous incrmenterons tous les lments du tableau. Autrement dit, tant quil existe
un lment ngatif ou nul, on incrmente tous les lments. Cette dernire phrase, et
notamment lexpression il existe , se note de la faon suivante :
1
2
3

( FOR SOME i IN T ' range = > T ( i ) <= 0 ) ;


-- OU
( FOR SOME e OF T = > e <= 0 )

Il sagit l du quantificateur existentiel. Il suffit quun seul lment rponde au


critre nonc pour que lexpression soit vraie. Nous pouvons dsormais complter
notre code :
1
2
3
4
5

WHILE ( FOR SOME e OF T = > e <= 0 ) LOOP


FOR e OF T LOOP
e := e + 1 ;
END LOOP ;
END LOOP ;

Lexpression anglaise for some peut se traduire en pour quelques . Le quantificateur mathmatique correspondant est il existe et se note . Exprim laide doprateurs boolens, cela quivaudrait : (T(1)<=0) OR (T(2)<=0) OR (T(3)<=0) OR
(T(4)<=0) OR ... .
Maintenant que vous connaissez les tableaux, notre prochain chapitre portera sur un
type similaire : les chanes de caractres ou string (en Ada). Je ne saurais trop vous
conseiller de vous exercer lutilisation des tableaux car nous serons amens les
utiliser trs souvent par la suite, et le prochain chapitre ne sen loignera gure. Donc
sil vous reste des questions, des zones dombre, relisez ce cours et effectuez les exercices.
Nhsitez pas non plus vous fixer des projets ou des TP personnels.

En rsum
Pour utiliser des tableaux, vous devez crer un type spcifique en indiquant la
taille, le nombre de dimensions et le type des lments du tableau.
143

CHAPITRE 11. LES TABLEAUX


La manipulation des tableaux se fait gnralement laide de boucles afin de
traiter les divers lments. Vous utiliserez le plus souvent la boucle FOR et, si
votre compilateur est compatible avec la norme Ada2012, la boucle FOR OF.
Un tableau est un type composite contraint. Il peut contenir autant dinformations que vous le souhaitez mais vous devez dfinir sa taille au pralable. Une fois
le tableau dclar, sa taille ne peut plus varier.

144

Chapitre

12

Les chanes de caractres


Difficult :
Aprs les tableaux, voici un nouveau type composite Ada : les chanes de caractres ou
strings. Rentrez les langues les garons, il ny aura rien de sexuel dans ce chapitre. dire
vrai, vous avez dj manipul des strings par le pass (je veux dire, en Ada) et ce chapitre ne
fera que mettre les choses au clair car vous disposez dors et dj de toutes les connaissances
ncessaires.

145

CHAPITRE 12. LES CHANES DE CARACTRES

Prsentation des Chanes de Caractres


Souvenez-vous, vous avez dj manipul des string ou chanes de caractres par le
pass. Notamment lorsque vous criviez :
1

Put ( " Bonjour ! " ) ;

Le texte entre guillemets "Bonjour !" est un string.


Ah. . . cest pas ce que javais pens. . . On va faire tout un chapitre dessus ?

Ce chapitre ne sera pas long. Nous avons toutes les connaissances utiles, il sagit juste de
dcouvrir un type composite que nous utilisons depuis longtemps sans nous en rendre
compte. Quest-ce finalement quune chane de caractres ? Eh bien le string ci-dessus
se rsume en fait ceci :
n1
B

n2
o

n3
n

n4
j

n5
o

n6
u

n7
r

n8

n9
!

Eh oui ! Ce nest rien dautre quun tableau de caractres index partir de 1. Notez
que certains langages comme le clbre C, commencent leurs indices 0 et ajoutent un
dernier caractre la fin : le caractre de fin de texte. Ce nest pas le cas en Ada o le
type String est trs aisment manipulable.

Dclaration et affectation dun string


Nous allons crer un programme ManipString. Nous aurons besoin du package ada.text_io.
De plus, avant de crer des instructions, nous allons devoir dclarer notre string de la
manire suivante :
1
2

with ada . text_io ;


use ada . text_io ;

3
4
5
6
7
8
9

procedure ManipString is
txt : string ( 1 .. 6 ) ;
begin
txt := " Salut ! " ;
put ( txt ) ;
end ManipString

Eh oui ! Un string ntant rien dautre quun tableau de caractres, il est donc contraint :
son nombre de cases est limit et doit tre indiqu lavance. Par consquent, il est
possible de lui affect la chane de caractres "Salut!" mais pas la chane "Salut !"
qui comporte 7 caractres (noubliez pas quun espace est le caractre !). Il est aussi
possible de dclarer notre objet txt tout en lui affectant une valeur initiale :
146

QUELQUES OPRATIONS SUR LES STRINGS


1
2

with ada . text_io ;


use ada . text_io ;

3
4
5
6
7
8

procedure ManipString is
txt : string := " Salut ! " ;
begin
put ( txt ) ;
end ManipString

Lobjet txt est automatiquement index de 1 6.

Quelques oprations sur les strings


Accs une valeur
Comme pour un tableau, il est possible daccder lune des valeurs contenues dans le
string. Par exemple :
1
2

with ada . text_io ;


use ada . text_io ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

procedure ManipString is
txt : string := " Bonjour jules ! " ;
indice : integer ;
begin
put_line ( txt ) ;
for i in txt ' range loop
if txt ( i ) = 'e '
then indice := i ;
end if ;
end loop ;
txt ( indice ) := 'i ' ;
txt ( indice + 1 ) := 'e ' ;
put ( " oups . " ) ;
put ( txt ) ;
end ManipString ;

Ce programme cherche la dernire occurence de la lettre e et la remplace par un i.


Il affichera ainsi :
Bonjour Jules !
Oups . Bonjour Julie !

Remarquez au passage quil est toujours possible dutiliser les attributs.


147

CHAPITRE 12. LES CHANES DE CARACTRES

Accs plusieurs valeurs


Nous voudrions maintenant modifier "Bonjour Jules !" en "Bonsoir Julie !". Cela
risque dtre un peu long de tout modifier lettre par lettre. Voici donc une faon plus
rapide :
1
2

with ada . text_io ;


use ada . text_io ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

procedure ManipString is
txt : string := " Bonjour Jules ! " ;
begin
put_line ( txt ) ;
txt ( 4 .. 7 ) := " soir " ;
for i in txt ' range loop
if txt ( i ) = 'e '
then txt ( i ) := 'i ' ;
txt ( i + 1 ) := 'e ' ;
exit ;
end if ;
end loop ;
put ( " oups . " ) ;
put ( txt ) ;
end ManipString ;

Il est ainsi possible dutiliser les slices (tranches) pour remplacer tout un paquet de
caractres dun coup. Vous remarquerez que jen ai galement profit pour condenser
mon code et supprimer la variable indice. Personne nest parfait : il est rare dcrire
un code parfait ds la premire criture. La plupart du temps, vous ttonnerez et peu
peu, vous parviendrez supprimer le superflu de vos codes.

Modifier la casse
Autre modification possible : modifier la casse (majuscule/minuscule). Nous aurons
besoin pour cela du package Ada.characters.handling. Le code suivant va ainsi crire
"Bonjour" en majuscule et transformer le j de Jules en un J majuscule.
1
2

with ada . text_io , Ada . characters . handling ;


use ada . text_io , Ada . characters . handling ;

3
4
5
6
7
8
9
10
11
12

148

procedure ManipString is
txt : string := " Bonjour jules ! " ;
begin
put_line ( txt ) ;
txt ( 1 .. 7 ) := to_upper ( txt ( 1 .. 7 ) ) ;
for i in txt ' range loop
if txt ( i ) = 'e '
then txt ( i ) := 'i ' ;
txt ( i + 1 ) := 'e ' ;

QUELQUES OPRATIONS SUR LES STRINGS


13
14
15
16
17
18
19

elsif txt ( i ) = 'j '


then txt ( i ) := to_upper ( txt ( i ) ) ;
end if ;
end loop ;
put ( " oups . " ) ;
put ( txt ) ;
end ManipString

La fonction To_Upper() transforme un character ou un string pour le mettre en majuscule. Il existe galement une fonction To_Lower() qui met le texte en minuscule.

Concatnation
Nous allons maintenant crire un programme qui vous donne le choix entre plusieurs
prnom (entre plusieurs strings) et affiche ensuite une phrase de bienvenue. Les diffrents prnoms possibles seront : "Jules", "Paul" et "Frederic". Contrainte supplmentaire : il faut utiliser le moins dinstructions Put() pour saluer notre utilisateur.
1
2

with Ada . Text_IO ;


use Ada . Text_IO ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

procedure ManipString is
txt : string := " Bienvenue , " ;
Nom_1 : string := " Jules . " ;
Nom_2 : string := " Paul . " ;
Nom_3 : string := " Frederic . " ;
choix : integer ;
begin
Put_line ( " Comment vous appelez - vous ? " ) ;
Put_line ( " 1 - " & Nom_1 ) ;
Put_line ( " 2 - " & Nom_2 ) ;
Put_line ( " 3 - " & Nom_3 ) ;
Get ( choix ) ; skip_line ;
case choix is
when 1 = > Put ( txt & Nom_1 ) ;
when 2 = > Put ( txt & Nom_2 ) ;
when others = > Put ( txt & Nom_3 ) ;
end case ;
end ManipString ;

Nous avons utilis le symbole & (appel esperluette) pour effectuer une opration de
concatnation. Cela signifie que lon met bout--bout deux strings (ou plus) pour en
obtenir un plus long.
Attention ! Les strings obtenus aprs ces concatnations ont des longueurs
variables. Vous ne pourrez donc pas crire le code suivant :

case choix is

149

CHAPITRE 12. LES CHANES DE CARACTRES


2
3
4
5

when 1 = > txt := txt & Nom_1 ;


when 2 = > txt := txt & Nom_2 ;
when others = > txt := txt & Nom_3 ;
end case ;

Eh oui ! Le string txt a t initialis avec une longueur de 11 characters. Et comme


tout tableau, sa longueur ne peut tre modifie ou variable. Or txt & Nom_1 a une
longueur de 17 characters (11+6) et txt & Nom_2 a une longueur de 16 (11+5).

Transformer une variable en string et inversement


Transformer une variable en string
Et si au lieu de "Bienvenue ####", je veux afficher une ligne disant
"Vous avez fait le choix n#, vous vous appelez donc ####", je
peux concatner des strings et des variables de types integer ?
Euh. . . non ! Rappelez-vous, je vous ai dit que le langage Ada avait un fort typage,
autrement dit on ne mlange pas les choux et les carottes, les strings et les integer !
Par contre, il y a un moyen de parvenir votre but en respectant ces contraintes de
fort typage, grce aux. . . attributs (eh oui encore) !
1

Put ( " Vous avez fait le choix n " & integer ' image ( choix ) & " ,
vous vous appelez donc " & Nom_1 ) ;

Lattribut integerimage() renvoie un string, une image de lInteger plac entre


parenthses. Bien entendu, cet attribut nest pas rserv au type Integer et sapplique
tous types de variables.
Transformer un string en variable

Et linverse ? Si je veux transformer un string en variable ?

Eh bien tout dpend du type de variable souhait. Si vous voulez une variable du type
integer, alors vous pourrez utilis lattribut Integervalue(). Entre les parenthses,
vous naurez qu crire votre string et lattribut value le transformera en un integer
(ou un natural, ou un float. . .).

Comparaison
Oui, il est possible de comparer des strings ! :- Il est possible de tester leur galit
avec loprateur = ; il est aussi possible dutiliser les oprateurs dordre <, >, <=, >= !
150

CHANES DE CARACTRES NON CONTRAINTES

Et il compare quoi le programme ? Ca veut dire quoi si jcris "Avion" >


"Bus" ?
Les oprateurs dordres utilisent lordre alphabtique et sont insensibles la casse (Majuscule/Minuscule). La comparaison "Avion" > "Bus" devrait donc renvoyer FALSE,
ceci prt que le compilateur ne voudra pas que vous lcriviez tel quel. Il prfrera :
1
2
3
4
5
6

...

txt1 : string := " Avion " ;


txt2 : string := " Bus " ;
BEGIN
if txt1 > txt2
then ...

Saisie au clavier
Et si je souhaite saisir un nom directement au clavier ?

Alors il existe une fonction pour cela : get_line. Elle saisira tout ce que vous tapez au
clavier jusqu ce que vous appuyiez sur Entre (donc pas besoin dutiliser skip_line
par la suite) et renverra ainsi une chane de caractres :
1

txt := get_line ;

La longueur du string renvoy par get_line est inconnue ! Or celle du string


txt est connue et fixe. Si le texte saisi est trop long, vous vous exposez des
erreurs, sil est trop court et que txt na pas t initialis (en le remplissant
de par exemple), vous risquez des erreurs lors de laffichage. Contraignant
nest-ce pas ?

Chanes de caractres non contraintes


Dclarer des strings illimits !
Il existe une faon de grer des chanes de caractres sans tre limit par leur longueur.
Pour cela, vous aurez besoin du package Ada.Strings.Unbounded. Et nous dclarerons
notre variable ainsi :
1

txt : unbounded_string ;

Cela signifie que txt sera une chane de caractre illimite ! ! !


151

CHAPITRE 12. LES CHANES DE CARACTRES

Wouhouh ! Jessaye a tout de suite ! Taurais du commencer par l ! !

Attendez ! Les unbounded_string ne sutilisent pas de la mme manire que les string
normaux ! Simplement parce que leur nature est diffrente. Lorsque vous dclarez un
tableau, par exemple :
1

txt : string ( 1 .. 8 ) ;

Lordinateur va crer en mmoire lquivalent de ceci :


1
?

2
?

3
?

4
?

5
?

6
?

7
?

8
?

Lorsque vous dclarez un unbounded_string comme nous lavons fait tout lheure,
lordinateur crera ceci :
{}
Cest en fait une liste, vide certes mais une liste tout de mme. cette liste, le programme ajoutera des caractres au fur et mesure. Il nest plus question dindices, ce
nest plus un tableau. Nous verrons les listes aprs avoir vu les pointeurs, ce qui vous
clairera sur les limites du procd.

Oprations sur les unbounded_string


Nous pouvons ensuite affecter une valeur nos unbounded_string. Seulement
il est impossible dcrire ceci :

1
2
3
4
5

...

txt : unbounded_string ;
BEGIN
txt := " coucou ! " ;
...

Cela reviendrait affecter un String un objet de type unbounded_string ! Or nous


savons que Ada est un langage fort typage ! Un Unbounded_String ne peut recevoir
comme valeur un string !
Vous devrez donc utiliser les fonctions To_Unbounded_string() et To_String() pour
convertir de la manire suivante :
1

...

2
3
4
5

152

txt : unbounded_string ;
BEGIN
txt := To_Unbounded_String ( " coucou ! " ) ;
put ( To_String ( txt ) ) ;

CHANES DE CARACTRES NON CONTRAINTES


6

...

Notre objet txt aura alors lallure suivante :


{c,o,u,c,o,u, ,!}
Il est toutefois possible doprer des concatnations grce loprateur &. Mais cette
fois, plusieurs oprateurs & ont t crs pour pouvoir concatner :
un string et un string pour obtenir un string
un unbounded_string et un string pour obtenir un unbounded_string
un string et un unbounded_string pour obtenir un unbounded_string
un unbounded_string et un unbounded_string pour obtenir un unbounded_string
un character et un unbounded_string pour obtenir un unbounded_string
un unbounded_string et un character pour obtenir un unbounded_string
Nous pouvons ainsi crire :
1

txt := txt & " Comment allez - vous ? "

Notre objet txt ressemblera alors ceci :


{c,o,u,c,o,u, ,!,C,o,m,m,e,n,t, ,a,l,l,e,
z,-,v,o,u,s, ,? }
Pourquoi tu ne prsentes pas a sous la forme dun tableau, ce serait plus
clair et on naurait pas besoin de compter pour connatre lemplacement dun
caractre ?
Noubliez pas que txt est un unbounded_string, pas un tableau (ni un string) !
Cest une liste, et il nest pas possible dcrire : txt(4) ou txt(1..3) ! Ces critures sont rserves aux tableaux (les strings sont des tableaux ne loublions
pas) et sont donc fausses concernant notre objet txt ! Pour tout vous dire, un
Unbounded_String na accs qu la premire lettre. Celle-ci donne accs
la seconde, qui elle-mme donne accs la troisime et ainsi de suite.
En effet, de nombreuses fonctionnalits lies aux tableaux ne sont plus disponibles.
Voici donc quelques fonctions pour les remplacer :
1

n := length ( txt ) ;
prendra comme valeur la

txt

-- la variable n ( natural )
-- longueur de l ' unbounded_string

3
4
5

char := Element ( txt , 5 ) ;


prendra la 5 me valeur

-- la variable char ( character )


-- de l ' unbounded_string txt

153

CHAPITRE 12. LES CHANES DE CARACTRES


Replace_Element ( txt ,6 , char ) ; -- le 6 me l ment de txt est
remplac par le character char

7
8
9

txt := N u ll _Unbo unded_ Strin g ; -- txt est r initialis en une


liste vide

Une indication tout de mme, si vous ne souhaitez utiliser quune certaine partie de
votre unbounded_string et que vous voudriez crire txt(5..8), vous pouvez vous en
sortir en utilisant To_String() et To_Unbounded_String() de la manire suivante :
1

put ( to_string ( txt ) ( 5 .. 8 ) ) ;

-- OU

3
4
5
6

txt := to _ unbounded_string ( to_string ( txt ) ( 5 .. 8 ) ) ;


put ( to_string ( txt ) ) ;

Comme vous pouvez vous en rendre compte, la manipulation des unbounded_strings


est plus lourde que celle des strings. Voila pourquoi nous prfrerons utiliser en gnral
les strings, nous rserverons lutilisation des unbounded_strings aux cas dextrmes
urgence.
Nous voila la fin de ce chapitre sur les strings et les unbounded_strings. Cela constitue galement un bon exercice sur les tableaux. Mais ne vous croyez pas sortis daffaire :
nous nen avons pas termin avec les tableaux. Les prochains chapitres, consacrs la
programmation modulaire (lutilisation et la cration de packages) ou la manipulation des fichiers, seront loccasion de travailler encore avec les tableaux, les strings
ou les unbounded_strings.

En rsum
Un string nest rien de plus quun tableau unidimensionnel compos de caractres.
Par consquent, si vous dclarer un String, vous devez absolument dfinir sa
taille : il sagit dun type contraint.
Les Strings supportent les tests dgalit et dingalit. Ces derniers comparent
en fait leur classement dans lordre alphabtique. cela sajoute lopration de
concatnation, symbolise par lesperluette &, qui permet de mettre bout bout
deux chanes de caractre.
Prfrez toujours les String pour leur simplicit. Si rellement la contrainte de
taille devient un handicap, optez alors pour un Unbounded_String. Vous aurez
alors besoin du package Ada.Strings.Unbounded.

154

Chapitre

13

La programmation modulaire I : les


packages
Difficult :
Pour ce nouveau chapitre, nous allons revenir sur les tableaux.

Encore des tableaux ? Avec les strings, a va faire le troisime chapitre !

155

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


Rassurez-vous, les tableaux ne constitueront quun prtexte et non pas le coeur de ce
chapitre. Nous avons vu tout ce quil y avait voir sur les tableaux. Mais cela ne doit
pas nous faire oublier leur existence, bien au contraire. Nous allons trs rgulirement
les utiliser. Mais, si vous avez effectu les exercices sur les tableaux, vous avez du vous
rendre compte quil fallait trs rgulirement rcrire les mmes fonctions, les mmes
procdures, les mmes dclarations de type. . .
Se pose alors la question :
Ne serait-il pas possible de mettre tout cela dans un fichier rutilisable
lenvie ?
Et la rponse, vous vous en doutez, est oui ! Ces fichiers, ce sont les fameux packages !

Les fichiers ncessaires


Avant de crer notre premier package, nous allons crer un nouveau programme appel
Test. dans lequel nous allons pouvoir tester toutes les procdures, fonctions, types,
variables que nous allons crer dans notre package. Voici la structure initiale de ce
programme :
1
2

with Integer_Array ;
use Integer_Array ;

procedure Test is
T : T_Vecteur ;
begin

4
5
6
7
8

end Test ;

Cest quoi ce package integer_array et ce type T_Vecteur ?

Ce package integer_array, cest justement le package que nous allons crer et le type
T_Vecteur est un ARRAY OF Integer de longueur 10 (pour linstant). Pour linstant, si lon tente de compiler ce programme, nous nous trouvons face deux obstacles :
Il ny a aucune instruction entre BEGIN et END !
Le package Integer_Array est introuvable (tout simplement parce quil est inexistant)
Nous allons donc devoir crer ce package. Comment faire ? Vous allez voir, cest trs
simple : cliquez sur File > New deux fois pour obtenir deux nouvelles feuilles vierges.
156

NOTRE PREMIRE PROCDURE. . . EMPAQUETE

Deux fois ? On voulait pas crer un seul package ? Y a pas erreur ?

Non, non ! Jai bien dit deux fois ! Vous comprendrez pourquoi ensuite. Sur lune des
feuilles, nous allons crire le code suivant :
1
2

WITH Ada . Integer_Text_IO , Ada . Text_IO ;


USE Ada . Integer_Text_IO , Ada . Text_IO ;

3
4

PACKAGE BODY Integer_Array IS

5
6

END Integer_Array ;

Puis, dans le mme rpertoire que votre fichier Test.adb, enregistrer ce nouveau fichier
sous le nom Integer_Array.adb.
Dans le second fichier vierge, crivez :
1

PACKAGE Integer_Array IS

2
3

END Integer_Array ;

Enfin, toujours dans le mme rpertoire, enregistrer ce fichier sous le nom Integer_Array.ads.
Et voil ! Vous venez de crer votre premier package ! Bon, jadmets quil est un peu
vide, mais nous allons remdier ce dfaut.
Le deuxime fichier DOIT avoir lextension
packages ne marcheront pas.

.ads et pas .adb ! Sinon, vos

Notre premire procdure. . . empaquete


Vous remarquerez que cela ressemble un peu ce que lon a lhabitude dcrire, sauf
quau lieu de crer une procdure, on cre un package.
a me dit toujours pas pourquoi je dois crer deux fichiers qui comportent
peu ou prou la mme chose ? !
Nous y arrivons. Pour comprendre, nous allons crer une procdure qui affiche un
tableau. Vous vous souvenez ? Nous en avons dj crit plusieurs (souvent appeles
Afficher ). Moi, je dcide que je vais lappeler Put() comme cest dusage en Ada.
Euh. . . il existe dj des procdures Put() et elles ne sont pas faites pour les
tableaux ! a sent le plantage a, non ? :-
157

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


Encore une fois, rassurez-vous et faites moi confiance, tout va bien se passer et je vous
expliquerai tout a la fin (Oui, a fait beaucoup de choses inexpliques , dixit
Mulder & Scully ). Dans le fichier Test.adb, nous allons crire :
1
2

WITH Integer_Array ;
USE Integer_Array ;

3
4
5
6
7
8
9

PROCEDURE Test IS
T : T_Vecteur ( 1 .. 10 ) ;
BEGIN
T := ( others = > 0 ) ;
Put (T , 2 ) ;
END Test ;

Je voudrais, en effet, que notre procdure Put() puisse galement grer le nombre
de chiffres affichs, si besoin est. Puis, dans le fichier Integer_Array.ads, nous allons
ajouter ces lignes entre IS et END :
1
2

TYPE T_Vecteur IS ARRAY ( integer range < >) OF Integer ;


PROCEDURE Put ( T : IN T_Vecteur ; Fore : IN Integer := 11 ) ;

Je prends les devants : il ny a aucune erreur, jai bien crit un point-virgule la fin
de la procdure et pas le mot IS. Pourquoi ? Pour la simple et bonne raison que notre
fichier Integer_Array.ads nest pas fait pour contenir des milliers de lignes de codes
et dinstructions ! Il ne contient quune liste de tout ce qui existe dans notre package.
On dit que la ligne PROCEDURE Put (T : IN T_Vecteur; Fore : IN Integer :=
11); est la spcification (ou le prototype) de la procdure Put(). La spcification
de notre procdure ne contient pas les instructions ncessaires son fonctionnement,
il indique seulement au compilateur : voil, jai une procdure qui sappelle Put() et
qui a besoin de ces 2 paramtres ! .
Le contenu de notre procdure (ce que lon appelle le corps ou BODY en Ada), nous
allons lcrire dans notre fichier Integer_Array.adb, entre IS et END. Vous pouvez soit
reprendre lune de vos procdures daffichage dj cres et la modifier, soit recopier la
procdure ci-dessous :
1
2
3
4
5
6
7

PROCEDURE Put ( T : IN T_Vecteur ; Fore : IN Integer := 11 ) IS


BEGIN
FOR I IN T ' RANGE LOOP
Put ( T ( I ) , Fore ) ;
put ( " ; " ) ;
END LOOP ;
END Put ;

Enregistrer vos fichiers, compiler votre programme Test.adb et lancez-le : vous obtenez
une jolie suite de 0 spars par des points virgules. Cela nous fait donc un total de trois
fichiers, dont voici les codes :
Test.adb :
WITH Integer_Array ;

158

NOTRE PREMIRE PROCDURE. . . EMPAQUETE


USE Integer_Array ;
PROCEDURE Test IS
T : T_Vecteur(1..10) ;
BEGIN
T := (others => 0) ;
Put(T,2) ;
END Test ;

Integer_Array.ads :
PACKAGE Integer_Array IS
TYPE T_Vecteur IS ARRAY(Integer range <>) OF Integer ;
PROCEDURE Put (T : IN T_Vecteur; Fore : IN Integer := 11);
END Integer_Array ;

Integer_Array.adb :
WITH Ada.Integer_Text_IO, Ada.Text_IO ;
USE Ada.Integer_Text_IO, Ada.Text_IO ;
PACKAGE BODY Integer_Array IS
PROCEDURE Put(T : IN T_Vecteur ; Fore : IN Integer := 11) IS
BEGIN
FOR I IN TRANGE LOOP
Put(T(I),Fore) ;
put(" ; ") ;
END LOOP ;
END Put ;
END Integer_Array ;

Nous pourrions rsumer la situation par le schma 13.1.


Reste lhistoire de la procdure Put(). La procdure Put() ne constitue pas un mot
rserv du langage, pour lutiliser vous tes obligs de faire appel un package :
Integer_Text_IO pour afficher un Integer ;
Float_text_IO pour afficher un float ;
notre package Integer_Array pour afficher un T_Vecteur.
chaque fois, cest une procdure Put() diffrente qui apparat et ce qui les diffrencie
pour le compilateur, ce sont le nombre et le type des paramtres de ces procdures.
Pour cela, GNAT sappuie sur les spcifications quil trouve dans les fichiers ads. En
cas dhsitation, le compilateur vous demandera de spcifier de quelle procdure vous
parlez en crivant :
Integer_Text_IO.Put() ou
Float_text_IO.Put() ou
Integer_Array.Put().
159

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES

Figure 13.1 Procdure daccs aux instructions des packages

160

VARIABLES ET CONSTANTES GLOBALES

Variables et constantes globales


Je vous ai plusieurs fois dit quune variable ne pouvait exister en dehors de la procdure,
de la fonction ou du bloc de dclaration o elle a t cre. Ainsi, sil est impossible que
deux variables portent le mme nom dans un mme bloc, il est toutefois possible que
deux variables aient le mme nom si elles sont dclares dans des blocs distincts (par
bloc, jentends procdure, fonction ou bloc DECLARE). De mme, je vous ai galement
expliqu que le seul moyen pour quun sous-programme et le programme principal
puissent se transmettre des valeurs, cest en utilisant les paramtres. Eh bien tout cela
est faux.
Quoi ? Mais pourquoi mavoir menti ?

Il ne sagit pas dun mensonge mais dune imprcision. Tout ce que je vous ai dit est
la fois le cas gnral et la mthode quil est bon dappliquer pour gnrer un code de
qualit. Toutefois, il est possible de crer des variables qui outrepassent ces restrictions.
Ce sont ce que lon appelle les variables globales. Il sagit de variables ou constantes
dclares dans un package. Ds lors, tout programme utilisant ce package y aura accs :
elles seront cres au dmarrage du programme et ne seront dtruites qu sa toute fin.
De plus, un sous-programme pourra y faire appel sans quelle ne soit cite dans les
paramtres.
Vous vous demandez srement pourquoi nous navons pas commenc par l ? Vous devez
savoir quutiliser des variables globales est trs risqu et que cest une pratique proscrire autant que possible. Imaginons une fonction itrative (utilisant une boucle) qui
modifierait notre variable dans le but de calculer son rsultat. La fonction se termine,
transmet son rsultat et libre la mmoire quelle avait rquisitionne : ses variables
internes sont alors supprimes. . . mais pas la variable globale qui continue dexister.
Sauf que sa valeur a t modifie par la fonction, et la disparition de cette dernire ne
rinitialisera pas la variable globale. Qui sait alors combien ditrations ont t effectues ? Comment retrouver la valeur initiale pour rtablir un fonctionnement normal
du programme ?
Lusage des variables globales doit donc tre limit aux cas dabsolue ncessit. Si
par exemple, une entreprise dispose dun parc dimprimantes en rseaux, quelques variables globales enregistrant le nombre total dimprimantes ou le nombre dimprimantes
libres seront ncessaires. Et encore, le nombre dimprimantes libres peut tre modifi
par plusieurs programmes successivement ou simultanment ! Cela crera de nombreux
problmes que nous dcouvrirons quand nous aborderons le multitasking au chapitre
31.
Lusage le plus frquent (et que je vous autorise volontiers) est celui des constantes
globales. Celles-ci peuvent vous permettre denregistrer des valeurs que vous ne souhaiter pas rcrire chaque fois afin dviter toute erreur. Il peut sagir par exemple
du titre de votre jeu, des dimensions de la fentre de votre programme ou de nombres
spcifiques comme dans lexemple ci-dessous :
161

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


1
2
3
4
5
6

PACKAGE P_Constantes IS
Pi
: Constant Float
bre nombre Pi
Exp
: Constant Float
constante de Neper
Phi
: Constant Float
nombre d ' or
Avogadro : Constant Float
constante d ' Avogadro
END P_Constantes ;

:= 3 . 14159 ;

-- Le c l

:= 2 . 71828 ;

-- La

:= 1 . 61803 ;

-- Le

:= 6 , 022141 * 10 . 0 ** 23 ; -- La

Et voil quelques fonctions utilisant ces constantes :

1
2
3
4
5
6

WITH P_Constantes ;
USE P_Constantes ;
...
FUNCTION Perimetre ( R : Float ) RETURN Float IS
BEGIN
RETURN 2 . 0 * Pi * R ;
END Perimetre ;

7
8
9
10
11
12

FUNCTION Exponentielle ( x : Float ) RETURN Float IS


BEGIN
RETURN Exp ** x ;
END Exponentielle ;
...

Vous remarquerez que Pi ou Exp nont jamais t dclares dans mes fonctions ni
mme passes en paramtre. Elles jouent ici pleinement leur rle de constante globale :
il serait idiot de calculer le primtre dun cercle avec autre chose que le nombre !
Tu ne cres pas de fichier ads ?

Cest dans ce cas, compltement inutile : mon package ne comporte que des constantes.
Je nai pas de corps dtailler. Les fichiers adb (Ada Body), ne sont utiles que sil faut
dcrire des procdures ou des fonctions. Si le package ne comporte que des types ou
variables globales, le fichier ads (Ada Specifications) suffit. Rappelez-vous le schma de
tout lheure : les programmes cherchent dabord les spcifications dans le fichier ads !
162

TROUVER ET CLASSER LES FICHIERS

Trouver et classer les fichiers


Les packages fournis avec GNAT
O se trouvent les packages Ada ?

Mais alors, si un package nest rien de plus que deux fichiers adb et ads, il
doit bien exister des fichiers ada.text_io.ads, ada.text_io.adb. . ., non ?
En effet. Tous les packages que nous citons en en-tte depuis le dbut de ce cours ont des
fichiers correspondants : un fichier ads et ventuellement un fichier adb. Pour dcouvrir
des fonctionnalits supplmentaires de Ada.Text_IO, Ada.Numerics.Discrete_Random
ou Ada.Strings.Unbounded, il suffit simplement de trouver ces fichiers ads et des les
examiner. Il est inutile douvrir le corps des packages, en gnral peu importe la faon
dont une procdure a t code, ce qui compte cest ce quelle fait. Il suffit donc douvrir
les spcifications, de connatre quelques mots dAnglais et de lire les commentaires qui
expliquent succinctement le rle de tel ou tel sous-programme ainsi que le rle des
divers paramtres.
Pour trouver ces fichiers, ouvrez le rpertoire dinstallation de GNAT. Vous devriez tout
dabord trouver un rpertoire correspondant la version de votre compilateur (lanne
en fait). Ouvrez-le puis suivez le chemin suivant (il est possible que les noms varient
lgrement selon votre systme dexploitation ou la version de votre compilateur) :
lib\gcc\i686-pc-mingw32\4.5.3\adainclude

Convention de nommage

Mais . . . cest un vrai chantier ! Il y a des centaines de fichier et les noms sont
incomprhensibles !
Cest vrai que cest dconcertant au dbut, mais vous parviendrez peu peu vous
retrouver dans cette norme pile de fichiers. Commencez dj par les classer par type
afin de sparer le corps des spcifications. Vous devriez remarquer que, quelques
rares exceptions, tous les noms de fichier commencent par une lettre suivie dun tiret.
Cette lettre est linitiale du premier mot du nom de package. Par exemple le package
Ada.Text_IO a un nom de fichier commenant par a- . La convention est la suivante :
Vous remarquerez ensuite que les points dans Ada.Text_IO ou Ada.Strings.Unbounded
sont remplacs par des tirets dans les noms de fichier. Ainsi Ada.Text_IO a un fichier
nomm a-textio.ads, Ada.Strings.Unbounded a un fichier nomm a-strunb.ads.
163

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


Lettre
a-

Nom complet
Ada

g-

Gnat

i-

Interfaces

s-

System

Remarque
Il sagit des principaux packages auxquels vous ferez appel.
Nous ne ferons que trs rarement appel
ces packages.
Ces packages servent faire interagir les
codes en Ada avec des codes en C, en
Fortran . . .
Nous nutiliserons jamais ces packages.

Organiser nos packages


Ne serait-il pas possible de classer nos packages dans des rpertoires nous
aussi ?
Bien sr que si. Vous verrez que lorsque vos programmes prendront de lampleur, le
nombre de packages augmentera lui-aussi. Il deviendra donc important de classer tous
ces fichiers pour mieux vous y retrouver. Si le logiciel GPS gre seul lensemble du
projet, lIDE Adagide est lui plus rudimentaire : placez par exemple vos packages dans
un sous-rpertoire nomm packages (original non ?). Bien sr, si vous cherchez
compiler, Adagide vous dira quil ne trouve pas vos packages. Il faut donc lui indiquer
o ils se situent. Cliquez sur menu Compile > Select Target, comme la figure 33.6.
Dans la fentre qui souvre, choisissez longlet Debug settings o vous devriez trouver
la ligne Compiler Options. Cest l quil vous faudra inscrire : -I./packages (voir la
figure 33.7). Cette ligne signifie Inclure (-I) le rpertoire ./packages .

Figure 13.2 Slectionner le menu Select Target


Compilez et construisez votre programme : a marche !
164

TROUVER ET CLASSER LES FICHIERS

Figure 13.3 Inscrire ici la commande -I./packages


GNAT devrait crer un fichier appel gnat.ago. Il sera dsormais interdit de
le supprimer car cest lui qui enregistre vos options et donc ladresse o se
situent vos packages.

Petite astuce pour limiter les fichiers gnrs


Vous avez du remarquer quen compilant votre code ou en construisant votre programme, GNAT gnre toute une flope de fichiers aux extensions exotiques : .o, .ali,
.bak, .bk.1, .ago . . . sans parler des copies de vos fichiers sources commenant par b~ !
Et en crant des packages, nous augmentons encore le nombre des fichiers qui seront
gnrs ! Ces fichiers sont utiles pour GNAT et permettent galement dacclrer la
compilation ; mais lorsquils sont trop nombreux, cela devient une perte de temps pour
vous. Voici une astuce pour faire le mnage rapidement.
Ouvrez un diteur de texte et copiez-y les commandes suivantes :
1
2
3
4
5
6
7
8

del
del
del
del
del
del
del
del

*. o
*. ali
*. bk .*
*. bak
*. 0
*. 1
*. 2
*. 3

165

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


9
10
11
12
13
14

del
del
del
del
del
del

*. 4
*. 5
*. 6
*. 7
*. 8
b ~*

Enregistrez ensuite ce fichier texte dans le rpertoire de votre programme en lui donnant
le nom Commande.bat. Dsormais, il suffira que vous double-cliquiez dessus pour quun
grand mnage soit fait, supprimant tous les fichiers dont lextension est note ci-dessus.
Remarquez dailleurs quil ny a pas dinstruction del gnat.ago afin de conserver ce
fichier.

Complter notre package (exercices)


Cahier des charges
Pour lheure, notre package ne contient pas grand chose : le type T_Vecteur et une
procdure Put(). Nous allons donc le complter, ce sera un excellent exercice pour
appliquer tout ce que nous avons vu et cela nous fournira un package bien utile. De
quoi avons-nous besoin ?
Des procdures daffichage
Des procdures de cration
Des fonctions de tri
Des fonctions pour les oprations lmentaires.
Procdures daffichage
1

PROCEDURE Put ( T : IN T_Vecteur ; FORE : IN integer := 11 ) ;

Celle-l ne sera pas complique : nous lavons dj faite. Elle affiche les valeurs contenues
dans un T_Vecteur T sur une mme ligne. Fore indique le nombre de chiffres afficher
(Fore pour Before = Avant la virgule).
1

PROCEDURE Put_line ( T : IN T_Vecteur ; FORE : IN integer := 11 )


;

Affiche les valeurs du T_Vecteur T comme Put() et retourne la ligne.


1

PROCEDURE Put_Column ( T : IN T_Vecteur ; FORE : IN integer := 11


; Index : IN Boolean := true ) ;

Affiche les valeurs du T_Vecteur T comme Put() mais en retournant la ligne aprs
chaque affichage partiel. Le paramtre boolen Index indiquera si lon souhaite afficher
lindice de la valeur affiche (le numro de sa case).
166

COMPLTER NOTRE PACKAGE (EXERCICES)


Procdures de cration
1

PROCEDURE Get ( T : IN OUT T_Vecteur ) ;

Permet lutilisateur de saisir au clavier les valeurs du T_Vecteur T. Laffichage devra


tre le plus sobre possible. Pas de Veuillez vous donner la peine de saisir la 1ere valeur
du T_Vecteur en question :
1

PROCEDURE Init ( T : IN OUT T_Vecteur ; Val : IN integer := 0 ) ;

Initialise un tableau en le remplissant de 0 par dfaut. Le paramtre Val permettra de


complter par autre chose que des 0.
1
2
3

PROCEDURE Generate ( T
: IN OUT T_Vecteur ;
Min : integer := integer ' first ;
Max : integer := integer ' last ) ;

Gnre un T_Vecteur rempli de valeurs alatoires comprises entre Min et Max. Il vous
faudra ajouter le package Ada.Numerics.Discrete_random au dbut de votre fichier
.adb !
Fonctions de tri
1

FUNCTION Tri_Selection ( T : T_Vecteur ) RETURN T_Vecteur ;

Pour lheure, nous navons vu quun seul algorithme de tri de tableau : le tri par slection (ou tri par extraction). Je vous invite donc en faire un petit copier-coller-modifier.
Mais il en existe de nombreux autres. Nous serons donc amens crer plusieurs fonctions de tri, do le nom un peu long de notre fonction. Jai prfr le terme franais
cette fois (Select_Sort ntant pas trs parlant). Peut-tre sera-t-il intressant de crer
galement les fonctions RangMin ou Echanger pour un usage futur ?
Oprations lmentaires

Les oprations ci-dessous seront dtailles ci-dessous dans Vecteurs et calcul


vectoriel.

FUNCTION Somme ( T : T_Vecteur ) RETURN integer ;

Renvoie la somme des lments du T_Vecteur.


1

FUNCTION " + " ( Left , Right : T_Vecteur ) RETURN T_Vecteur ;

Effectue la somme de deux T_Vecteurs. Lcriture trs spcifique de cette fonction


(avec un nom de fonction crit comme un string et exactement deux paramtres Left
et Right pour Gauche et Droite) nous permettra dcrire par la suite T3 := T1 + T2
au lieu de T3 := +(T1,T2) ! On dit que lon surcharge loprateur "+".
1

FUNCTION " * " ( Left : integer ; Right : T_Vecteur ) RETURN


T_Vecteur ;

167

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


Effectue le produit dun nombre entier par un T_Vecteur.
1

FUNCTION " * " ( Left , Right : T_Vecteur ) RETURN Integer ;

Effectue le produit scalaire de deux T_Vecteurs. Pas de confusion possible avec la


fonction prcdente car les paramtres ne sont pas de mme type.
1
2

FUNCTION Minimum ( T : T_Vecteur ) RETURN Integer ;


FUNCTION Maximum ( T : T_Vecteur ) RETURN Integer ;

Renvoient respectivement le plus petit et le plus grand lment dun T_Vecteur.

Solutions
Voici les packages Integer_Array.adb et Integer_Array.ads tels que je les ai crits. Je
vous conseille dessayer de les crire par vous-mme avant de copier-coller ma solution, ce sera un trs bon entranement. Autre conseil, pour les oprations lmentaires,
je vous invite lire la sous-partie qui suit afin de comprendre les quelques ressorts
mathmatiques.
Integer_Array.adb :
WITH Ada.Integer_Text_IO, Ada.Text_IO, Ada.Numerics.Discrete_Random ;
USE Ada.Integer_Text_IO, Ada.Text_IO ;
package body Integer_Array IS
---------------------------Procdures daffichage--------------------------PROCEDURE Put (
T
: IN
T_Vecteur;
Fore : IN
Integer := 11) IS
BEGIN
FOR I IN TRANGE LOOP
Put(T(I),Fore) ;
put(" ; ") ;
END LOOP ;
END Put ;
PROCEDURE Put_Column (
T
: IN
T_Vecteur;
Fore : IN
Integer := 11) IS
BEGIN
FOR I IN TRANGE LOOP
Put(T(I),Fore) ;
new_line ;
END LOOP ;
END Put_Column ;

168

COMPLTER NOTRE PACKAGE (EXERCICES)

PROCEDURE Put_Line (
T
: IN
T_Vecteur;
Fore : IN
Integer := 11) IS
BEGIN
Put(T,Fore) ;
New_Line ;
END Put_line ;
-------------------------Procdures de saisie------------------------PROCEDURE Init (
T
:
OUT T_Vecteur;
Value : IN
Integer := 0) IS
BEGIN
T := (OTHERS => Value) ;
END Init ;
procedure generate(T : in out T_Vecteur ;
min : in integer := integerfirst ;
max : in integer := integerlast) is
subtype Random_Range is integer range min..max ;
Package Random_Array is new ada.Numerics.Discrete_Random(Random_Range) ;
use Random_Array ;
G : generator ;
begin
reset(G) ;
T := (others => random(g)) ;
end generate ;
procedure get(T : in out T_Vecteur) is
begin
for i in Trange loop
put(integerimage(i) & " : ") ;
get(T(i)) ;
skip_line ;
end loop ;
end get ;
---------------------Fonctions de tri--------------------function Tri_Selection(T : T_Vecteur) return T_Vecteur is
function RangMin(T : T_Vecteur ; debut : integer ; fin : integer) return integer
Rang : integer := debut ;
Min : integer := T(debut) ;

169

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


begin
for i in debut..fin loop
if T(i)<Min
then Min := T(i) ;
Rang := i ;
end if ;
end loop ;
return Rang ;
end RangMin ;
procedure Echanger(a : in out integer ; b : in out integer) is
c : integer ;
begin
c := a ;
a := b ;
b := c ;
end Echanger ;
Tab : T_Vecteur := T ;
begin
for i in Tabrange loop
Echanger(Tab(i),Tab(RangMin(Tab,i,Tablast))) ;
end loop ;
return Tab ;
end Tri_Selection ;
----------------------------Oprations lmentaires---------------------------function Somme(T:T_Vecteur) return integer is
S : integer := 0 ;
begin
for i in Trange loop
S:= S + T(i) ;
end loop ;
return S ;
end somme ;
FUNCTION "+" (Left,Right : T_Vecteur) return T_Vecteur is
T : T_Vecteur ;
begin
for i in Leftrange loop
T(i) := Left(i) + Right(i) ;
end loop ;
return T ;
end "+" ;
FUNCTION "*" (Left : integer ; Right : T_Vecteur) return T_Vecteur is
T : T_Vecteur ;

170

COMPLTER NOTRE PACKAGE (EXERCICES)


begin
for i in Rightrange loop
T(i) := Left * Right(i) ;
end loop ;
return T ;
end "*" ;
FUNCTION "*" (Left,Right : T_Vecteur) return Integer is
S : Integer := 0 ;
begin
for i in Leftrange loop
S := S + Left(i) * Right(i) ;
end loop ;
return S ;
end "*" ;
FUNCTION
min :
BEGIN
FOR I
if

Minimum(T : T_Vecteur) return integer is


integer := T(Tfirst) ;

FUNCTION
max :
BEGIN
FOR I
if

Maximum(T : T_Vecteur) return integer is


integer := T(Tfirst) ;

in Trange loop
T(i)<min
then min := T(i) ;
end if ;
end loop ;
return min ;
end Minimum ;

in Trange loop
T(i)>max
then max := T(i) ;
end if ;
end loop ;
return max ;
end Maximum ;
END Integer_Array ;

Integer_Array.ads :
PACKAGE Integer_Array IS
--------------------------------------Dfinition des types et variables-------------------------------------Taille : CONSTANT Integer := 10;
TYPE T_Vecteur IS ARRAY(1..Taille) OF Integer ;

171

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES

---------------------------Procdures daffichage--------------------------PROCEDURE Put (


T
: IN
Fore : IN

T_Vecteur;
Integer := 11);

PROCEDURE Put_Column (
T
: IN
T_Vecteur;
Fore : IN
Integer := 11);
PROCEDURE Put_Line (
T
: IN
T_Vecteur;
Fore : IN
Integer := 11);
-------------------------Procdures de saisie------------------------PROCEDURE Init (
T
:
OUT T_Vecteur;
Value : IN
Integer := 0);
PROCEDURE
T
Min
Max

Generate
: IN OUT
: IN
: IN

(
T_Vecteur;
Integer := IntegerFirst;
Integer := IntegerLast);

PROCEDURE Get (
T : IN OUT T_Vecteur);
---------------------Fonctions de tri--------------------function Tri_Selection(T : T_Vecteur) return T_Vecteur ;
----------------------------Oprations lmentaires---------------------------function Somme(T:T_Vecteur) return integer ;
FUNCTION "+" (Left,Right : T_Vecteur) return T_Vecteur ;
FUNCTION "*" (Left : integer ; Right : T_Vecteur) return T_Vecteur ;
FUNCTION "*" (Left,Right : T_Vecteur) return Integer ;

172

VECTEURS ET CALCUL VECTORIEL (OPTIONNEL)

FUNCTION Minimum(T : T_Vecteur) return integer ;


FUNCTION Maximum(T : T_Vecteur) return integer ;
END Integer_Array ;

Vous remarquerez que jutilise autant que possible les attributs et agrgats. Pensez que
vous serez amens rutiliser ce package rgulirement et srement aussi modifier
la longueur de nos tableaux T_Vecteur. Il ne faut pas que vous ayez besoin de relire
toutes vos procdures et fonctions pour les modifier !

Vecteurs et calcul vectoriel (optionnel)


Quest-ce exactement quun T_Vecteur ?
Depuis le dbut de ce chapitre, nous parlons de T_Vecteur au lieu de tableau
une dimension. Pourquoi ?
Le terme T_Vecteur (ou Vector) nest pas utilis par le langage Ada, contrairement
dautres. Pour Ada, cela reste toujours un ARRAY. Mais des analogies peuvent tre
faites avec lobjet mathmatique appel vecteur . Alors, sans faire un cours de Mathmatiques, quest-ce quun vecteur ?
De manire (trs) schmatique un T_Vecteur est la reprsentation dun dplacement,
symbolis par une flche, comme sur la figure 13.4.

Figure 13.4 Reprsentation de deux vecteurs gaux


~ se dplace de 3 units vers la droite et
Ainsi, sur le schma ci-dessus, le T_Vecteur AB
~
de 1 unit vers le haut. Il reprsente donc le mme dplacement que le T_Vecteur CD
173

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES


~ = CD
~ . On remarquera que ces vecteurs sont parallles , de
et on peut crire AB
mme longueur et dans le mme sens.
 Du coup, on peut dire que ces deux vecteurs
3
ont comme coordonnes : (3 ; 1) ou
, ce qui devrait commencer vous rappeler
1
nos tableaux, non ? Pour les rcalcitrants, sachez quil est possible en Mathmatiques
de travailler avec des vecteurs en 3, 4, 5. . . dimensions. Les coordonnes dun vecteur
ressemblent alors cela :

3
1

(3 ; 1 ; 5 ; 7 ; 9) ou
5
7
9
a ressemble sacrment nos tableaux T_Vecteurs, non ?

Calcul vectoriel
Plusieurs oprations sont possibles sur les T_Vecteurs. Tout dabord, laddition ou
~ ou CD
~ a t
Relation de Chasles. Sur le schma ci-dessus, chacun des vecteurs AB
dcompos en deux vecteurs noirs tracs en pointills : que lon aille de A B en
~ ou en suivant le premier vecteur noir puis le second, le rsultat
suivant le vecteur AB
est le mme, on arrive en B ! Eh bien, suivre un vecteur puis un autre, cela revient
les additionner.
Do la formule :
 
 
 
3
3
0
=
+
1
0
1
On remarque quil a suffi dajouter chaque coordonne avec celle en face . Il est aussi
possible de multiplier un vecteur par un nombre. Cela revient simplement lallonger
ou le rtrcir (notez que multipliez un vecteur par un nombre ngatif changera le sens
du vecteur). Par exemple :
 
 
3
15
5
=
1
5
Troisime opration : le produit scalaire. Il sagit ici de multiplier deux vecteurs entre
eux. Nous ne rentrerons pas dans le dtail, ce serait trop long expliquer et sans
rapport avec notre sujet. Voici comment effectuer le produit scalaire de deux vecteurs :


3
2
1 4 = (3 2) + (1 4) + (5 6) = 6 + 4 + 30 = 40
5
6
Si le produit scalaire de deux vecteurs vaut 0, cest quils sont perpendiculaires .
Bien, nous en avons fini avec lexplication mathmatique des vecteurs. Pour ceux qui
174

VECTEURS ET CALCUL VECTORIEL (OPTIONNEL)


ne connaissaient pas les vecteurs, ceci devrait avoir quelque peu clairci votre lanterne.
Pour ceux qui les connaissaient dj, vous me pardonnerez davoir pris quelques lgrets avec la rigueur mathmatique pour pouvoir vulgariser ce domaine fondamental
des Maths.
Nous avons maintenant un joli package Integer_Array. Nous pourrons lutiliser aussi
souvent que possible, ds lors que nous aurons besoin de tableaux une seule dimension contenant des integers. Il vous est possible galement de crer un package
Integer_Array2D pour les tableaux deux dimensions. Mais surtout, conserver ce package car nous le complterons au fur et mesure avec de nouvelles fonctions de tri,
plus puissantes et efficaces, notamment lorsque nous aborderons la rcursivit ou les
notions de complexit dalgorithme. Lors du chapitre sur la gnricit, nous ferons en
sorte que ce package puisse tre utilis avec des float, des natural. . . Bref, nous navons
pas fini de lamliorer.
Qui plus est, vous avez srement remarqu que ce chapitre sappelle La programmation modulaire I . Cest donc que nous aborderons plus tard un chapitre La
programmation modulaire II . Mais rassurez-vous, vous en savez suffisamment aujourdhui pour crer vos packages. Le chapitre II permettra damliorer nos packages et
daborder les notions dhritage et dencapsulation chres la programmation oriente
objet.

En rsum
Un package est constitu dun fichier ads contenant les spcifications de vos fonctions et procdures et, si ncessaire, dun fichier adb contenant le corps. Ce dernier
se distingue par lutilisation des mots cls PACKAGE BODY.
Prenez soin de vrifier que les corps des fonctions et procdures correspondent
trait pour trait leurs spcifications car ce sont ces dernires qui feront foi.
Les types, les variables et constantes globales sont dclars avec les spcifications
des fonctions et procdures dans le fichier ads.
viter dutiliser des variables globales autant que cela est possible. En revanche,
il nest pas idiot de globaliser vos constantes.
Nhsitez pas fouiller parmi les packages officiels. Vous y trouverez des ppites
qui vous viteront de rinventer la roue ou vous ouvriront de nouvelles possibilits.

175

CHAPITRE 13. LA PROGRAMMATION MODULAIRE I : LES PACKAGES

176

Chapitre

14

Les fichiers
Difficult :
Nos programmes actuels souffrent dun grave inconvnient : tout le travail que nous pouvons
effectu, tous les rsultats obtenus, toutes les variables enregistres en mmoire vive. . . ont
une dure qui se limite la dure dutilisation de notre programme. Sitt ferm, nous
perdons toutes ces donnes, alors quelle pourraient savrer fort utiles pour une future
utilisation.
Il serait donc utile de pouvoir les enregistrer, non pas en mmoire vive, mais sur le disque
dur. Et pour cela, nous devons tre capable de crer des fichiers, de les lire ou de les
modifier. Cest dailleurs ainsi que procdent la plupart des logiciels. Word ou OpenOffice
ont besoin denregistrer le travail effectu par lutilisateur dans un fichier .doc, .odt. . . De
mme, nimporte quel jeu a besoin de crer des fichiers de sauvegardes mais aussi de lire
continuellement des fichiers (les images ou les vidos notamment) sil veut fonctionner
correctement.

177

CHAPITRE 14. LES FICHIERS


Cest donc aux fichiers que nous allons nous intresser dans ce chapitre. Vous allez
voir, cela ne sera pas trs compliqu, car vous disposez dj de la plupart des notions
ncessaires. Il y a juste quelques points comprendre avant de pouvoir nous amuser.

Ouvrir / Fermer un fichier texte


Nous allons voir plusieurs types de fichiers utilisables en Ada. Le premier type est le
fichier texte. La premire chose faire lorsque lon veut manipuler un fichier, cest
de louvrir ! Cette remarque peut paratre idiote et pourtant : toute modification dun
fichier existant ne peut se faire que si votre programme a pralablement ouvert le fichier
concern. Et bien entendu, la dernire chose faire est de le fermer.

Package ncessaire
Le package utiliser, vous le connaissez dj : Ada.Text_IO ! Le mme que nous utilisions pour afficher du texte ou en saisir sur lcran de la console. En fait, la console peut
tre considre comme une sorte de fichier qui souvre et se ferme automatiquement.

Le type de lobjet
Comme toujours, nous devons dclarer nos variables ou nos objets avant de pouvoir
les manipuler. Nous allons crer une variable composite de type fichier qui fera le lien
avec le vrai fichier texte :
MonFichier : File_type ;

Fermer un fichier
Pour fermer un fichier, rien de plus simple, il suffit de taper linstruction suivante
(toujours entre BEGIN et END, bien sr) :
close ( MonFicher ) ;

Euh. . . avant de le fermer, ne vaudrait-il pas mieux louvrir ?

En effet ! Si vous fermez un fichier qui nest pas ouvert, alors vous aurez droit cette
magnifique erreur :
raised ADA . IO_EXCEPTIONS . STATUS_ERROR : file not open

Il faut donc ouvrir avant de fermer ! Mais je commence par la fermeture car cest ce
quil y a de plus simple.
178

OUVRIR / FERMER UN FICHIER TEXTE

Ouvrir un fichier
Il y a une subtilit lorsque vous dsirez ouvrir un fichier, je vous demande donc de lire
toute cette partie et la suivante en me faisant confiance. Pas de questions, jy rpondrai
par la suite. Il faut distinguer deux cas pour ouvrir un fichier :
Soit ce fichier existe dj.
Soit ce fichier nexiste pas encore.
Ouvrir un fichier existant
Voici la spcification de linstruction permettant douvrir un fichier existant :
1
2
3

procedure open ( File : in out file_type ;


Mode : in File_Mode := Out_File ;
Name : in string ) ;

File est le nom de la variable fichier : ici, nous lavons appel MonFichier. Nous reviendrons dans la partie suivante sur Mode, mais pour linstant nous allons lignorer vu
quil est dj prdfini. Name est le nom du fichier concern, extension comprise. Par
exemple, si notre fichier a lextension .txt et sappelle enregistrement, alors Name doit
valoir "enregistrement.txt". Nous pouvons ainsi taper le code suivant :
1
2

WITH Ada . Text_IO ;


USE Ada . Text_IO ;

3
4
5
6
7
8
9

PROCEDURE TestFichier IS
MonFichier : File_type ;
BEGIN
open ( MonFichier , Name = > " enregistrement . txt " ) ;
close ( MonFichier ) ;
END TestFichier ;

Seul souci, si le compilateur ne voit aucune erreur, la console nous affiche toutefois
ceci :
raised ADA . IO_EXCEPTIONS . NAME_ERROR : enregistrement . txt : No
error

Le fichier demand nexiste pas. Deux possibilits :


Soit vous le crez la main dans le mme rpertoire que votre programme.
Soit vous lisez la sous-sous-partie suivante.
Crer un fichier
Voici la spcification de linstruction permettant la cration dun fichier :
179

CHAPITRE 14. LES FICHIERS


procedure Create ( File : in out File_Type ;
Mode : in File_Mode := Out_File ;
Name : in String ) ;

1
2
3

Elle ressemble beaucoup la procdure open(). Et pour cause, elle fait la mme chose
(ouvrir un fichier) la diffrence quelle commence par crer le fichier demand. Nous
pouvons donc crire le code suivant :
1
2

WITH Ada . Text_IO ;


USE Ada . Text_IO ;

3
4
5
6
7
8
9

PROCEDURE TestFichier IS
MonFichier : File_type ;
BEGIN
create ( MonFichier , Name = > " enregistrement . txt " ) ;
close ( MonFichier ) ;
END TestFichier ;

Et cette fois pas derreur aprs compilation ! a marche !


Euh. . . a marche, a marche. . . cest vite dit. Il ne se passe absolument rien !
Je pensais que mon programme allait ouvrir le fichier texte et que je le verrais
lcran ? !
Non ! Ouvrir un fichier permet seulement au logiciel de le lire ou de le modifier
(au logiciel seulement). Pour que lutilisateur puisse voir le fichier, il faut quil
ouvre un logiciel (le bloc-note par exemple) qui ouvrira son tour et affichera
le fichier demand.
Dans lexemple donn ci-dessus, notre programme ouvre un fichier, et ne fait rien de
plus que de le fermer. Pas daffichage, pas de modification. . . Nous verrons dans ce
chapitre comment modifier le fichier, mais nous ne verrons que beaucoup plus tard
comment faire un bel affichage la manire des diteurs de texte (dernire partie du
cours).
Alors deuxime question : que se passe-t-il si le fichier existe dj ?

Eh bien testez ! Cest la meilleure faon de le savoir. Si vous avez dj lanc votre
programme, alors vous disposez dun fichier "enregistrement.txt". Ouvrez-le avec
NotePad par exemple et tapez quelques mots dans ce fichier. Enregistrez, fermez et
relancez votre programme. Ouvrez nouveau votre fichier : il est vide !
La procdure open() se contente douvrir le fichier, mais celui-ci doit dj exister. La
procdure create() cre le fichier, quoiquil arrive. Si un fichier porte dj le mme nom,
il est supprim et remplac par un nouveau, vide.
180

LE PARAMTRE MODE
Dtruire un fichier

Puisquil est possible de crer un fichier, il doit tre possible de le supprimer ?

Tout fait. Voici le prototype de la procdure en question :


1

procedure Delete ( File : in out File_Type ) ;

Attention ! Pour supprimer un fichier, vous devez auparavant lavoir ouvert !

Le paramtre Mode
Nous avons laiss une difficult de ct tout lheure : le paramtre Mode. Il existe
trois modes possibles pour les fichiers en Ada (et dans la plupart des langages) :
Lecture seule : In_File
criture seule : Out_File
Ajout : Append_File

a veut dire quoi ?

Lecture seule
En mode Lecture seule, cest--dire si vous crivez ceci :
1

Open ( MonFichier , In_File , " enregisrement . txt " ) ;

Vous ne pourrez que lire ce qui est crit dans le fichier, impossible de le modifier. Cest
un peu comme si notre fichier tait un paramtre en mode IN.

criture seule
En mode criture seule, cest--dire si vous crivez ceci :
1

Open ( MonFichier , Out_File , " enregisrement . txt " ) ;

181

CHAPITRE 14. LES FICHIERS


Vous ne pourrez qucrire dans le fichier partir du dbut, en crasant ventuellement
ce qui est dj crit, impossible de lire ce qui existe dj. Cest un peu comme si notre
fichier tait un paramtre en mode OUT.

Ajout
Le mode Ajout, cest--dire si vous crivez ceci :
1

Open ( MonFichier , Append_File , " enregisrement . txt " ) ;

Vous pourrez crire dans le fichier, comme en mode Out_File. La diffrence, cest que
vous ncrirez pas en partant du dbut, mais de la fin. Vous ajouterez des informations
la fin au lieu de supprimer celles existantes.
Et il ny aurait pas un mode IN OUT des fois ?

Eh bien non. Il va falloir faire avec. Dailleurs, mme Word ne peut gure faire mieux :
soit il crit dans le fichier au moment de la sauvegarde, soit il le lit au moment de louverture. Les autres oprations (mise en gras, changement de police, texte nouveau. . .)
ne sont pas effectues directement dans le fichier mais seulement en mmoire vive et
ne seront inscrites dans le fichier que lorsque Word les enregistrera.

Oprations sur les fichiers textes


Une fois notre fichier ouvert, il serait bon de pouvoir effectuer quelques oprations
avec. Attention, les oprations disponibles ne seront pas les mmes selon que vous avez
ouvert votre fichier en mode In_File ou en mode Out_File/Append_File !

Mode lecture seule : In_File


Saisir un caractre
Tout dabord vous allez devoir tout au long de ce chapitre imaginez votre fichier et
lemplacement du curseur (voir la figure 14.1).
Dans lexemple prcdent, vous pouvez vous rendre compte que mon curseur se trouve
la seconde ligne, vers la fin du mot situ . Ainsi, si je veux connatre le prochain
caractre, je vais devoir utiliser la procdure get() que vous connaissez dj, mais de
manire un peu diffrente :
1

procedure Get ( File : in File_Type ; Item : out Character ) ;

Comme vous pouvez vous en rendre compte en lisant ce prototype, il ne suffira pas
dindiquer entre les parenthses dans quelle variable de type character vous voulez
182

OPRATIONS SUR LES FICHIERS TEXTES

Figure 14.1 dfaut de voir votre fichier texte et le curseur, vous devrez les imaginer
enregistrer le caractre saisi, il faudra aussi indiquer dans quel fichier ! Comme dans
lexemple ci-dessous :
1

Get ( MonFichier , C ) ;

Une fois cette procdure utilise, la variable C vaudra (il y aura srement
un problme avec le caractre latin) et le curseur sera dplac la fin du mot
situ , cest--dire entre le et lespace vide !
Autre remarque dimportance : si vous tes en fin de ligne, en fin de page ou en fin
de fichier, vous aurez droit un joli message derreur ! Le curseur ne retourne pas
automatiquement la ligne. Pour pallier cela, deux possibilits. Premire possibilit :
vous pouvez utiliser pralablement les fonctions suivantes :
1
2
3

function End_Of_Line ( File : in File_Type ) return Boolean ;


function End_Of_Page ( File : in File_Type ) return Boolean ;
function End_Of_File ( File : in File_Type ) return Boolean ;

Elle renvoient TRUE si vous tes, respectivement, en fin de ligne, en fin de page ou en
fin de fichier. Selon les cas, vous devrez alors utiliser ensuite les procdures :
1
2
3

procedure Skip_Line ( File : in File_Type ; Spacing : in


Positive_Count := 1 ) ;
procedure Skip_Page ( File : in File_Type ) ;
procedure Reset ( File : in out File_Type ) ;

Skip_line(MonFichier) vous permettra de passer la ligne suivante (nous lutilisions


jusque l pour vider le tampon). Il est mme possible de lui demander de sauter
plusieurs lignes grce au paramtre Spacing en crivant Skip_Line(MonFichier,5).
Skip_Page(MonFichier) vous permettra de passer la page suivante. Reset(MonFichier)
aura pour effet de remettre votre curseur au tout dbut du fichier. Do le code suivant :
1
2
3
4
5
6

loop
if end_of_line ( MonFichier )
then skip_line ( MonFichier ) ;
elsif end_of_page ( MonFichier )
then skip_page ( MonFichier ) ;
elsif end_of_file ( MonFichier )

183

CHAPITRE 14. LES FICHIERS


7
8
9
10

then reset ( MonFichier ) ;


else get ( C ) ; exit ;
end if ;
end loop ;

Cette boucle permet de traiter les diffrents cas possibles et de recommencer tant
que lon na pas saisi un vrai caractre ! Attention, si le fichier est vide, vous risquez
dattendre un moment Passons la deuxime possibilit :
procedure Look_Ahead ( File
: in File_Type ;
Item
: out Character ;
End_Of_Line : out Boolean ) ;

1
2
3

Cette procdure ne fait pas avancer le curseur mais regarde ce qui se trouve aprs : si
lon se trouve en fin de ligne, de page ou de fichier, le paramtre End_Of_Line vaudra
true et le paramtre Item ne vaudra rien du tout. Dans le cas contraire, End_Of_Line
vaudra false et Item aura la valeur du caractre suivant. Je le rpte, le curseur nest
pas avanc dans ce cas l ! Voici un exemple avec une variable fin de type boolean et
une variable C de type Character.
1
2
3
4
5

Look_Ahead ( MonFichier ,C , fin ) ;


if not fin
then put ( " Le caract re vaut : " ) ; put ( C ) ;
else put ( "C ' est la fin des haricots ! " ) ;
end if ;

Saisir un string
Pour saisir directement une ligne et pas un seul caractre, vous pourrez utiliser lune
des procdures dont voici les prototypes :
1
2
3

procedure Get ( File : in File_Type ; Item : out String ) ;


procedure Get_Line ( File : in File_Type ; Item : out String ;
Last : out Natural ) ;
function Get_Line ( File : in File_Type ) return string ;

La procdure Get() saisit tout le texte prsent dans lobjet-fichier File et lenregistre
dans le string Item (le curseur se trouve alors la fin du fichier). La procdure
get_line() saisit tout le texte situ entre le curseur et la fin de la ligne et laffecte
dans le string Item ; elle renvoie galement la longueur de la chane de caractre dans
la variable Last (le curseur sera alors plac au dbut de la ligne suivante). Enfin, la
fonction Get_line se contente de renvoyer le texte entre le curseur et la fin de ligne
(comme la procdure, seule lutilisation et labsence de paramtre last diffrent).
Mais nous nous retrouvons face un inconvnient de taille : nous ne connaissons pas
lavance la longueur des lignes et donc la longueur des strings qui seront retourns.
cela, deux solutions : soit nous utilisons des strings suffisamment grands en priant pour
que les lignes ne soient pas plus longues (et il faudra prendre quelques prcautions
laffichage), soit nous faisons appel aux unbounded_strings :
184

OPRATIONS SUR LES FICHIERS TEXTES


Premire mthode :
...
S : string(1..100) ;
MonFichier : file_type ;
long : natural ;
BEGIN
open(MonFichier, In_File, "enregistrement.txt") ;
get_line(MonFichier, S, long) ;
put_line(S(1..long)) ;
...

Seconde mthode :
...
txt : unbounded_string ;
MonFichier : file_type ;
BEGIN
open(MonFichier, In_File, "enregistrement.txt") ;
txt:=To_Unbounded_String(get_line(MonFichier)) ;
put_line(To_String(txt)) ;
...

Mode criture : Out_File / Append_File


Rappel : en mode Append_File, toute modification se fera la fin du fichier !
En mode Out_File, les modifications se feront partir du dbut, au besoin
en crasant ce qui tait dj crit.
Vous devriez commencer vous douter des oprations dcriture applicables aux fichiers
textes, car , -dire-vrai, vous les avez dj vues. Voici quelques prototypes :
1
2
3
4
5

procedure New_Line ( File : in File_Type ; Spacing : in


Positive_Count := 1 ) ;
procedure New_Page ( File : in File_Type ) ;
procedure Put ( File : in File_Type ; Item : in Character ) ;
procedure Put ( File : in File_Type ; Item : in String ) ;
procedure Put_Line ( File : in File_Type ; Item : in String ) ;

Vous devriez dors et dj vous doutez de ce que font ces instructions. New_line()
insre un saut de ligne dans notre fichier lemplacement du curseur (par dfaut, mais
le paramtre spacing permet dinsrer 2, 3, 4. . . sauts de lignes). New_Page() insre
un saut de page. Les procdures Put() permettent dinsrer un caractre ou toute une
chane de caractres. La procdure put_line() insre une chane de caractres et un
saut de ligne.
185

CHAPITRE 14. LES FICHIERS

Autres oprations
Il existe galement quelques fonctions ou procdures utilisables quels que soient les
modes ! En voici quelques-unes pour se renseigner sur notre fichier :
Pour connatre le mode dans lequel un fichier est ouvert (In_File, Out_File,
Append_File). Permet dviter des erreurs de manipulation.
function Mode (File : in File_Type) return File_Mode;

Pour connatre le nom du fichier ouvert.


function Name (File : in File_Type) return String;

Pour savoir si un fichier est ouvert ou non. Pour viter de fermer un fichier dj
ferm, ou douvrir un fichier dj ouvert, etc.
function Is_Open (File : in File_Type) return Boolean;

Et en voici quelques autres pour dplacer le curseur ou se renseigner sur sa position.


Pour information, le type Positive_Count sont en fait des natural mais 0 en est exclu
(les prototypes ci-dessous sont crits tels quils le sont dans le package Ada.Text_IO).
Pour placer le curseur une certaine colonne ou une certaine ligne.
procedure Set_Col (File : in File_Type; To : in Positive_Count);
procedure Set_Line (File : in File_Type; To : in Positive_Count);

Pour connatre la colonne, la ligne ou la page o se situe le curseur.


function Col (File : in File_Type) return Positive_Count;
function Line (File : in File_Type) return Positive_Count;
function Page (File : in File_Type) return Positive_Count;

Les fichiers binaires squentiels


On ne peut manipuler que des fichiers contenant du texte ? Comment je fais
si je veux enregistrer les valeurs contenues dans un tableau par exemple ? Je
dois absolument utiliser les attributs image et value ?
Non, le fichier texte nest pas obligatoire, mais il a lavantage dtre modifiable par
lutilisateur grce un simple diteur de texte, type Notepad par exemple. Mais si
186

LES FICHIERS BINAIRES SQUENTIELS


vous ne souhaitez quenregistrer des integer (par exemple), il est possible denregistrer
vos donnes dans des fichiers binaires prvus cet effet.
Cest quoi un fichier binaire ?

Cest un fichier constitu dune suite de bits (les chiffres 0 et 1, seuls chiffres connus de
lordinateur). a ne vous aide pas beaucoup ? a tombe bien car ces fichiers contiennent
une suite dinformations illisibles par lutilisateur. Pour pouvoir lire ces donnes, il faut
faire appel un logiciel qui connaitra le type dinformations contenues dans le fichier
et qui saura comment les traiter. Par exemple :
Les fichiers MP3, sont illisibles par le commun des mortels. En revanche, en
utilisant un logiciel tel que VLC (http://www.videolan.org/vlc/) ou amarok
(http://www.amarok.kde.org/wiki/Download/fr), vous pourrez couter votre
morceau de musique.
Les fichiers images (.bmp, .tiff, .png, . jpg, .gif. . .) ne seront lisibles qu laide
dun logiciel comme photofiltre (http://www.photofiltre.free.fr/frames.
htm) par exemple.
On pourrait encore multiplier les exemples, la plupart des fichiers utiliss aujourdhui tant des fichiers binaires.
Parmi ces fichiers binaires, il en existe deux sortes : les fichiers squentiels et les
fichiers accs direct. La diffrence fondamentale rside dans la faon daccder
aux donnes quils contiennent. Les premiers sont utilisables comme les fichiers textes
(qui sont eux-mme des fichiers squentiels), cest pourquoi nous allons les aborder
de ce pas. Les seconds sont utiliss un peu diffremment, et nous les verrons dans la
sous-partie suivante.
Nous allons crer un programme Enregistrer qui, comme son nom lindique, enregistrera des donnes de type integer dans un fichier binaire accs squentiel. Nous
devons dabord rgler le problme des packages. Celui que nous allons utiliser ici est
Ada.sequential_IO. Le souci, cest quil est fait pour tous les types de donnes (float,
natural, array, string, integer, character. . .), on dit quil est gnrique (nous aborderons
cette notion dans la quatrime partie de ce cours). La premire chose faire est donc
de crer un package plus spcifique :
1
2

WITH Ada . Integer_Text_IO ;


WITH Ada . Sequential_IO ;

USE Ada . Integer_Text_IO ;

3
4
5
6
7

PROCEDURE Enregistrer IS
PACKAGE P_integer_file IS NEW Ada . Sequential_IO ( integer ) ;
USE P_integer_file ;
...

Comme vous pouvez le remarquer, Ada.Sequential_IO ne bnficie pas dune clause


USE (du fait de sa gnricit) et doit tre redclar plus loin : nous creons un
187

CHAPITRE 14. LES FICHIERS


package P_integer_file qui est en fait le package Ada.Sequential_IO mais rserv
aux donnes de type integer. Nous pouvons ensuite crire notre clause de contexte
USE P_integer_file . Nous avons dj vu ce genre de chose durant notre premier
TP sur le craps pour crer des nombres alatoires. Je ne vais pas mtendre davantage
sur cette notion un peu complique car nous y reviendrons plus tard. Notez tout de
mme la nomenclature : je commence mon nom de package par un P_ pour mieux le
distinguer des types ou des variables.
Nous allons ensuite dclarer notre variable composite fichier, notre type T_Tableau et
notre variable T de type T_Tableau. Je vous laisse le choix dans la manire de saisir
les valeurs de T. Puis, nous allons devoir crer notre fichier, louvrir et le fermer :
1

...

MonFichier : File_Type ;
type T_Tableau is array ( 1 .. 5 , 1 .. 5 ) of integer ;
T : T_Tableau ;
BEGIN
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- Saisie libre de T ( d brouillez - vous ) --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

2
3
4
5
6
7
8
9

create ( MonFichier , Out_File , " sauvegarde . inf " ) ;

10
11

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- enregistrement ( vu apr s ) --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

12
13
14
15
16
17

close ( MonFichier ) ;
END Enregistrer ;

Comme je vous lai dit, lencart Saisie libre de T est votre charge (affectation
au clavier, alatoire ou prdtermine, vous de voir). La procdure create() peut tre
remplace par la procdure open(), comme nous en avions lhabitude avec les fichiers
textes. Les modes douverture (In_File, Out_File, Append_File) sont galement les
mmes. Concentrons-nous plutt sur le deuxime encart : comment enregistrer nos
donnes ? Les procdures put_line() et put() ne sont plus utilisables ici ! En revanche,
elles ont une sur jumelle : write() ! Traduction pour les anglophobes : crire .
Notre second encart va donc pouvoir tre remplac par ceci :
1

...

2
3
4
5
6
7

...

for i in T ' range ( 1 ) loop


for j in T ' range ( 2 ) loop
write ( MonFichier , T (i , j ) ) ;
end loop ;
end loop ;

Compiler votre code et excutez-le. Un fichier sauvegarde.inf doit avoir t cr.


Ouvrez-le avec le bloc-notes : il affiche des symboles bizarres, mais rien de ce que vous
avez enregistr ! Je vous rappelle quil sagit dun fichier binaire ! Donc il est logique
188

LES FICHIERS BINAIRES DIRECTS


quil ne soit pas lisible avec un simple diteur de texte. Pour le lire, nous allons crer,
dans le mme rpertoire, un second programme : Lire. Il faudra donc redclarer
notre package P_integer_file et tout et tout.
Pour la lecture, nous ne pouvons, bien entendu, pas non plus utilis get() ou get_line().
Nous utiliserons la procdure read() (traduction : lire) pour obtenir les informations
voulues et la fonction Enf_Of_File() pour tre certains de ne pas saisir de valeur une
fois rendu la fin du fichier ( noter que les fins de ligne ou fins de page nexiste pas
dans un fichier binaire).
1
2

WITH Ada . Integer_Text_IO ;


WITH Ada . Sequential_IO ;

USE Ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

PROCEDURE Lire IS
PACKAGE P_integer_file IS NEW Ada . Sequential_IO ( integer ) ;
USE P_integer_file ;
MonFichier : File_Type ;
type T_Tableau is array ( 1 .. 5 , 1 .. 5 ) of integer ;
T : T_Tableau ;
BEGIN
open ( MonFichier , In_File , " sauvegarde . inf " ) ;
for i in T ' range ( 1 ) loop
for j in T ' range ( 2 ) loop
exit when Enf_Of_File ( MonFichier ) ;
read ( MonFichier , T (i , j ) ) ;
put ( T (i , j ) ) ;
end loop ;
end loop ;
close ( MonFichier ) ;
END Lire ;

Les procdures et fonctions delete(), reset(), Name(), Mode() ou Is_Open()


existent galement pour les fichiers squentiels.
Voil, nous avons dors et dj fait le tour des fichiers binaires accs squentiel. Le
principe nest gure diffrent des fichiers textes (qui sont, je le rappelle, eux aussi
accs squentiels), retenez simplement que put() est remplac par write(), que get() est
remplac par read() et quil faut pralablement spcifier le type de donnes enregistres
dans vos fichiers. Nous allons maintenant attaquer les fichiers binaires accs direct.

Les fichiers binaires directs


Les fichiers binaires accs directs fonctionnent la manire dun tableau. Chaque
lment enregistr dans le fichier est dot dun numro, dun indice, comme dans un
tableau. Tout accs une valeur, que ce soit en lecture ou en criture, se fait en
indiquant cet indice. Premire consquence et premire grande diffrence avec les fi189

CHAPITRE 14. LES FICHIERS


chiers squentiels, le mode Append_File nest plus disponible. En revanche, un mode
InOut_File est enfin disponible !
Nous allons enregistrer cette fois les valeurs contenues dans un tableau de float unidimensionnel. Nous utiliserons donc le package Ada.Direct_IO qui, comme Ada.Sequential_IO,
est un package gnrique (fait pour tous types de donnes). Il nous faudra donc crer un
package comme prcdemment avec P_integer_file (mais laide de Ada.Direct_IO
cette fois). Voici le code de ce programme :
1
2

WITH Ada . Float_Text_IO ;


WITH Ada . Direct_IO ;

USE Ada . Float_Text_IO ;

PROCEDURE FichierDirect IS
PACKAGE P_float_file IS NEW Ada . Direct_IO ( Float ) ;
USE P_float_file ;
MonFichier : File_Type ;

4
5
6
7
8

TYPE T_Vecteur IS ARRAY ( 1 .. 8 ) OF Float ;


T : T_Vecteur := ( 1 .0 , 2 .0 , 4 .2 , 6 .5 , 10 .0 , 65 .2 , 5 .3 , 101 . 01 ) ;
x : float ;
BEGIN
create ( MonFichier , InOut_File , " vecteur . inf " ) ;

9
10
11
12
13
14

for i in 1 .. 8 loop
write ( MonFichier , T ( i ) , count ( i ) ) ;
read ( MonFichier ,x , count ( i ) ) ;
put ( x ) ;
end loop ;

15
16
17
18
19
20
21
22

close ( MonFichier ) ;
END FichierDirect ;

Le troisime paramtre des procdures write() et read() est bien entendu


lindice de la valeur lue ou crite. Toutefois, cet indice est de type Count
(une sorte de type natural) incompatible malheureusement avec le type de
la variable i (integer). Cest pourquoi nous sommes obligs dcrire count(i)
pour la convertir en variable de type Count.
L encore, si vous tentez douvrir votre fichier vecteur.inf , vous aurez droit un joli
texte incomprhensible. Remarquez que linstruction write(MonFichier,T(i),count(i))
pourrait tre remplace par les deux lignes suivantes :
1
2

set_index ( MonFichier , count ( i ) ) ;


write ( MonFichier , T ( i ) ) ;

En effet, linstruction set_index() place le curseur lendroit voulu, et il existe une


deuxime procdure write() qui ne spcifie pas lindice o vous souhaitez crire. Cette
procdure crit donc l o se trouve le curseur. Et il en est de mme pour la procdure
Read().
190

LES RPERTOIRES
Comme pour un tableau, vous ne pouvez pas lire un emplacement vide, cela
gnrerait une erreur ! Cest l linconvnient des fichiers binaires accs
directs.

Les rpertoires
Peut-tre avez-vous remarqu que, comme pour les packages, les fichiers que vous crez
ou que vous pouvez lire se situent tous dans le mme rpertoire que votre programme.
Si jamais vous les dplacez, votre programme ne les retrouvera plus. Pourtant, lorsque
vous mnerez des projets plus consquents (comme les TP de ce tutoriel ou bien vos
propres projets), il sera prfrable de rassembler les fichiers de mme types dans dautres
rpertoires.

Chemins absolus et relatifs


Vous savez que les rpertoires et fichiers de votre ordinateur sont organiss en arborescence (voir figure 14.2). Chaque rpertoire est ainsi dot dune adresse, cest--dire dun
chemin daccs. Si votre programme est situ dans le rpertoire C:\Rep1\Rep2\Rep3
(jutilise un chemin windows mais le principe est le mme sous les autres systmes
dexploitation), vous pouvez fournir deux adresses lordinateur : soit cette adresse
complte, appele chemin absolu, soit le chemin suivre pour retrouver les fichiers
partir de son rpertoire courant, appel chemin relatif.

Figure 14.2 Arborescence des rpertoires


Si votre programme est situ dans le mme rpertoire que vos fichiers, le chemin relatif
sera not par un point ( . ), que vous soyez sous Windows, Mac ou Linux. Ce point
signifie dans le rpertoire courant . Si vos fichiers sont situs dans le rpertoire
C:\Rep1\Rep2 (adresse absolue), le chemin relatif sera not par deux points (..) qui
signifient dans le rpertoire du dessous .
Compliquons la tche : notre programme est toujours situ dans le rpertoire C:\Rep1\Rep2\Rep3
mais les fichiers sont situs dans le rpertoire C:\Rep1\Rep2\Rep3\Rep4. Ladresse re191

CHAPITRE 14. LES FICHIERS


lative des fichiers est donc .\Rep4. Enfin, si nous dplaons nos fichiers dans un rpertoire C:\Rep1\Rep2\AutreRep, leur chemin relatif sera ..\AutreRep (le programme
doit revenir au rpertoire du dessous avant douvrir le rpertoire AutreRep)

Indiquer le chemin daccs


Maintenant que ces rappels ont t faits, crons donc un rpertoire files dans le
mme rpertoire que notre programme et plaons-y un fichier appel "Sauvegarde.txt".
Comment le lire dsormais ? Au lieu que votre programme nutilise linstruction suivante :
1

Open (F , In_File , " Sauvegarde . txt " ) ;

Nous allons lui indiquer le nom du fichier ouvrir ainsi que le chemin suivre :
1

Open (F , In_File , " ./ files / Sauvegarde . txt " ) ; -- Avec adresse


relative

--

OU

4
5

Open (F , In_File , " C :/ Rep1 / Rep2 / Rep3 / files / Sauvegarde . txt " ) ; -Avec adresse absolue

Le paramtre pour le nom du fichier contient ainsi non seulement son nom mais galement son adresse complte.

Grer fichiers et rpertoires

Le programme ne pourrait pas copier ou supprimer les rpertoire lui-mme ?

Bien sr que si ! Le langage Ada a tout prvu. Pour cela, vous aurez besoin du package
Ada.Directories. Le nom du fichier correspondant est a-direct.ads et je vous invite
le lire par vous-mme pour dcouvrir les fonctionnalits que je ne dtaillerai pas ici.
Voici quelques instructions pour grer vos rpertoires :
1
2

function Current_Directory return String ;


procedure Set_Directory
( Directory : String ) ;

3
4
5

procedure Create_Directory ( New_Directory : String ) ;


procedure Delete_Directory ( Directory : String ) ;

6
7
8

procedure Create_Path
procedure Delete_Tree

( New_Directory : String ) ;
( Directory : String ) ;

Current_Directory renvoie une chane de caractres correspondant ladresse du


rpertoire courant. La procdure Set_Directory permet quant elle de dfinir un
192

QUELQUES EXERCICES
rpertoire par dfaut, si par exemple lensemble de vos ressources se trouvait dans
un mme dossier. Create_Directory() et Delete_Directory() vous permettrons de
crer ou supprimer un rpertoire unique, tandis que Create_Path() et Delete_Tree()
vous permettrons de crer toute une chane de rpertoire ou de supprimer un dossier
et lensemble de ses sous-dossiers. Des programmes similaires sont disponibles pour les
fichiers :
1
2
3
4

procedure Delete_File ( Name : String ) ;


procedure Rename
( Old_Name , New_Name : String ) ;
procedure Copy_File
( Source_Name
: String ;
Target_Name
: String ) ;

Ces procdures vous permettront respectivement de supprimer un fichier, de le renommer ou de le copier. Pour les procdures Rename() et Copy_File(), le premier
paramtre correspond au nom actuel du fichier, le second paramtre correspond quant
lui au nouveau nom ou au nom du fichier cible. Voici enfin trois dernires fonctions
parmi les plus utiles :
1
2
3

function Size
( Name : String ) return File_Size ;
function Exists ( Name : String ) return Boolean ;
function Kind
( Name : String ) return File_Kind ;

Size() renverra la taille dun fichier ; Exists() vous permettra de savoir si un fichier
ou un dossier existe ; Kind() enfin, renverra le type de fichier vis : un dossier (le
rsultat vaudra alors Directory ), un fichier ordinaire (le rsultat vaudra alors
Ordinary_File ) ou un fichier spcial (le rsultat vaudra alors Special_File ).

Quelques exercices
Exercice 1
nonc
Crer un fichier texte appel "poeme.txt" et dans lequel vous copierez le texte suivant :
Que jaime faire apprendre un nombre utile aux sages ! Immortel Archimde, artiste,
ingnieur, Qui de ton jugement peut priser la valeur ? Pour moi ton problme eut de
pareils avantages.
Jadis, mysterieux, un problme bloquait Tout ladmirable procde, loeuvre grandiose
Que Pythagore decouvrit aux anciens Grecs. quadrature ! Vieux tourment du philosophe
Insoluble rondeur, trop longtemps vous avez Defie Pythagore et ses imitateurs. Comment intgrer lespace plan circulaire ? Former un triangle auquel il quivaudra ?
Nouvelle invention : Archimde inscrira Dedans un hexagone ; apprciera son aire Fonction du rayon. Pas trop ne sy tiendra : Ddoublera chaque lment antrieur ;
193

CHAPITRE 14. LES FICHIERS


Toujours de lorbe calcule approchera ; Dfinira limite ; enfin, larc, le limiteur De cet
inquitant cercle, ennemi trop rebelle Professeur, enseignez son problme avec zle
Ce pome est un moyen mnmotechnique permettant de rciter les chiffres du nombre
( 3, 14159), il suffit pour cela de compter le nombre de lettres de chacun des mots
(10 quivalant au chiffre 0).
Puis, crer un programme poeme.exe qui lira ce fichier et laffichera dans la console.
Solution
1
2

with ada . Text_IO , ada . Strings . Unbounded ;


use ada . Text_IO , ada . Strings . Unbounded ;

procedure poeme is
F : File_Type ;
txt : unbounded_string ;
begin

4
5
6
7
8

open (F , in_file , " poeme . txt " ) ;

9
10

while not end_of_file ( F ) loop


txt := to_unbounded_string ( get_line ( F ) ) ;
put_line ( to_string ( txt ) ) ;
end loop ;

11
12
13
14
15

close ( F ) ;

16
17
18

end poeme ;

Exercice 2
nonc
Plus compliqu, reprendre le problme prcdent et modifier le code source de sorte que
le programme affiche galement les chiffres du nombre . Attention ! La ponctuation
ne doit pas tre compte et je rappelle que la seule faon dobtenir 0 est davoir un
nombre 10 chiffres !
Solution
1
2

WITH Ada . Text_IO , Ada . Integer_Text_IO , Ada . Strings . Unbounded ;


USE Ada . Text_IO , Ada . Integer_Text_IO , Ada . Strings . Unbounded ;

3
4
5
6

PROCEDURE Poeme IS
F
: File_Type ;
Txt : Unbounded_String ;

7
8

194

PROCEDURE Comptage ( Txt : String ) IS

QUELQUES EXERCICES
9
10
11
12
13
14
15
16
17
18

N : Natural := 0 ;
BEGIN
FOR I IN Txt ' RANGE LOOP
CASE Txt ( I ) IS
WHEN ' '
= > IF
N /= 0 THEN Put ( N mod 10 , 3 ) ; N := 0 ; END IF ;
WHEN '. ' | '? ' | ' , ' | '; ' | '! ' | ': ' | ' ' ' = > IF
N /= 0 THEN Put ( N mod 10 , 3 ) ; N := 0 ; END IF ;
WHEN others
=> n
:= n + 1 ;
END CASE ;
END LOOP ;
END Comptage ;

19
20

BEGIN

21
22

Open (F , In_File ," poeme . txt ") ;

23
24
25
26
27
28
29

WHILE NOT End_Of_File ( F ) LOOP


Txt := To_Unbounded_String ( Get_Line ( F ) ) ;
Put_Line ( To_String ( Txt ) ) ;
Comptage ( To_String ( Txt ) ) ;
New_line ;
END LOOP ;

30
31

Close ( F ) ;

32
33

END Poeme ;

Exercice 3
nonc
Nous allons crer un fichier "Highest_Score.txt" dans lequel seront enregistrs les
informations concernant le meilleur score obtenu un jeu (ce code pourra tre mis en
package pour tre rutilis plus tard). Seront demands : le nom du gagnant, le score
obtenu, le jour, le mois et lanne dobtention de ce rsultat.
Voici un exemple de la prsentation de ce fichier texte. Pas despaces, les intituls sont
en majuscule suivis dun signe =. Il y aura 4 valeurs entires enregistrer et un string,
comme sur la figure 14.3.
Le programme donnera le choix entre lire le meilleur score et en enregistrer un nouveau.
Vous aurez besoin pour cela du package Ada.Calendar et de quelques fonctions :
clock : renvoie la date du moment. Rsultat de type Time.
year(clock) : extrait lanne de la date du moment. Rsultat : integer.
month(clock) : extrait le mois de la date du moment. Rsultat : integer.
day(clock) : extrait le jour de la date du moment. Rsultat : integer.
195

CHAPITRE 14. LES FICHIERS

Figure 14.3 Rsultat attendu


Solution
1
2
3
4
5

WITH
WITH
WITH
WITH
WITH

Ada . Text_IO ;
USE
Ada . Integer_Text_IO ;
USE
Ada . Calendar ;
USE
Ada . Characters . Handling ; USE
Ada . Strings . Unbounded ;
USE

Ada . Text_IO ;
Ada . Integer_Text_IO ;
Ada . Calendar ;
Ada . Characters . Handling ;
Ada . Strings . Unbounded ;

6
7

procedure BestScore is

8
9
10
11

-- - - - - - - - - - - - - - - - - - -- Lecture du score - -- - - - - - - - - - - - - - - - - - -

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

196

procedure read_score ( F : file_type ) is


txt : unbounded_string ;
long : natural ;
Score , Jour , Mois , Annee : natural ;
Nom : unbounded_string ;
begin
while not end_of_file ( F ) loop
txt := To_Unbounded_String ( get_line ( F ) ) ;
long := length ( txt ) ;
if long >= 6
then if To_String ( txt ) ( 1 .. 6 ) = " SCORE = "
then Score := natural ' value ( To_String ( txt ) (
7 .. long ) ) ;
end if ;
if To_String ( txt ) ( 1 .. 6 ) = " MONTH = "
then Mois := natural ' value ( To_String ( txt ) ( 7
.. long ) ) ;
end if ;
end if ;
if long >= 5
then if To_String ( txt ) ( 1 .. 5 ) = " YEAR = "
then Annee := natural ' value ( To_String ( txt ) (
6 .. long ) ) ;
end if ;
if To_String ( txt ) ( 1 .. 5 ) = " NAME = "

QUELQUES EXERCICES
35
36
37
38
39
40
41
42
43
44
45

then Nom := to_unbounded_string ( To_string (


txt ) ( 6 .. long ) ) ;
end if ;
end if ;
if long >= 4 and then To_String ( txt ) ( 1 .. 4 ) = " DAY = "
then Jour := natural ' value ( To_String ( txt ) ( 5 .. long ) )
;
end if ;
end loop ;
Put_line ( "
" & to_string ( Nom ) ) ;
Put ( " a obtenu le score de " ) ; Put ( Score ) ; New_line ;
Put ( " le " & integer ' image ( jour ) & " / " & integer ' image (
mois ) & " / " & integer ' image ( annee ) & " . " ) ;
end read_score ;

46
47
48
49

-- - - - - - - - - - - - - - - - - - - -- criture du score - -- - - - - - - - - - - - - - - - - - - -

50
51
52
53
54
55
56
57
58
59
60
61
62

procedure Write_Score ( F : file_type ) is


txt : unbounded_string ;
score : integer ;
begin
Put ( " Quel est votre nom ? " ) ; txt := to_unbounded_string
( get_line ) ;
Put_line (F , " NAME = " & to_string ( txt ) ) ;
Put ( " Quel est votre score ? " ) ; get ( score ) ; skip_line ;
Put_line (F , " SCORE = " & integer ' image ( score ) ) ;
Put_line (F , " YEAR = " & integer ' image ( year ( clock ) ) ) ;
Put_line (F , " MONTH = " & integer ' image ( month ( clock ) ) ) ;
Put_line (F , " DAY = " & integer ' image ( day ( clock ) ) ) ;
end Write_Score ;

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - -PROC DURE PRINCIPALE


--- - - - - - - - - - - - - - - - - - - - - - - - - - - - c : character ;
NomFichier : constant string := " Highest_Score . txt " ;
F : file_type ;
begin
loop
Put_line ( " Que voulez - vous faire ? Lire ( L ) le meilleur
score obtenu , " ) ;
Put ( " ou en enregistrer un nouveau ( N ) ? " ) ;
get_immediate ( c ) ; new_line ; new_line ;
c := to_upper ( c ) ;
exit when c = 'L ' or c = 'N ' ;
end loop ;

78
79

if c = 'L '

197

CHAPITRE 14. LES FICHIERS


80
81
82
83
84
85
86
87
88
89
90

then open (F , In_File , NomFichier ) ;


read_score ( F ) ;
close ( F ) ;
else create (F , Out_File , NomFichier ) ;
write_score ( F ) ;
close ( F ) ;
open (F , In_File , NomFichier ) ;
read_score ( F ) ;
close ( F ) ;
end if ;
end BestScore ;

Ce chapitre sur les fichiers vous permettra enfin de garder une trace des actions de vos
programmes ; vous voil dsormais arms pour crer des programmes plus complets.
Vous pouvez dors et dj rinvestir ce que vous avez vu et conu dans ces derniers
chapitres pour amliorer votre jeu de craps (vous vous souvenez, le TP) en permettant
au joueur de sauvegarder son (ses) score(s), par exemple. Pour le prochain chapitre,
nous allons mettre de ct les tableaux (vous les retrouverez vite, rassurez vous :- )
pour crer nos propres types.

En rsum
Avant deffectuer la moindre opration sur un fichier, ouvrez-le et surtout, noubliez pas de le fermer avant de clore votre programme.
Les fichiers squentiels (que ce soient des fichiers textes ou binaires) ne sont
accessibles quen lecture ou en criture, mais jamais les deux en mme temps.
Il est prfrable de ne pas garder un fichier ouvert trop longtemps, on prfre en
gnral copier son contenu dans une ou plusieurs variables et travailler sur ces
variables.
Avant de saisir une valeur en provenance dun fichier, assurez-vous que vous ne
risquez rien : vous pouvez tre en fin de ligne, en fin de page ou en fin de fichier ;
dans un fichier accs direct, lemplacement suivant nest pas ncessairement
occup !
Nhsitez pas classer vos fichiers dans un sous-dossier par soucis dorganisation.
Mais pensez alors indiquer le chemin daccs votre programme, de prfrence
un chemin relatif.

198

Chapitre

15

Crer vos propres types


Difficult :
Nous avons dj abord de nombreux types (Integer, Float, Character, String, tableaux
et jen passe) mais peu dentre eux suffiraient modliser la plupart des objets de la vie
courante. Imaginez que vous vouliez modliser lidentit des personnages de votre futur
jeu vido rvolutionnaire : des String modliseraient les nom et prnom, un Integer peut
modliser lge, des Float pourraient modliser la taille et le poids. . . mais aucun type ne
peut lui seul modliser lensemble de ces donnes, mme pas les tableaux. De mme,
comment modliser une pendule ? Pour les Integer, 23 + 2 = 25, alors que pour une
pendule 23H + 2H donne 1H du matin !
Nous allons donc apprendre crer nos propres types composites, les modifier, les faire
voluer et mme crer des sous-types !

199

CHAPITRE 15. CRER VOS PROPRES TYPES

Crer partir de types prdfinis


Cette partie nest pas la plus folichonne : nous allons rutiliser des types prexistants
pour en crer des nouveaux. Malgr tout, elle vous sera souvent trs utile et elle nest
pas trs complique comprendre.

Sous-type comme intervalle


Schmatiquement, Integer couvre les nombres entiers (0, 1, 2, 3. . . -1, -2, -3. . .) et
natural les nombres entiers naturels (0, 1, 2, 3. . .). Nous souhaiterions crer un type
T_positif et un type T_negatif qui ne comportent pas le nombre 0. Pour cela, nous
allons crer un sous-type (SUBTYPE) de natural ou de integer qui couvrira lintervalle
(RANGE) allant de 1 linfini (symbole : +) pour les positifs et de linfini ngatif
(symbole : ) -1 pour les ngatifs.
Linfini nexiste pas en informatique. Les nombres gnrs sont ncessairement
limits, quand bien mme cette limite est trs leve, il ne sagit pas de linfini
au sens mathmatique.
Nous allons donc, dans la partie dclarative, crire soit :
subtype T_positif is integer range 1 .. integer ' last ;
subtype T_negatif is integer range integer ' first .. - 1 ;

1
2

Soit :
subtype T_positif is natural range 1 .. natural ' last ;

Il sera alors impossible dcrire ceci :


1
2

n : T_positif := 0 ;
m : T_negatif := 1 ;

Comme T_positif et T_negatif drivent des types integer ou natural (lui-mme drivant du type integer ), toutes les oprations valables avec les integer/natural, comme
laddition ou la multiplication, sont bien sr valables avec les types T_positif et
T_negatif, tant que vous ne sortez pas des intervalles dfinis.

Types modulaires
Deuxime exemple, nous souhaiterions crer un type T_chiffre allant de 0 9. Nous
crirons alors :
subtype T_chiffre is integer range 0 .. 9 ;

Seulement, les oprations suivantes engendreront une erreur :


1

...

200

NUMRER LES VALEURS DUN TYPE


2
3
4
5
6

c : T_chiffre ;
BEGIN
c := 9 ;
-- Jusque l , tout va bien
c := c + 2 ;
--c devrait valoir 11 . A e ! Pas possible !
...

Il serait donc plus pratique si aprs 9, les comptes recommenaient 0 ! Dans ce cas
9+1 donnerait 0, 9+2 donnerait 1, 9+3 donnerait 2. . . Cest cela que servent les
sous-types modulaires que lon dfinira ainsi :
1

type T_chiffre is mod 10 ;

Le type T_chiffre comportera 10 nombres : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ! Pour ceux qui ont


de bonnes connaissances en mathmatiques, X = 9 quivaudra crire X 9 (10),
cest--dire que X est congru 9 modulo 10 .
Autre exemple, nous allons crer des types T_Heure, T_Minute, T_Seconde, T_Milliseconde
pour indiquer le temps. Remarquez que depuis le dbut, jintroduis mes types par la
lettre T, ce qui permet de distinguer rapidement si un mot correspond un type ou
une variable.
1
2
3
4
5

type T_Heure is mod 24 ;


type T_Minute is mod 60 ;
type T_Seconde is mod 60 ;
type T_Milliseconde is mod 1_000
-- on peut aussi
crire 1000 , mais le symbole _ ( touche 8 )
-- permet de s parer
les chiffres pour y voir plus clair

Le nombre crit aprs MOD nest pas le nombre maximal du sous-type mais
le nombre dlments contenus dans ce sous-types, le premier tant 0. Par
exemple, ces dclarations interdisent donc une variable H de type T_Heure
de valoir 24 ! Si elle vaut 23 et quon lincrmente (+1) alors elle retournera
0. De mme une variable mil de type T_Milliseconde peut prendre une valeur
entre 0 et 999 mais pas 1000 !
Une variable M de type T_Minute nest plus un integer ou un natural ! Cest
un type modulaire au fonctionnement un peu diffrent. Pour lafficher, par
exemple, il faudra crire Put(integer(M)) .

numrer les valeurs dun type


Continuons avec notre exemple du temps. Les types T_Heure, T_Minute, T_Seconde
et T_Milliseconde ont t crs comme des types modulaires. Crer un type T_Annee
ne devrait pas vous poser de soucis. Trs bien. Mais pour les jours ou les mois, il serait
bon de distinguer trois types distincts :
201

CHAPITRE 15. CRER VOS PROPRES TYPES


T_Numero_Jour : type allant de 1 31 (non modulaire car commenant 1 et
non 0)
T_Nom_Jour : type gal LUNDI, MARDI, MERCREDI. . .
T_Mois : type gal JANVIER, FVRIER, MARS. . .
Nous savons dclarer le premier type :
subtype T_Numero_Jour is integer range 1 .. 31 ;

Pour les suivants, il faut crer ce que lon appelle un type numr (un type o il faut
numrer toutes les valeurs disponibles), de la manire suivante :
1
2
3

type T_Mois is ( JANVIER , FEVRIER , MARS , AVRIL , MAI , JUIN ,


JUILLET , AOUT , SEPTEMBRE , OCTOBRE , NOVEMBRE ,
DECEMBRE ) ;
type T_Nom_Jour is ( LUNDI , MARDI , MERCREDI , JEUDI , VENDREDI , SAMEDI ,
DIMANCHE ) ;

Pourquoi ne pas se contenter dun nombre pour reprsenter le jour ou le


mois ?
Tout simplement par souci de clart et de mmorisation. Est-il plus simple dcrire :
jour = 4 ou jour = JEUDI ? mois := 5 ou mois := MAI ? La deuxime
faon est bien plus lisible. Souvenez-vous des fichiers et de leur mode douverture : ne
vaut-il pas mieux crire In_File , Out_File ou Append_File que 0, 1 ou 2 ?
Ne vaut-il mieux pas crire TRUE ou FALSE que 1 ou 0 ? De plus, il est possible dutiliser
les attributs avec les types numrs :
T_Moisfirst renverra JANVIER (le premier mois)
T_Nom_Jourlast renverra DIMANCHE (le dernier jour)
T_Moissucc(JUIN) renverra JUILLET (le mois suivant JUIN)
T_Moispred(JUIN) renverra MAI (le mois prcdant JUIN)
T_Moispos(JUIN) renverra 5 (le numro du mois de JUIN dans la liste)
T_Moisval(1) renverra FVRIER (le mois n1 dans la liste, voir aprs)
put(T_Nom_Jourimage(MARDI)) permettra dcire MARDI lcran
Les types numrs sont rangs partir de 0, donc JANVIER est le mois n0
et FVRIER le mois n1 !
Par consquent, il est mme possible de comparer des variables de type numr :
MARDI>JEUDI renverra false ! Enfin, les affectations se font de la manire la plus
simple quil soit :
202

LES TYPES STRUCTURS


1
2
3
4

...

mois : T_Mois ;
BEGIN
mois := AOUT ;

Les types structurs


Dclarer un type construit
La thorie
Nous disposons maintenant des types T_Heure, T_Minute, T_Seconde, T_Milliseconde,
T_Annee (je vous ai laiss libres de le faire), T_Mois, T_Nom_Jour et T_Numero_Jour,
ouf ! Cest un peu long tout a, non ? Il serait plus pratique de tout mettre dans un seul
type de donnes (T_Temps ou T_Date par exemple) de manire viter de dclarer
tout un escadron de variables pour enregistrer une simple date (figure 15.1).

Figure 15.1 Un type T_Temps permettrait de rassembler toutes les informations


Cest ce que nous permettent les types articles. Si le terme employ par la terminologie
Ada est Type article , bon nombre de langages parlent plutt de types structurs
ou (dailleurs en C, le mot cl est struct). Pour une fois les termes du C me semblent
plus clairs et je prfrerai donc gnralement cette terminologie. Nous allons donc
203

CHAPITRE 15. CRER VOS PROPRES TYPES


crer notre type structur en crivant TYPE T_Temps IS comme dhabitude puis
nous allons ouvrir un bloc RECORD. Lexemple par limage :
1
2
3
4

type T_Temps is
record
...
end record ;

Rappelons que to record signifie en anglais Enregistrer . Cest donc entre les
instructions RECORD et END RECORD que nous allons enregistrer le contenu du type
T_Temps, cest--dire lheure, les minutes, les secondes et les millisecondes :
1
2
3
4
5
6
7

type T_Temps is
record
H : T_Heure ;
Min : T_Minute ;
S : T_Seconde ;
Milli : T_Milliseconde ;
end record ;

Ainsi, cest comme si lon dclarait quatre variables de types diffrents sauf que ce
que lon dclare, ce sont les quatre composantes de notre type. Par la suite, nous
naurons plus quune seule variable de type T_Temps dclarer et modifier. Il est
mme possible dattribuer des valeurs par dfaut nos diffrentes composantes. Ainsi,
contrairement aux autres types de variables ou dobjets prdfinis, il sera possible
dinitialiser un objet de type T_Temps simplement en le dclarant.
1
2
3
4
5
6
7

type T_Temps is
record
H : T_Heure := 0 ;
Min : T_Minute := 0 ;
S : T_Seconde := 0 ;
Milli : T_Milliseconde := 0 ;
end record ;

8
9

t : T_Temps ;
-- Sans rien faire , t vaut
automatiquement 0 H 0 Min 0 S 0 mS !

Mise en pratique
Voici quelques petits exercices dapplication btes et mchant.
Crer un type T_Jour contenant le nom et le numro du jour.
Crer le type T_Date contenant le jour, le mois et lanne.
Solution
1

type T_Jour is

204

LES TYPES STRUCTURS


2
3
4
5

record
nom : T_Nom_Jour := LUNDI ;
num : T_Numero_Jour := 1 ;
end record ;

6
7
8
9
10
11
12

type T_Date is
record
jour : T_Jour ;
mois : T_Mois ;
annee : T_Annee ;
end record ;

Ordre des dclarations


Il est trs important de dclarer vos types dans lordre suivant :

1. les types T_Annee,T_Mois,T_Nom_Jour,T_Numero_Jour dans nimporte quel


ordre ;
2. le type T_Jour ;
3. le type T_Date !
En effet, le type T_Jour a besoin pour tre dclar que les types T_Numero_Jour
et T_Nom_Jour soient dj dclars, sinon, lors de la compilation, le compilateur va
tenter de construire notre type T_Jour et dcouvrir que pour cela il a besoin de deux
types inconnus ! Car le compilateur va lire votre code du haut vers le bas et il lve les
erreurs au fur et mesure quelles apparaissent.
Pour pallier ce problme, soit vous respectez lordre logique des dclarations, soit
vous ne voulez pas (cest bte) ou vous ne pouvez pas (a arrive) respecter cet ordre et
auquel cas, je vous conseille dcrire les spcifications des types ncessaires auparavant :
1
2
3

type T_Nom_Jour ;
-- Liste des sp cifications vitant
les soucis la compilation
type T_Numero_Jour ;
type T_Jour ;

4
5
6
7
8

type T_Date is
vrac )
record
...
end record ;

--D finition des types ( le tout en

9
10
11
12
13

type T_Jour is
record
...
end record ;

205

CHAPITRE 15. CRER VOS PROPRES TYPES


14
15
16
17
18

type T_Nom_Jour is
record
...
end record ;

19
20
21
22
23

type T_Numero_Jour is
record
...
end record ;

Dclarer et modifier un objet de type structur


Dclaration dun objet de type structur
Le langage Ada considre automatiquement les types que vous construisez comme les
siens. Donc la dclaration se fait le plus simplement du monde, comme pour nimporte
quelle variable :
1
2

t : T_Temps ;
prise_bastille : T_Date ;

Affectation dun objet de type structur


Comment attribuer une valeur notre objet t de type T_Temps ? Il y a plusieurs
faons. Tout dabord avec les agrgats, comme pour les tableaux ( la diffrence que
les valeurs ne sont pas toutes du mme type) :
1
2
3
4

t := ( 11 , 55 , 48 , 763 ) ; -- pensez respecter l ' ordre dans lequel


les composantes
-- ont t d clar es lors du record : H ,
Min ,S , Milli
-- OU BIEN
t := ( H = > 7 , Min = > 56 , Milli = > 604 , S = > 37 ) ; -- Plus de
soucis d ' orde

Cette mthode est rapide, efficace mais pas trs lisible. Si on ne connat pas bien le
type T_Temps, on risque de se tromper dans lordre des composantes. Et puis, si on
veut seulement faire une affectation sur les minutes ? Voici donc une seconde faon,
sans agrgats :
1
2
3
4
5
6

t . H := 11 ;
t . S := 48 ;
t . Min := 55 ;
t . Milli := 763 ;
...
t . Min := t . Min + 15 ;
-- Possible car t . Min est la composante
de type T_Minute qui est un type modulaire

206

LES TYPES STRUCTURS


7

ment pour autant !

-- En revanche , les t . H n ' est pas incr

Un nouvel exercice et sa correction


vous daffecter une valeur notre objet prise_bastille ! Pour rappel, la Bastille est
tombe le 14 juillet 1789 (et ctait un mardi).
1
2
3
4
5
6

prise_bastille := (( MARDI , 14 ) , JUILLET , 1789 ) ;


-- OU BIEN
prise_bastille . jour . nom := MARDI ;
prise_bastille . jour . num := 14 ;
prise_bastille . mois := JUILLET ;
prise_bastille . annee := 1789 ;

Il faudra, pour la deuxime mthode, utiliser deux fois les points pour accder la
composante de composante.

22 ! Revl les tableaux !


Compliquons notre tche en mlangeant tableaux et dates !
Tableau dans un type
Tout dabord, il pourrait tre utile que certaines dates aient un nom ("Marignan",
"La Bastille" , "Gergovie". . .) enregistr dans un string de 15 cases (par exemple).
Reprenons notre type T_Date :
1
2
3
4
5
6
7

type T_Date is
record
nom : string ( 1 .. 15 ) := " Pas d ' An 0 !!! " ;
jour : T_Jour := ( LUNDI , 1 ) ;
mois : T_Mois := JANVIER ;
annee : T_Annee := 0 ;
end record ;

Il contient dsormais un tableau (le string). Les oprations suivantes sont alors possibles :
1
2
3
4
5
6

...

prise_bastille : T_Date ;
BEGIN
prise_bastille . nom := " La Mastique
volontaire , pas d ' inqui tude
prise_bastille . nom ( 4 ) := 'B ' ;
Rectifications
prise_bastille . nom ( 9 .. 10 ) := " ll " ;

" ;

-- Erreur
--

207

CHAPITRE 15. CRER VOS PROPRES TYPES

Attention, prise_bastille nest pas un tableau, cest prise_bastille.nom qui en


est un !

Type dans un tableau (Euh. . . Van Gogh ?)


Plus compliqu, on veut crer un tableau avec des dates retenir (10 dates par
exemple). Une fois notre type T_Date dclar, dclarons notre type T_Chronologie et
une variable T :
1
2

type T_Chronologie is array ( 1 .. 10 ) of T_Date ;


T : T_Chronologie ;

Cette fois cest T qui est notre tableau et T(1), T(2). . . sont des dates. Allons-y pour
quelques affectations :
1

T ( 1 ) := ( " La Bastille

" ,( MARDI , 14 ) , JUILLET , 1789 ) ;

2
3
4
5

T ( 2 ) . Nom := " Marignan


T ( 2 ) . Annee := 1515 ;
T ( 2 ) . Jour . Num := 14 ;

" ;

6
7
8
9

T ( 3 ) . Nom := " Jour G


" ;
T ( 3 ) . Nom ( 6 ) := 'J ' ;
T ( 3 ) . Nom ( 1 .. 12 ) := " Debarquement " ;

Eh oui, les dernires sont bizarres, non ? Petite explication :


T est un tableau ;
T(3) est la 3me case du tableau qui contient une date ;
T(3).Nom est un String, soit un tableau contenant des caractres et contenu
dans la troisime case du tableau T ;
T(3).Nom(6) est la 6me case du tableau T(3).Nom et cest le caractre G
que lon modifie ensuite en J.

Les types structurs : polymorphes et mutants !


Les types structurs polymorphes
Prrequis
La notion de type polymorphe tait dj prsente dans la norme Ada83, la
norme Ada95 a intgr la Programmation Oriente Objet pour laquelle le
polymorphisme a un autre sens : le polymorphisme recouvre donc deux aspects en Ada. Nous parlerons pour linstant de polymorphisme de type, par
distinction avec le polymorphisme de classe cher la POO.
208

LES TYPES STRUCTURS : POLYMORPHES ET MUTANTS !


Le polymorphisme est la proprit davoir plusieurs formes, plusieurs apparences. En
informatique, cest lide de permettre un type donn, davoir plusieurs apparences
distinctes. Nos types T_Date et T_Temps ne conviennent plus cette partie, prenons
un nouvel exemple : un type T_Bulletin qui contient les moyennes dun lve dans
diffrentes matires. Selon que notre lve est en primaire, au collge ou au lyce,
il naura pas les mmes matires (nous nentrerons pas dans le dtail des filires et
des options par classe, lexemple doit rester comprhensible, quitte tre caricatural).
Considrons les donnes suivantes :
Un lve de primaire a du Franais, des Maths et du Sport.
Un lve de collge a du Franais, des Maths, du Sport, une langue trangre
(LV1) et de la Biologie.
Un lve de lyce a du Franais, des Maths, du Sport, une Langue trangre
(LV1), de la Biologie, de la Chimie et une deuxime Langue trangre (LV2).
Oui, je sais il manque beaucoup de choses et cest trs loin de la ralit, mais
je nai pas envie dcrire des dizaines de composantes ! :-
Notre type T_Bulletin na pas besoin davoir une composante Chimie si notre lve
est en primaire ; il doit donc sadapter llve ou plutt sa classe ! Nous allons donc
dfinir deux autres types : un type numratif T_Classe et un type structur T_Eleve.
1

type T_Classe is ( PRIMAIRE , COLLEGE , LYCEE ) ;

2
3
4
5
6
7
8

type T_Eleve is
record
nom : string ( 1 .. 20 ) ;
-- 20 caract res devraient
suffire
prenom : string ( 1 .. 20 ) ;
-- on compl tera au besoin par
des espaces
classe : T_Classe ;
-- Tout l ' int r t d ' crire un T
devant le nom de notre type ! ! !
end record ;

Crer un type polymorphe T_Bulletin


Maintenant, nous allons pouvoir crer notre type T_Bulletin, mais comme nous lavons
dit, sa structure dpend de llve et surtout de sa classe. Il faut donc paramtr notre
type T_Classe !
Paramtrer un type ? Cest possible ?

Bien sr, souvenez vous des tableaux ! Le type ARRAY est gnrique, il peut marcher
209

CHAPITRE 15. CRER VOS PROPRES TYPES


avec des integer, des float. . . Il ny a donc pas de raison que les types articles naient
pas la mme capacit. Pour notre type T_Bulletin, cela devrait donner ceci :
1
2
3

type T_Bulletin ( classe : T_Classe ) is


record
-- PARTIE FIXE

francais , math , sport : float ;


0 ;

-- ou float range 0 . 0 .. 20 .

-- PARTIE VARIABLE : COLLEGE ET LYCEE

7
8

case classe is
when PRIMAIRE = >
null ;
when COLLEGE | LYCEE = >
lv1 , bio : float ;

9
10
11
12
13
14

-- PARTIE SPECIFIQUE AU LYCEE

15
16
17
18
19
20
21
22
23
24

case classe is
when LYCEE = >
chimie , lv2 : float ;
when others = >
null ;
end case ;
end case ;
end record ;

Ce paramtrage nous permet ainsi dviter davoir dfinir plusieurs types T_Bulletin
_Primaire, T_Bulletin_College, T_Bulletin_Lycee. Il suffit de fournir en paramtre
la classe en question. Attention toutefois ! Le paramtre fournit doit tre discret.
Parce quun paramtre peut-tre bruyant ?

Non, discret nest pas ici le contraire de bruyant ! Cest une notion mathmatique qui
signifie que les valeurs sont toutes isoles les unes des autres ou, dune autre faon,
si je prends deux valeurs au hasard, il ny a quun nombre fini de valeurs comprises
entre les deux que jai prises. Par exemple : les integer sont de type discret (entre 2 et
5, il ny a que 3 et 4, soit 2 nombres) ; les nombres rels (et le type float) ne sont pas
discrets (entre 2.0 et 5.0, il y a une infinit de nombres comme 3.0 ou 3.01 ou 3.001 ou
3.00000000002 ou 4.124578521. . .). De mme, nos types numrs sont discrets, mais
les types structurs ne sont pas accepts comme discrets !
Autre souci : ctait pas plus simple dutiliser IF/ELSIF plutt que des CASE
imbriqus ?
210

LES TYPES STRUCTURS : POLYMORPHES ET MUTANTS !


Vous vous doutez bien que non ! Les conditions sont strictes lorsque lon construit un
type polymorphe. Pas de IF, ne pas faire de rptition dans linstruction WHEN (ne pas
rpter la composante lv1 par exemple), un seul CASE et cest fini (do lobligation de
les imbriquer plutt que den crire deux distincts). . . Cest quon ne ferait plus ce que
lon veut !
Des objets polymorphes
Maintenant que nous avons dclar nos types, il faut dclarer nos variables :
1
2
3
4

Kevin : T_Eleve := ( " DUPONT


" , PRIMAIRE ) ;
Laura : T_Eleve := ( " DUPUIS
" , LYCEE ) ;
Bltn_Kevin : T_Bulletin ( Kevin . classe ) ;
Bltn_Laura : T_Bulletin ( Laura . classe ) ;

" ," Kevin


" ," Laura

Vous devriez avoir remarqu deux choses :


Il faut fournir un paramtre quand on dfinit Bltn_Kevin et Bltn_Laura, sans
quoi le compilateur chouera et vous demandera de prciser votre pense.
Par consquent, lorsque vous dclarez les variables Kevin et Laura, vous devez les
avoir initialises (ou tout du moins avoir initialis Kevin.classe et Laura.classe).
Nous avons ainsi obtenu deux variables composites Bltn_Kevin et Bltn_Laura qui
nont pas la mme structure. Bltn_Kevin a comme composantes :
Bltn_Kevin.classe : accessible seulement en lecture, pour des tests par exemple.
Bltn_Kevin.Math, Bltn_Kevin.Francais et Bltn_Kevin.sport : accessibles en lecture et en criture.
Quant elle, la variable Bltn_Laura a davantage de composantes :
Bltn_Laura.classe : en lecture seulement
Bltn_Laura.Math, Bltn_Laura.francais, Bltn_Laura.sport : en lecture et criture (comme Bltn_Kevin)
Bltn_Laura.lv1, Bltn_Laura.lv2, Bltn_Laura.bio, Bltn_Laura.chimie : en lecture et criture.
Le paramtre Bltn_Laura.classe se comporte comme une composante mais
nest pas modifiable ! Impossible dcrire par la suite Bltn_Laura.classe :=
PRIMAIRE ! ! !
Il est ensuite possible de crer des sous-types (pour viter des erreurs par exemple) :
1
2
3

type T _ B ul l et in_Primaire is T_Bulletin ( PRIMAIRE ) ;


type T _Bu ll et in_College is T_Bulletin ( COLLEGE ) ;
type T_Bulletin_Lycee is T_Bulletin ( LYCEE ) ;

211

CHAPITRE 15. CRER VOS PROPRES TYPES

Les types structurs mutants


Comment peut-on muter ?
Je vous arrte tout de suite : il ne sagit pas de monstres ou de types avec des pouvoirs
spciaux ! Non, un type mutant est simplement un type structur qui peut muter, cest-dire changer. Je vais pour cela reprendre mon type T_Bulletin. Attention a va aller
trs vite :
1
2
3

type T_Bulletin ( classe : T_Classe := PRIMAIRE ) is


record
-- PARTIE FIXE

francais , math , sport : float ;


0 ;

-- ou float range 0 . 0 .. 20 .

-- PARTIE VARIABLE : COLLEGE ET LYCEE

7
8

case classe is
when PRIMAIRE = >
null ;
when COLLEGE | LYCEE = >
lv1 , bio : float ;

9
10
11
12
13
14

-- PARTIE SPECIFIQUE AU LYCEE

15
16
17
18
19
20
21
22
23
24

case classe is
when LYCEE = >
chimie , lv2 : float ;
when others = >
null ;
end case ;
end case ;
end record ;

Euh. . . Taurais pas oublier de faire une modification aprs ton copier-coller ?

Non, regardez bien mon paramtre classe tout en haut : il a dsormais une valeur par
dfaut : PRIMAIRE ! Vous allez me dire que a ne change pas grand chose, et pourtant
si ! Ainsi vous allez pouvoir crire :
1
2

Eric : T_Eleve ;
-- non pr d fini
Bltn_Eric : T_Bulletin ;
-- pas la peine d ' indiquer un
param tre , il est d j pr d fini

Et dsormais, Bltn_Eric est un type mutant, il va pouvoir changer de structure en


cours dalgorithme ! Nous pourrons ainsi crire :
212

LES TYPES STRUCTURS : POLYMORPHES ET MUTANTS !


1
2
3
4
5
6
7

...

Eric : T_Eleve ;
Bltn_Eric : T_Bulletin ;
BEGIN
Bltn_Eric := ( COLLEGE , 12 .5 , 13 .0 , 9 .5 , 15 .0 , 8 . 0 ) ;
Bltn_Eric := Bltn_Kevin ;
-- On
suppose bien - s r que Bltn_Kevin est d j " pr rempli "
...

Par contre, comme le fait dattribuer une classe Bltn_Eric va modifier toute
sa structure, il est interdit de noter uniquement Bltn_Eric.classe :=
COLLEGE ;
Toute changement de la composante classe doit se faire de manire globale , cest-dire tout en une seule fois ! Autre contrainte, si vous dclarez votre objet Bltn_Eric
de la manire suivante :
1

Bltn_Eric : T_Bulletin ( COLLEGE ) ;

Alors votre objet ne sera plus mutable ! On revient au cas dun simple type structur
polymorphe. Donc ne spcifiez pas la classe durant la dclaration si vous souhaitez
pouvoir changer la structure.
Toujours plus loin !
Pour aller plus loin il serait judicieux de fusionner les types T_Eleve et T_Bulletin
de la manire suivante :
1
2
3

type T_Bulletin ( classe : T_Classe := PRIMAIRE ) is


-- !!! POUR L ' INSTANT , PAS DE CHANGEMENTS !!!
record
-- PARTIE FIXE

4
5

francais , math , sport : float ;


0 ;

-- ou float range 0 . 0 .. 20 .

6
7

-- PARTIE VARIABLE : COLLEGE ET LYCEE

8
9
10
11
12
13

case classe is
when PRIMAIRE = >
null ;
when COLLEGE | LYCEE = >
lv1 , bio : float ;

14
15

-- PARTIE SPECIFIQUE AU LYCEE

16
17
18
19

case classe is
when LYCEE = >
chimie , lv2 : float ;

213

CHAPITRE 15. CRER VOS PROPRES TYPES


20
21
22
23
24

when others = >


null ;
end case ;
end case ;
end record ;

25
26
27
28
29
30
31

type T_Eleve is
MODIFICATION SE FAIT ICI !!!
record
nom : string ( 1 .. 20 ) ;
prenom : string ( 1 .. 20 ) ;
bulletin : T_Bulletin ;
end record ;

-- !!! LA

Inutile de garder deux types : le type T_Bulletin sera intgr au type T_Eleve. En
revanche, il faudra que le type T_Eleve soit dclar aprs le type T_Bulletin. Si cela se
rvlait impossible, vous pouvez toujours viter ce problme en crivant la spcification
de T_Eleve, puis en dcrivant T_Bulletin et enfin T_Eleve :
1

type T_Eleve ;
-- !!! AJOUT D ' UNE
SPECIFICATION !!! Le reste ne change pas .

2
3
4
5
6

type T_Bulletin ( classe : T_Classe := PRIMAIRE ) is


record
...
end record ;

7
8
9
10
11

type T_Eleve is
record
...
end record ;

Plus besoin non plus de garder une composante classe au sein du type T_Eleve puisquelle existe dj dans la composante bulletin !
Vous voil dsormais arms pour crer vos propres types (modulaires, numrs, soustypes, structurs [non paramtr, polymorphe ou mutant]). Combin lusage des tableaux, voil qui largit vraiment nos horizons et nos possibilits. Si nous dveloppions
notre type T_Eleve et les fonctionnalits qui sy rapportent, nous pourrions crer un
package volumineux et complet permettant dtablir une sorte de base de donnes des
lves dun tablissement : cration et modification dun fichier lve enregistrant
de nombreuse variables de type T_Eleve, le numro de tlphone, ladresse, les notes
par matire (dans des tableaux inclus dans le type T_Eleve). . . la seule limite est votre
imagination et votre temps libre.
Dailleurs, nous allons raliser ce genre de programme dans le prochain chapitre : il
sagira de crer un logiciel grant, non pas des lves, mais votre collection de CD,
DVD. . . Comme vous lavez devin, notre prochain chapitre ne sera pas thorique, ce
sera un TP ! Alors, si certains points vous posent encore problme, nhsitez pas les
revoir.
214

LES TYPES STRUCTURS : POLYMORPHES ET MUTANTS !

En rsum
Un sous-type ou SUBTYPE, est une restriction dun type prexistant. Par consquent, les sous-types bnficient de toutes les fonctionnalits offertes au type
initial.
Prfrez crer un type numr plutt que de reprsenter certaines proprits
par des nombres. Il est plus simple de comprendre quune personne est de sexe
masculin que de comprendre que son sexe est le n0.
Les types articles ou structurs sont cres laide du mot-cl RECORD. Contrairement aux tableaux, ils permettent de rassembler de nombreuses informations
de types trs divers dans des composantes. Ces composantes peuvent tre ellesmmes des types articles.
Les types articles peuvent tre paramtrs la manire des fonctions. Ce paramtrage permet de crer des types polymorphes, cest dire quune partie de
la structure du type dpendra du paramtre fourni. Lorsquun variable de type
polymorphe est dclare, son type doit absolument tre paramtr.
En fournissant une valeur par dfaut au paramtre, vous pouvez dfinir un type
mutant dont la structure pourra voluer au cours de votre code. Plus aucun
paramtrage nest alors ncessaire la dclaration de votre variable.

215

CHAPITRE 15. CRER VOS PROPRES TYPES

216

Chapitre

16

TP : Logiciel de gestion de bibliothque


Difficult :
Bienvenue dans le premier TP de la partie III, cest--dire le second TP de ce cours. Comme
je vous le disais dans le prcdent chapitre, nous allons cette fois crer un logiciel pour
grer votre collection de CD, DVD, VHS, BluRay. . . Plus prcisment, notre programme
permettra denregistrer un film, un album de musique, un jeu vido ou un autre type de
donne, mais aussi de consulter a posteriori la liste des uvres. Je vous propose dappeler
notre programme Maktaba, ce qui signifie Bibliothque en Swahili (pourquoi pas ? On
a bien Ubuntu, Amarok. . .).
Cela nous permettra de rutiliser les types de donnes structurs (pour enregistrer un
film, il faut indiquer son titre, le type de support, son genre. . .), les types numrs (les
supports CD, DVD. . .), les fichiers binaires ou texte (pour enregistrer notre liste duvres
ou exporter des informations sur une uvre), les strings (pour enregistrer les titres des
morceaux de musique par exemple) ou encore les packages (notre programme devrait tre
assez consquent).

217

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


Cette fois encore, je commencerai ce TP en vous fournissant un cahier des charges : que
veut-on comme fonctionnalits ? Comme donnes ? Quelle structure pour nos fichiers
et notre code source ? Puis, je vous guiderai dans la conception du programme : nous
ne raliserons pas tout en seule fois, je commencerai par vous demander de raliser un
programme simple avant dajouter des fonctionnalits supplmentaires ou de prendre
en charge des cas particuliers. Enfin, je vous transmettrai les sources et les spcifications
dune solution possible (bien entendu il ny a pas quune seule solution mais plusieurs,
chacune ayant ses avantages et inconvnients). En conclusion, comme pour le premier
TP, je vous soumettrai quelques ides damliorations possibles de notre programme.
Prt dmarrer ce nouveau challenge ? Alors au travail !

Cahier des charges


Quelles donnes pour quels types de donnes ?
Pour tablir les types dont nous pourrions avoir besoin, nous devons lister les donnes
ncessaires notre programme pour lenregistrement ou la lecture.
Le contenu dune uvre
Nous souhaitons enregistrer des uvres, mais quest-ce quune uvre ? Cest avant
tout :

un titre : autant en emporte le vent , Mario bros 3 , Nevermind . . .


une catgorie : FILM, JEU (VIDO), ALBUM (DE MUSIQUE), AUTRE. . .
un support : CD, DVD, BLURAY, VHS, HDDVD. . .
une note : de zro trois toiles.

Ensuite, selon la catgorie de luvre, dautres informations peuvent tre ncessaires.


Pour un film, nous aurons besoin :
du nom du ralisateur ;
de savoir si le film est en VF.
Pour un jeu vido, nous aurons besoin :
de la console : Nes, PS1, PC, Nintendo64. . . ;
de savoir si vous avez termin le jeu.
Pour un album de musique, nous aurons besoin :
du nom de lartiste : Nirvana , ACDC , Bob Marley . . .
Dune liste des morceaux dans lordre : Come as you are , Something in the
way . . .
Il serait bon galement quun type doeuvre par dfaut existe.
218

CAHIER DES CHARGES


Des types en cascade
Suite cela, vous avez du comprendre que nous aurons besoin dun type T_oeuvre qui
soit structur et polymorphe (voire mme mutable, ce serait encore mieux et je vous
le conseille fortement). Ce type structur devrait comprendre de nombreuses composantes de types aussi divers que du texte (string mais pas dunbounded_string, je vous
expliquerai plus tard pourquoi), des tableaux, des boolens, des types numrs (pour
les supports ou la catgorie). . .
Rien que a ? Tavais pas plus long ?

Cest en effet beaucoup et en mme temps bien peu ! Pensez que nous aurions pu enregistrer les dures des films ou des morceaux, si les films sont en
VOSTFR, la pochette de lalbum par exemple, lemplacement o est sens
tre rang le CD (sur ltagre, dans la tour, chez le voisin. . .) ou encore
sa date dachat ou de parution. . . Ayez en tte que la plupart des logiciels
actuels sont bien plus complexes que cela, notre programme nest que peu de
chose ct. Toutefois, il vous sera possible de le perfectionner plus tard.
Tout cela laisse supposer que nous devrions crer un package spcifique pour dclarer
notre type T_Oeuvre afin de librer notre code source principal. Enfin, dernire information, nos textes devant tre enregistrs dans des fichiers, nous ne pourrons pas
utiliser les unbounded_string, mais seulement les string. Lexplication technique vous
sera rvle la fin de la partie III, lors du chapitre sur les listes. Mais cela implique
donc que vos strings devront tre suffisamment longs pour pouvoir accueillir des titres
rallonge comme celui-ci, extrait dun album de Nirvana : Frances Farmer will have
her revenge on Seattle .
Les types de fichiers
Qui dit enregistrement, dit ncessairement fichiers. Aux vues de notre type structur
T_Oeuvre, cela signifie que nous aurions besoin dun fichier binaire pour jouer le rle
de base de donne (squentiel ou accs direct, cest vous de voir. Pour ma part, jai
choisi les fichiers squentiels dont la manipulation sera plus simple pour vous). Il serait
mme bon de sparer les bases de donnes (une pour les jeux, une pour la musique. . . ).
Ces fichiers porteront par consquent les noms de ListeJeu.bdd , "ListeAlbum.bdd",
ListeFilm.bdd et ListeAutre.bdd (bdd = Base De Donnes).

Quelle architecture pour les fichiers


Notre programme ayant besoin de divers types de fichiers, il serait judicieux de ne pas
tout mlanger.
219

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


Maktaba.exe devra se trouver dans un rpertoire Maktaba, libre vous de
placer un raccourci sur le bureau si vous le souhaitez.
Les bases de donnes ListeJeu.bdd et autres devront se trouver dans un sousrpertoire data .
Un sous-rpertoire Manual permettra lenregistrement dun fichier texte.
Cela nous fait donc un rpertoire principal et deux sous-rpertoires.

Quelles fonctionnalits pour quelles fonctions et procdures ?


Maktaba.exe devra proposer les fonctionnalits suivantes :
Saisie dune nouvelle uvre par lutilisateur.
Enregistrement dune nouvelle uvre par lutilisateur (ajout dans la base de
donne).
Modification par lutilisateur dune uvre existante.
Suppression par lutilisateur dune uvre existante.
Affichage de la base de donne ou dune oeuvre dans la console (sous la forme
dun tableau).
Accs aux fonctionnalits par lignes de commande : lutilisateur devra taper
add, modify, delete, print pour accder une fonctionnalit. Le programme grera
bien-sr dventuelles erreurs de frappe commises par lutilisateur.
Possibilit daccder un manuel en console par la commande manual. Ce manuel sera rdig dans un fichier texte manual.txt plac dans le sous-rpertoire
manual voqu prcdemment et dcrira les diffrentes commandes possibles.

Architecture du code source


Argh ! ! ! Mais jamais je ne parviendrai faire tout a ! Je savais bien que je
naurais pas du me lancer dans cette galre !
Gardez espoir ! Ce ne sera pas aussi compliqu que cela peut paratre. En revanche,
cela risque dtre long (notamment le codage des procdures de saisie et daffichage),
donc il sera ncessaire dy aller tape par tape et dadopter une approche par modules
(tiens, a devrait vous rappeler les packages a). Nous aurons donc crer les fichiers
Ada suivants :
Maktaba.adb : la procdure principale qui ne se chargera que de linterface
utilisateur, du cur du logiciel.
Maktaba_Types.ads : pour dclarer nos types et nos constantes.
Maktaba_Functions.adb et Maktaba_Functions.ads : pour les diffrentes
fonctionnalits lies la base de donnes (lecture, saisie, enregistrement, affichage).
220

CONCEPTION DU PROGRAMME (SUIVEZ LE GUIDE)

Conception du programme (suivez le guide)


Cette partie nest pas obligatoire, elle permettra toutefois ceux qui hsitent se
lancer ou qui ne voient pas comment faire, de trouver une mthode ou des voies pour
programmer. Attention, il ne sagit pas dune solution toute faite (celle-ci sera fournie
la fin) mais plutt dun guide et je vous invite essayer de raliser ce TP par vous
mme, en recourant le moins possible cette partie.

Cration des types


Nous allons commencer par crer deux fichiers : Maktaba.adb et Maktaba_Types.ads.
Notre fichier Maktaba.adb ne contiendra pour linstant pas grand chose :
1
2

WITH Maktaba_Types ;
WITH Ada . Text_IO ;

USE Maktaba_Types ;
USE Ada . Text_IO ;

3
4
5
6

PROCEDURE Maktaba IS
Oeuvre : T_Oeuvre ;
BEGIN

7
8

END Maktaba ;

Comme vous pouvez le constater il ny a quasiment rien. Nous allons nous concentrer sur le fichier Maktaba_Types.ads et les diffrents types : nous devons crer un
type structur et polymorphe (et mme mutable) T_Oeuvre. Ce type devra contenir
diffrentes composantes :
titre de luvre, realisateur, console et artiste seront des strings (avec les contraintes
que cela implique en terme de taille), mais surtout pas des unbounded_string
(cela poserait problme pour lenregistrement). Il serait bon que les strings soient
initialiss.
Categorie et support seront des types numrs.
VF et Termine seront des boolean (vrai ou faux).
Note sera un sous-type natural de 0 3 ou Integer (voire modulaire).
Morceaux sera un tableau de 30 strings (ou plus).
vous donc de crer ce type T_Oeuvre ainsi que les types numrs T_Categorie
et T_Support et le sous-type T_Note. Pensez toutefois que, pour tre polymorphe,
T_Oeuvre doit dpendre de la catgorie de loeuvre et que pour tre mutable, cette
catgorie doit tre initialise :
1
2
3
4
5
6

type T_Oeuvre ( categorie : T_Categorie := # Une_Valeur #) is


record
...
case categorie is
when FILM = > ...
...

221

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


7
8

end case ;
end record ;

Affichage dune uvre


Il est temps de crer nos fichiers Maktaba_Functions.adb et Maktaba_Functions.ads !
Nous aurons besoin lavenir dafficher lintgralit de notre base de donnes, mais
avant dafficher 300 uvres, nous devrions crer une fonction ou procdure qui en affiche
une et une seule. Cela nous permettra davoir dors et dj un programme Maktaba.exe
oprationnel qui saisirait une uvre (arbitrairement pour linstant) puis lafficherait.
procedure Affichage ( oeuvre : T_Oeuvre ) ;

Pensez faire un affichage compact et clair : il y aura terme des dizaines doeuvres !
Pensez galement terminer laffichage par un ou plusieurs new_line pour viter les
soucis daffichage plus tard.

Saisie dune oeuvre


La premire chose faire par la suite sera de crer une procdure ou une fonction de
saisie dune uvre. Le sous-programme de saisie ne sera pas compliqu mettre en
oeuvre mais sera long rdiger car il devra prvoir toutes les composantes. Mais avant
de vous lancer dans la saisie dune uvre, je vous conseille dimplmenter une fonction
de saisie de string. Il existe bien get_line ou get, mais si vous souhaitez saisir un string
de taille 20, il ne faut pas que lutilisateur saisisse un texte de 35 ou 3 caractres. Or
lutilisateur final naura gnralement aucune connaissance de ces contraintes, donc je
conseille de commencer par l :
function get_text ( taille : natural ) return string ;

Autre indication pour simplifier votre code, il serait bon que votre fonction de saisie
duvre (appelons-la Saisie_Oeuvre) ne soccupe pas de la saisie de la catgorie. Ce
travail sera effectu par une fonction tierce (Saisie_Categorie) qui fournira la premire
la catgorie saisir. Cela simplifiera grandement votre travail et votre rflexion. De
manire gnrale, une grande partie de vos fonctions et procdures devraient avoir la
catgorie de luvre en paramtre.
Function Saisie_Categorie return T_Categorie ;
Function Saisie_Oeuvre ( Cat : T_Categorie ) return T_Oeuvre ;

1
2

Les saisies de strings se feront avec notre fonction get_text. En revanche, les saisies
dentiers devront grer les cas o lutilisateur entrerait une note suprieure 3 :
1
2
3

TANT QUE choix > 3


| Saisir ( choix )
FIN DE BOUCLE

De mme, pour saisir un boolen ou un type structur, vous pourrez proposer lutilisateur un choix similaire celui-ci :
222

CONCEPTION DU PROGRAMME (SUIVEZ LE GUIDE)

Votre
1.
2.
3.
Votre

film est enregistr sur :


un CD
un DVD
une VHS
film est - il en VF ? ( O : oui / N : Non ) _

Donc prvoyez les cas o lutilisateur rpondrait de travers pour limiter les plantages.

Gestion des fichiers


La plupart des oprations suivantes se feront uniquement sur les fichiers : sauvegarde
dans la base de donnes (bdd), affichage dune bdd, modification dun lment dune
bdd, suppression dun lment dune bdd. . . Nous devrons crer un package pour manipuler des fichiers binaires. Comme dit prcdemment, je vous invite utiliser les
fichiers squentiels plutt qu accs direct pour viter de rajouter de la difficult la
difficult (bien sr vous tes libres de votre choix, le type de fichier binaire ne fait pas
partie du cahier des charges).
Sauvegarde et affichage avec la BDD
Limplmentation de ces deux fonctionnalits ne devrait pas poser de problme. Il vous
suffira douvrir un fichier, de le fermer, en pensant entre temps soit ajouter un lment
(Append_File), soit parcourir le fichier pour le lire (In_file).
Mais si vous avez essay dimplmenter ces fonctionnalits, vous avez du vous rendre
compte quelles exigent toutes les deux de commencer par traiter une question toute
bte : Quel fichier dois-je ouvrir ? . Et cette question, vous devrez vous la reposer
chaque fois. Il y a donc plusieurs faons de faire : soit on la joue gros bourrin et alors
Vive le copier-coller ! , soit on est un peu plus fut et on rdige une procdure qui
se charge douvrir le bon fichier selon la catgorie fournie en paramtre. Ce paramtre
peut tre lui-mme fourni par la fonction Saisie_categorie voque prcdemment.
1
2
3

procedure Ouvrir ( cat : T_Categorie ) ;


procedure Affichage_BDD ( cat : T_Categorie ) ;
procedure Sauvegarde ( Oeuvre : T_Oeuvre ) ;

Modification et suppression dun lment de la BDD


Le plus simple pour effectuer cette opration est de ne manipuler que des fichiers : pas
la peine de se casser la tte tenter de supprimer un lment du fichier. Voici une
mthode pour supprimer llment numro N dun fichier F :
1
2
3
4

Ouvrir
Ouvrir
Copier
sauter

le fichier F ( Nom : truc )


le fichier G ( Nom : truc2 )
les (N - 1 ) premiers l ments de F dans G
le N - me l ment de F

223

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


Copier le reste des l ments de F dans G
Supprimer F
Recreer F ( Nom : Truc ) comme une copie de G
Fermer F
Fermer G

5
6
7
8
9

Un raisonnement similaire peut tre effectu pour la modification de la BDD.


Affichage du manuel
L, jespre bien que vous navez pas besoin de moi !

Les commandes
Jusque l, notre fichier Maktaba.adb ne contient rien de bien srieux, il ne nous sert qu
tester nos procdures et fonctions. Mais puisque nous avons fini, nous allons pouvoir le
rdiger correctement. Lide ici est simple : si lutilisateur tape un mot particulier, le
programme ralise une opration particulire. Nous allons donc rutiliser notre fonction
get_text pour la saisie des commandes. Le corps de la procdure principale sera simple :
une boucle infinie qui se contente de demander de saisir du texte et qui, si le texte
correspond une commande connue, lance quelques sous-programmes dj rdigs.
Lun de ces strings entranera bien entendu la sortie de la boucle (commande : quit ou
exit par exemple). En cas derreur de lutilisateur, le programme affichera toutefois une
phrase du genre Si vous ne comprenez rien, vous navez qu taper Manual pour lire
ce #*%$ de Manuel (RTFM) (en plus aimable bien sr :- ) de faon ne pas
laisser lutilisateur sans indications.

Solutions possibles
Comme promis, voici une solution possible comparer avec votre travail.
Maktaba.adb :
1

--

--

--

--

--

--

224

-------------------------------------------------------------------------

PROJET MAKTABA

MAKTABA . ADS

SOLUTIONS POSSIBLES
7
8
9
10
11
12
13
14

--

--- AUTEUR : KAJI9


--- DATE : 13 / 11 / 2011
----- Contient la proc dure principale du logiciel MAKTBA ainsi
que les
--- proc dures d ' affichage et de saisie .
---------------------------------------------------------------------------

15
16

-- Retrouvez le tuto l ' adresse suivante \ url { http :// www .


siteduzero . com / tutoriel -3 - 558031 -1 - second - tp - un - logiciel - de gestion - de - bibliotheque . html }

17
18
19
20
21

with Maktaba_Types ;
with Maktab a_Functions ;
Makta ba_Functions ;
with Ada . Characters . Handling ;
Handling ;
with Ada . Text_IO ;

use Maktaba_Types ;
use
use Ada . Characters .
use Ada . Text_IO ;

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

procedure Maktaba is
oeuvre : T_Oeuvre ;
reponse : string ( 1 .. 6 ) ;
begin
loop
Put ( " > " ) ; reponse := Get_Text ( 6 ) ;
reponse := To_Upper ( Reponse ) ;
if reponse = " QUIT " or reponse = " EXIT "
then exit ;
elsif reponse = " NEW
"
then oeuvre := saisie ( get_categorie ) ;
sauvegarde ( oeuvre ) ;
elsif reponse = " MANUAL "
then Affichage_Manuel ;
elsif reponse = " INIT " or reponse = " ERASE "
then creation ( get_categorie ) ;
elsif reponse = " PRINT "
then affichage_bdd ( get_categorie ) ;
elsif reponse = " EDIT "
then Edit_bdd ( get_categorie ) ;
else put_line ( " Commande inconnue . Pour plus d '

225

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE

44
45
46

informations , tapez l ' instruction MANUAL . " ) ;


end if ;
end loop ;
end Maktaba ;

Maktaba_Types.ads :
1

--

--

--

--

--

--

--

8
9
10
11
12
13
14

------------------------------------------------------------------------

PROJET MAKTABA

MAKTABA_TYPES . ADS

--

--- AUTEUR : KAJI9


--- DATE : 13 / 11 / 2011
----- Contient les diff rents types n cessaires au fonctionnement
du logiciel --- MAKTABA .
------------------------------------------------------------------------

15
16

-- Retrouvez le tuto l ' adresse suivante \ url { http :// www .


siteduzero . com / tutoriel -3 - 558031 -1 - second - tp - un - logiciel - de gestion - de - bibliotheque . html }

17
18

package Maktaba_Types is

19
20
21
22

type T_Categorie is ( FILM , JEU , ALBUM , AUTRE ) ;


type T_Support is ( CD , DVD , BLURAY , VHS , HDDVD ) ;
type T_Morceaux is array ( 1 .. 30 ) of string ( 1 .. 50 ) ;

23
24
25
26

226

type T_Oeuvre ( categorie : T_Categorie := AUTRE ) is


record
titre : string ( 1 .. 50 ) := ( others = > ' ') ;

SOLUTIONS POSSIBLES
support : T_Support := CD ;
note : natural range 0 .. 3 ;

27
28
29

case categorie is
when FILM = > realisateur : string ( 1 .. 20 ) := ( others
= > ' ') ;
VF : boolean := false ;
when JEU
= > console : string ( 1 .. 20 ) := ( others = > '
') ;
termine : boolean := false ;
when ALBUM = > artiste : string ( 1 .. 20 ) := ( others = > '
') ;
morceaux : T_Morceaux := ( others = >(
others = > ' ') ) ;
when AUTRE = > null ;
end case ;
end record ;

30
31
32
33
34
35
36
37
38
39
40

type
type
type
type

41
42
43
44

T_Film is new T_Oeuvre ( FILM ) ;


T_Jeu is new T_Oeuvre ( JEU ) ;
T_Album is new T_Oeuvre ( ALBUM ) ;
T_Autre is new T_Oeuvre ( AUTRE ) ;

45
46

end ;

Maktaba_Functions.ads :
1

--

--

--

--

--

--

--

8
9
10
11
12

---------------------------------------------------------------------------

PROJET MAKTABA

MAKTABA_FUNCTIONS . ADS

--

--- AUTEUR : KAJI9


--- DATE : 13 / 11 / 2011
----- Contient les fonctions de saisie , d ' affichage , de sauvegarde
... du
--- logiciel MAKTABA .

227

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE

13

--

14

--

---

--------------------------------------------------------------------

15
16

-- Retrouvez le tuto l ' adresse suivante \ url { http :// www .


siteduzero . com / tutoriel -3 - 558031 -1 - second - tp - un - logiciel - de gestion - de - bibliotheque . html }

17
18
19

with Maktaba_Types ;
WITH Ada . Sequential_IO ;

use Maktaba_Types ;

20
21

package Maktaba_Functions is

22
23
24
25

Package P_Fichier is new Ada . Sequential_IO ( T_Oeuvre ) ;


use P_Fichier ;
subtype T_Fichier is P_Fichier . File_type ;

26
27

procedure sauvegarde ( Oeuvre : in T_Oeuvre ) ;

28
29

procedure sauvegarde ( oeuvre : in T_Oeuvre ; rang : natural )


;

30
31

procedure creation ( cat : T_categorie ) ;

32
33

procedure Ouvrir ( F : in out T_Fichier ; mode : P_Fichier .


File_Mode := P_Fichier . In_file ; cat : T_Categorie ) ;

34
35

procedure supprimer ( cat : T_Categorie ; rang : natural ) ;

36
37

--

SAISIES

38
39

function get_text ( size : integer ) return string ;

40
41

function get_categorie return T_Categorie ;

42
43

function saisie ( cat : T_Categorie ) return T_Oeuvre ;

44
45

--

MANIPULATION DE LA BDD

46
47

procedure affichage_oeuvre ( Oeuvre : in T_Oeuvre ) ;

48
49

procedure affichage_bdd ( cat : T_categorie ) ;

50
51

procedure Edit_bdd ( cat : T_Categorie ) ;

52
53
54

228

--

Manuel

SOLUTIONS POSSIBLES
procedure affichage_manuel ;

55
56
57

end Makt aba_Functions ;

Maktaba_Functions.adb :
1

--

--

--

--

--

--

--

8
9
10
11
12
13
14

------------------------------------------------------------------------

PROJET MAKTABA

----

MAKTABA_FUNCTIONS . ADB

--

--- AUTEUR : KAJI9


--- DATE : 13 / 11 / 2011
----- Contient les fonctions de saisie , d ' affichage , de sauvegarde
... du
--- logiciel MAKTABA .
---------------------------------------------------------------------------

15
16

-- Retrouvez le tuto l ' adresse suivante \ url { http :// www .


siteduzero . com / tutoriel -3 - 558031 -1 - second - tp - un - logiciel - de gestion - de - bibliotheque . html }

17
18
19
20
21

with Ada . Text_IO ;


with Ada . Integer_Text_IO ;
Integer_Text_IO ;
with Ada . Characters . Handling ;
Handling ;
with ada . Strings . Unbounded ;
Unbounded ;

use Ada . Text_IO ;


use Ada .
use Ada . Characters .
Use ada . Strings .

22
23

package body Maktaba_Functions is

24

229

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -GESTION DES FICHIERS
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

25
26
27
28
29
30
31
32
33
34
35

procedure sauvegarde ( Oeuvre : in T_Oeuvre ) is


F : T_Fichier ;
begin
ouvrir (F , Append_File , oeuvre . categorie ) ;
write (F , Oeuvre ) ;
close ( F ) ;
end sauvegarde ;

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

procedure sauvegarde ( oeuvre : in T_Oeuvre ; rang : natural ) is


F , G : T_Fichier ;
tmp : T_Oeuvre ;
begin
-Ouverture de F en In et de G en Append
ouvrir (F , In_File , oeuvre . categorie ) ;
create (G , Append_File , Name ( F ) & " 2 " ) ;
-copie de F + oeuvre dans G
for i in 1 .. rang - 1 loop
read (F , tmp ) ;
write (G , tmp ) ;
end loop ;
write (G , oeuvre ) ;
read (F , tmp ) ; -- lecture de l ' l ment supprimer
while not end_of_file ( F ) loop
read (F , tmp ) ;
write (G , tmp ) ;
end loop ;
-Suppression de F / recr ation de F
-fermeture de G / r ouverture de G
delete ( F ) ;
creation ( oeuvre . categorie ) ;
ouvrir (F , Append_File , oeuvre . categorie ) ;
close ( G ) ;
open (G , In_File , Name ( F ) & " 2 " ) ;
-copie de G dans F
while not end_of_file ( G ) loop
read (G , tmp ) ;
write (F , tmp ) ;
end loop ;
-- Fermeture de F / Suppression de G
close ( F ) ;
delete ( G ) ;
end sauvegarde ;

71
72
73
74

230

procedure Creation ( cat : T_categorie ) is


F : T_Fichier ;
begin

SOLUTIONS POSSIBLES
75
76
77
78
79
80
81
82
83
84
85

case cat is
when FILM = > create (F , out_file , " ./ data / ListeFilm . bdd " ) ;
close ( F ) ;
when JEU = > create (F , out_file , " ./ data / ListeJeu . bdd " ) ;
close ( F ) ;
when ALBUM = > create (F , out_file , " ./ data / ListeAlbum . bdd " )
;
close ( F ) ;
when AUTRE = > create (F , out_file , " ./ data / ListeAutre . bdd " )
;
close ( F ) ;
end case ;
end creation ;

86
87
88
89
90
91
92
93
94
95
96
97

procedure Ouvrir ( F : in out T_Fichier ;


mode : P_Fichier . File_Mode := P_Fichier .
In_file ;
cat : T_Categorie ) is
begin
case cat is
when FILM = > open (F , mode , " ./ data / ListeFilm . bdd " ) ;
when JEU
= > open (F , mode , " ./ data / ListeJeu . bdd " ) ;
when ALBUM = > open (F , mode , " ./ data / ListeAlbum . bdd " ) ;
when AUTRE = > open (F , mode , " ./ data / ListeAutre . bdd " ) ;
end case ;
end Ouvrir ;

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

procedure supprimer ( cat : T_Categorie ; rang : natural ) is


F , G : T_Fichier ;
tmp : T_Oeuvre ;
begin
-Ouverture de F en In et de G en Append
ouvrir (F , In_File , cat ) ;
create (G , Append_File , Name ( F ) & " 2 " ) ;
-copie de F - 1 oeuvre dans G
for i in 1 .. rang - 1 loop
read (F , tmp ) ;
write (G , tmp ) ;
end loop ;
read (F , tmp ) ;
while not end_of_file ( F ) loop
read (F , tmp ) ;
write (G , tmp ) ;
end loop ;
-Suppression de F / recr ation de F
-fermeture de G / r ouverture de G
delete ( F ) ;
creation ( cat ) ;
ouvrir (F , Append_File , cat ) ;
close ( G ) ;

231

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


122
123
124
125
126
127
128
129
130
131

open (G , In_File , Name ( F ) & " 2 " ) ;


-copie de G dans F
while not end_of_file ( G ) loop
read (G , tmp ) ;
write (F , tmp ) ;
end loop ;
-- Fermeture de F / Suppression de G
close ( F ) ;
delete ( G ) ;
end supprimer ;

132
133
134
135

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -SAISIE
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

function get_text ( size : integer ) return string is


U : Unbounded_String := Nu ll_Unb ounde d_Stri ng ;
T : string ( 1 .. size ) := ( others = > ' ') ;
begin
U := to_unbounded_string ( get_line ) ;
if length ( U ) > size
then T := to_string ( U ) ( 1 .. size ) ;
else T ( 1 .. length ( U ) ) := to_string ( U ) ;
for i in length ( U ) + 1 .. size loop
T ( i ) := ' ' ;
end loop ;
end if ;
return T ;
end get_text ;

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

function get_categorie return T_Categorie is


choix_cat : character ;
begin
Put_line ( " Choisissez la categorie desiree : " ) ;
Put_line ( "F - Film
M - Album de Musique " ) ;
Put_line ( "J - Jeu Video
Autre ? " ) ;
Get_Immediate ( choix_cat ) ; choix_cat := to_upper (
choix_cat ) ;
case choix_cat is
when 'F '
= > return FILM ;
when 'M '
= > return ALBUM ;
when 'J '
= > return JEU ;
when others = > return AUTRE ;
end case ;
end get_categorie ;

166
167
168
169
170

232

function saisie ( cat : T_Categorie ) return T_Oeuvre is


oeuvre : T_Oeuvre ( cat ) ;
choix : character ;
note : integer ;

SOLUTIONS POSSIBLES
171

begin

172
173
174
175

;
176
177
178
179
180
181
182
183

-SAISIE DES PARAMETRES COMMUNS


Put_line ( " Quel est le titre de l ' oeuvre ? " ) ;
oeuvre . titre := get_text ( 50 ) ;
Put_line ( " Quelle note donneriez - vous ? ( Entre 0 et 3 ) " )

loop
get ( note ) ; skip_line ;
if note in 0 .. 3
then oeuvre . note := note ;
exit ;
else Put_line ( " ERREUR ! La note doit tre comprise
entre 0 et 3 ! " ) ;
end if ;
end loop ;

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

Put_line ( " Sur quel type de support l ' oeuvre est - elle
enregistree ? " ) ;
Put_line ( "1 - VHS
2 - CD
3 - DVD " ) ;
Put_Line ( "4 - HDDVD
5 - BLURAY " ) ;
loop
get_immediate ( choix ) ; choix := to_upper ( choix ) ;
case choix is
when '1 '
= > oeuvre . support := VHS ; exit ;
when '2 '
= > oeuvre . support := CD ; exit ;
when '3 '
= > oeuvre . support := DVD ; exit ;
when '4 '
= > oeuvre . support := HDDVD ; exit ;
when '5 '
= > oeuvre . support := BLURAY ; exit ;
when others = > Put_line ( " Veuillez reconfirmer votre
choix . " ) ;
end case ;
end loop ;
-SAISIE DES PARAMETRES SPECIFIQUES
case cat is
when FILM = > Put_line ( " Quel est le realisateur ? " ) ;
oeuvre . realisateur := get_text ( 20 ) ;
Put_line ( " Le film est - il en VF ? ( O : Oui
/ N : Non ) " ) ;
loop
get_immediate ( choix ) ; choix :=
to_upper ( choix ) ;
if choix = 'O '
then oeuvre . vf := true ; exit ;
elsif choix = 'N '
then oeuvre . vf := false ; exit ;
else Put_line ( " Veuillez appuyer sur
O pour Oui ou sur N pour Non " ) ;
end if ;
end loop ;
return oeuvre ;

233

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

when ALBUM = > Put_line ( " Quel est l ' artiste ? " ) ;
oeuvre . artiste := get_text ( 20 ) ;
for i in oeuvre . morceaux ' range loop
Put_line ( " Voulez - vous ajouter un
morceau ? ( O : Oui / N : Non ) " ) ;
get_immediate ( choix ) ; choix :=
to_upper ( choix ) ;
if choix = 'O '
then Put_line ( " Quel est le titre du
morceau ? " ) ;
oeuvre . morceaux ( i ) := get_text (
50 ) ;
else exit ;
end if ;
end loop ;
return oeuvre ;
when JEU = > Put_line ( " Quelle est la console ? " ) ;
oeuvre . console := get_text ( 20 ) ;
Put_line ( " Avez - vous fini le jeu ? ( O : Oui
/ N : Non ) " ) ;
loop
get_immediate ( choix ) ; choix :=
to_upper ( choix ) ;
if choix = 'O '
then oeuvre . termine := true ; exit ;
elsif choix = 'N '
then oeuvre . termine := false ; exit
;
else Put_line ( " Veuillez appuyer sur
O pour Oui ou sur N pour Non " ) ;
end if ;
end loop ;
return oeuvre ;
when AUTRE = > return oeuvre ;
end case ;
end Saisie ;

242
243
244
245

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AFFICHAGE D ' UNE BDD


--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

246
247
248
249
250
251
252
253

234

procedure affichage_oeuvre ( oeuvre : T_oeuvre ) is


-- Affiche une seule oeuvre
null_string : constant string ( 1 .. 50 ) := ( others = > ' ') ;
begin
put ( " >>> Titre
: " ) ; put ( Oeuvre . titre ) ; new_line ;
put ( " >>> Support
: " ) ; put ( T_Support ' image ( Oeuvre .
support ) ) ; new_line ;
put ( " >>> Note
: " ) ; put ( Oeuvre . note , 1 ) ; new_line ;
case oeuvre . categorie is

SOLUTIONS POSSIBLES
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

when FILM = > put ( " >>> Realisateur : " ) ; put ( Oeuvre .
realisateur ) ; new_line ;
put ( " >>> VF
: ") ;
if Oeuvre . vf
then put ( " oui " ) ;
else put ( " non " ) ;
end if ; new_line ( 2 ) ;
when JEU = > put ( " >>> Console
: " ) ; put ( Oeuvre .
console ) ; new_line ;
put ( " >>> Termine
: ") ;
if Oeuvre . termine
then put ( " oui " ) ;
else put ( " non " ) ;
end if ; new_line ( 2 ) ;
when ALBUM = > put ( " >>> Artiste
: " ) ; put ( Oeuvre .
artiste ) ; new_line ;
put_line ( " >>> Morceaux
: ") ;
for i in Oeuvre . morceaux ' range loop
exit when Oeuvre . morceaux ( i ) =
null_string ;
put ( "
" ) ; put (i , 2 ) ; put ( " : " ) ;
Put ( oeuvre . morceaux ( I ) ) ; New_Line ;
end loop ;
new_line ( 2 ) ;
when AUTRE = > new_line ;
end case ;
end affichage_oeuvre ;

276
277

278
279
280
281
282
283

procedure affichage_bdd ( cat : T_categorie ) is


-- Affiche toute une base de donn es selon la cat gorie
demand e
n : natural := 1 ;
suite : character ;
Oeuvre : T_Oeuvre ( cat ) ;
F : T_Fichier ;
begin
ouvrir (F , In_File , cat ) ;

284
285
286
287
288
289
290
291
292
293
294
295

while not end_of_file ( F ) loop


if n = 0
then put ( " Cliquez pour continuer " ) ;
get_immediate ( suite ) ; new_line ;
end if ;
read (F , Oeuvre ) ;
Affichage_oeuvre ( oeuvre ) ;
n := ( n + 1 ) mod 5 ;
end loop ;
close ( F ) ;
end Affichage_bdd ;

296

235

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


-----------------------------------------------------EDITION DE LA BASE DE DONNEES
------------------------------------------------------

297
298
299
300

procedure Edit_bdd ( cat : T_Categorie ) is


-- Edite une base de donn es pour
modification ou suppression
choix : character ;
n : natural := 1 ;
Oeuvre : T_Oeuvre ( cat ) ;
F : T_Fichier ;
begin
ouvrir (F , In_File , cat ) ;

301

302
303
304
305
306
307
308

while not end_of_file ( F ) loop


read (F , Oeuvre ) ;
Affichage_oeuvre ( oeuvre ) ;
put_line ( " Que voulez - vous faire : Supprimer ( S ) , Modifier (
M ) , Quitter ( Q ) ou Continuer ? " ) ;
Get_Immediate ( choix ) ; choix := to_upper ( choix ) ;
if choix = 'M '
then close ( F ) ;
oeuvre := saisie ( cat ) ;
sauvegarde ( oeuvre , n ) ;
exit ;
elsif choix = 'S '
then close ( F ) ;
Supprimer ( cat , n ) ;
exit ;
elsif choix = 'Q '
then close ( F ) ;
exit ;
end if ;
n := n + 1 ;
end loop ;

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

if Is_Open ( F )
then close ( F ) ;
end if ;
end Edit_bdd ;

334
335
336
337

236

-----------------------------------------------------EDITION DE LA BASE DE DONNEES


------------------------------------------------------

SOLUTIONS POSSIBLES
338
339
340
341
342
343
344
345
346
347

procedure affichage_manuel is
F : Ada . text_IO . File_Type ;
begin
open (F , In_File , " ./ manual / manual . txt " ) ;
while not end_of_file ( F ) loop
put_line ( get_line ( F ) ) ;
end loop ;
close ( F ) ;
end affichage_manuel ;

348
349

end Makt aba_Functions ;

Manual.txt :
1
2
3
4
5
6
7

Commandes :
Quitter
: Quit / Exit
Nouveau
: New
Manuel
: Manual
R initialiser une base : Init / Erase
Afficher une base
: Print
Modifier une base
: Edit

En rsum
Pistes damlioration :
Fournir une interface graphique plutt que cette vilaine console. Pour linstant,
vous ne pouvez pas encore le faire, mais cela viendra.
Proposer de grer dautres types de supports : disque durs, cls USB. . .
Proposer lenregistrement de nouvelles informations : le genre de luvre (film
dhorreur ou daventure, album rock ou rap. . .), une description, les acteurs du
film, les membres du groupe de musique, lditeur du jeu vido, la dure des
morceaux. . .
Proposer lexportation de vos bases de donnes sous formes de fichiers textes
consultables plus aisment ou limportation dune uvre partir dun fichier
texte.
Permettre de complter les instructions en ligne de commande par des options :
modify -f modifierait un film par exemple.
Implmenter un service de recherche duvre par titre, par auteur ou mme par
mots-cls !
Encore une fois, ce ne sont pas les ides qui manquent pour dvelopper un tel logiciel.
Jespre toutefois que ce TP vous aura permis de faire un point sur les notions abordes
depuis le dbut de la partie III car nous allons maintenant nous atteler un type de
donnes plus complexe : les pointeurs. Et, de la mme manire que nous avons parl,
237

CHAPITRE 16. TP : LOGICIEL DE GESTION DE BIBLIOTHQUE


reparl et rereparl des tableaux, nous allons parler, reparler et rereparler des pointeurs
dans les prochains chapitres.

238

Chapitre

17

Les pointeurs I : allocation dynamique


Difficult :
Voici trs certainement LE chapitre de la troisime partie du cours. Pourquoi ? Eh bien
parce que cest, nen pas douter le plus dur de tous. Les pointeurs constituent une vritable
pine dans le pied de tout jeune programmeur. Non pas quils soient compliqus manipuler,
non bien au contraire ! Vous connaissez dj les oprations qui leur sont applicables. Non.
Le souci, cest quils sont difficiles conceptualiser, comprendre.
Comment a marche ? Je pointe sur quoi en ce moment ? quoi a sert ? Cest un pointeur
ou un objet point ? Je mets un pointeur ou pas ? Au secours ! Voil rsum en quelques
questions tout le dsarroi qui vous submergera srement en manipulant les pointeurs. Cest
en tous cas celui qui ma submerg lorsque jai commenc les utiliser et ce, jusqu obtenir
le dclic. Et, rassurez-vous, je compte bien prendre le temps de vous expliquer pour que
vous aussi vous ayez le dclic votre tour.

239

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE


Ce chapitre tant long et compliqu il a donc t divis en deux. La premire moiti
(celle que vous lisez actuellement) se chargera de vous expliquer les bases : quest-ce
quun pointeur ? Comment cela marche-t-il ? quoi cela ressemble-t-il en Ada ? La
seconde moiti aura pour but de couvrir toutes les possibilits offertes par le langage
Ada en la matire. Aussi compliqus soient-ils (et mme si le langage Ada fait moins
appel eux que dautres comme le C/C++), les pointeurs nous seront trs utiles par
la suite notamment pour le chapitre 10 et la partie V. Nhsitez pas relire ce double
chapitre, si besoin est, pour vous assurer que vous avez assimil toutes les notions.

Mmoire, variable et pointeur


Notre premier objectif sera donc de comprendre ce quest un pointeur. Et pour cela, il
faut avant tout se rappeler ce quest une variable !
On va pas tout reprendre zro quand mme ?

Et si ! Ou presque. Reprenons calmement et posons-nous la question suivante : que se


passe-t-il lorsque lon crit dans la partie dclarative la ligne ci-dessous et quoi cela
sert-il ?
1

MaVariablePerso : integer ;

En crivant cela, nous indiquons lordinateur que nous aurons besoin dun espace
en mmoire suffisamment grand pour accueillir un nombre de type integer, cest--dire
compris entre - 2 147 483 648 et + 2 147 483 647. Mon ordinateur va devoir rquisitionner une zone mmoire pour ma variable MaVariablePerso, comme sur la figure
17.1. Et pour la retrouver, cette zone mmoire disposera dun adresse, par exemple le
n6025. Ainsi, cette adresse, je suis sr de trouver MaVariablePerso et rien dautre !
Il ne peut y avoir quune seule variable par adresse.
Ainsi, si par la suite je viens crire MaVariablePerso := 5 ; , alors lemplacement
mmoire n6025 qui tait encore vide (ou qui contenait danciennes donnes sans aucun
rapport avec notre programme actuel) recevra la valeur 5 quil conservera jusqu la
fin du programme (voir figure 17.2.
En revanche, nous ne pourrons pas crire MaVariablePerso := 5.37 ; car 5.37 est
un float, et un float nest pas cod de la mme manire par lordinateur quun integer
ou un character. Donc lordinateur sait qu ladresse n6025, il ne pourra enregistrer
que des donnes de type integer sous peine de plantage. Lavantage des langages dits
de haut niveau (tels Ada), cest quils soccupent de grer la rquisition de mmoire,
dviter en amont de mauvaises affectations et surtout, ces langages nous permettent
de ne pas avoir nous tracasser de ladresse mmoire utilise (le n6025). Il nous suffit
dindiquer le nom de cet emplacement (MaVariablePerso) pour pouvoir y accder en
criture ou/et en lecture.
Or, il est parfois utile de passer non plus par le nom de cet emplacement, mais par son
240

MMOIRE, VARIABLE ET POINTEUR

Figure 17.1 Schma de la mmoire aprs dclaration dune variable

Figure 17.2 Schma de la mmoire aprs affectation dune valeur notre variable
241

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE


adresse, et cest l quinterviennent les pointeurs aussi appels accesseurs en Ada !
Au lieu de dire je veux modifier/lire la variable qui sappelle MaVariablePerso , nous
devrons dire je veux modifier/lire ce qui est crit ladresse n6025 .
Pour comprendre ce quest un pointeur, mettons-nous dans la peau de linspecteur
Derrick (la classe ! Non ?). Il sait que le meurtrier quil recherche vit ladresse 15
rue Mozart, btiment C, 5me tage appartement 34. Mais il ne connat pas son nom.
Que fait-il ? Soit il imprime quelque part dans son cerveau ladresse du criminel (vue
ladresse, a sera pas facile, mais. . . cest Derrick ! ), soit il utilise un bon vieux postit pour la noter (moins glamour mais plus srieux vu son ge). Ensuite, il se rend
ladresse indique, fait ce quil a faire (interrogatoire, arrestation. . .) termine lenqute
et youpi, il peut jeter son post-it car il a rsolu une nouvelle enqute.
Et cest quoi le pointeur dans cette histoire ?

Eh bien mon pointeur cest : le post-it ! Ou le coin du cerveau de Derrick qui a enregistr cette adresse si complique ! Traduit en langage informatique, cela signifie quune
adresse mmoire est trop complique pour tre retenue ou utilise par lutilisateur,
mieux vaut lenregistrer quelque part, et le plus srieux, cest denregistrer cette adresse
dans une bonne vieille variable tout ce quil y a de plus classique (ou presque). Et cest
cette variable, contenant une adresse mmoire intressante qui est notre pointeur (voir
figure 17.3).
Sur mon schma, le pointeur est donc la case n1025 ! Cette case contient ladresse (le
n3073) dun autre emplacement mmoire. Un pointeur nest donc rien de plus quune
variable au contenu inutilisable en tant que tel puisque ce contenu nest rien dautre
que ladresse mmoire dinformations plus importantes. Le pointeur nest donc pas tant
intressant par ce quil contient que par ce quil pointe.
Nous verrons par la suite que cest tout de mme un poil plus compliqu.

Le type access
Dclarer un pointeur
Le terme Ada pour pointeur est ACCESS ! Cest pourquoi on parle galement daccesseur.
Malheureusement, comme pour les tableaux, il nest pas possible dcrire :
1

Ptr : access ;

Toujours pour viter des problmes de typage, il nous faut dfinir auparavant un type
T_Pointeur indiquant sur quel type de donnes nous souhaitons pointer. Pour chan242

LE TYPE ACCESS

Figure 17.3 Ladresse mmoire n3073 est enregistre dans une variable.
ger, choisissons que le type T_Pointeur pointera sur. . . des integer ! Oui je sais, cest
toujours des integer. Nous allons donc dclarer notre type de la manire suivante :
1

type T_Pointeur is access Integer ;

Puis nous pourrons dclarer des variable Ptr1, Ptr2 et Ptr3 de type T_pointeur :
1

Ptr1 , Ptr2 , Ptr3 : T_Pointeur ;

Que contient mon pointeur ?


En faisant cette dclaration, le langage Ada initialise automatiquement vos pointeurs
(tous les langages ne le font pas, il est alors important de linitialiser soi-mme). Mais
attention ! Ils ne pointent sur rien pour linstant (il ne faudrait pas quils pointent au
hasard sur des donnes de type float, character ou autre qui nauraient pas t effaces).
Ces pointeurs sont donc initialiss avec une valeur nulle : NULL. Cest comme si nous
avions crit :
1

Ptr1 , Ptr2 , Ptr3 : T_Pointeur := Null ;

Autrement dit, ils sont vides !


243

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE

Bon alors maintenant, comment on leur donne une valeur utile ?

Ooh ! Malheureux ! Vous oubliez quelque chose ! La valeur du pointeur nest pas utile en
tant que telle car elle doit correspondre un adresse mmoire qui, elle, nous intresse !
Or le souci, cest que cet emplacement mmoire nest pas encore cr ! quoi bon
affecter une adresse notre pointeur sil ny a rien ladresse indique ? La premire
chose faire est de crer cet emplacement mmoire (et bien entendu, de le lier par la
mme occasion notre pointeur). Pour cela nous devons crire :
1

Ptr1 := new integer ;

Ainsi, Ptr1 pointera sur une adresse qui elle, comportera un integer !
O dois-je crire cette ligne ? Dans la partie dclaration ?

Non, surtout pas ! Je sais que cest normalement dans la partie dclarative que se
font les rquisitions de mmoire, mais nous ny dclarerons que le type T_Pointeur
et nos trois pointeurs Ptr1, Ptr2 et Ptr3. La ligne de code prcdente doit scrire
aprs le BEGIN, dans le corps de votre procdure ou fonction. Cest ce que lon appelle
lallocation dynamique. Ce procd permet, pour simplifier, de crer et supprimer
des variables nimporte quand durant le programme . Nous entrerons dans les dtails
durant lavant-dernire sous-partie, rassurez-vous. Retenez simplement qu la dclaration, nos pointeurs ne pointent sur rien et que pour leur affecter une valeur, il faut
crer un nouvel emplacement mmoire.
Une dernire chose, pas la peine dcrire des Put(Ptr1) ou Get(Ptr1), car je vous ai
un petit peu menti au dbut de ce chapitre, le contenu de Ptr1 nest pas un simple
integer (ce serait trop simple), ladresse contenue dans notre pointeur est un peu plus
complique et dailleurs, vous navez pas besoin de la connatre, la seule chose qui
compte cest quelle pointe bien sur une adresse valide.

Comment accder aux donnes ?


Et mon pointeur, il pointe sur quoi ? :-

Ben. . . pour linstant sur pas grand chose vrai dire puisque lemplacement mmoire
est vide. Bah oui, un pointeur a pointe et puis cest tout. . . ou presque ! Lemplacement
mmoire point na pas de nom (ce nest pas une variable), il nest donc pas possible
pour lheure de le modifier ou de lire sa valeur. Alors comment faire ? Une premire
mthode serait de revoir laffectation de notre pointeur ainsi :
244

LE TYPE ACCESS
1

Ptr1 := new integer '( 124 ) ;

Ainsi, Ptr1 pointera sur un emplacement mmoire contenant le nombre 124. Le souci,
cest quon ne va pas rquisitionner un nouvel emplacement mmoire chaque fois alors
que lon pourrait rutiliser celui existant ! Voici donc une autre faon :
1
2

Ptr2 := new integer ;


Ptr2 . all := 421 ;

Eh oui, je vous avais bien dit que les pointeurs taient un poil plus compliqu. Ils
comportent une sorte de composante comme les types structurs ! Sauf que ALL a
beau ressembl une composante, ce nest pas une composante du pointeur, mais la
valeur contenue dans lemplacement mmoire point !
Il existe une troisime faon de procder, si lon souhaite avoir un pointeur sur une
variable ou une constante dj connue. Mais nous verrons cette mthode plus loin, la
fin de ce chapitre.

Oprations sur les pointeurs


Quelles oprations peut-on donc faire avec des pointeurs ? Eh bien je vous lai dj
dit : pas grand chose de plus que ce que vous savez dj faire ! Voire mme moins de
choses. Je mexplique : vous pouvez tester lgalit (ou la non galit) de deux pointeurs,
effectuer des affectations. . . et cest tout. Oh, certes vous pouvez effectuer toutes les
oprations que vous souhaitez sur Ptr1.all puisque ce nest pas un pointeur mais un
integer ! Mais sur Ptr1, (et je ne me rpte pas pour le plaisir) vous navez le droit
quau test dgalit (et non-galit) et laffectation ! Vous pouvez ainsi crire :
1
2

if Ptr1 = Ptr2
then ...

Ou :
1
2

if Ptr1 /= Ptr2
then ...

Mais surtout pas :


1
2

if Ptr1 > Ptr2


then ...

a naurait aucun sens ! En revanche vous pouvez crire Ptr1 := Ptr2 ; . Cela
signifiera que Ptr1 pointera sur le mme emplacement mmoire que Ptr2 ! Cela signifie
aussi que lemplacement quil pointait auparavant est perdu : il reste en mmoire, sauf
quon a perdu son adresse ! Cet emplacement sera automatiquement supprim la fin
de notre programme.
Ce que je vous dis l est une mauvaise pratique : si lemplacement est perdu,
il nempche quil prend de la place en mmoire pour rien. Il serait bon de le
supprimer immdiatement pour librer de la place en mmoire pour dautres
oprations.
245

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE


Autre problme : crer deux pointeurs sur une mme donne (comme nous
lavons fait dans le dernier exemple) est la fois inutile, ou plutt redondant
(on utilise deux pointeurs pour faire la mme chose, cest du gaspillage de
mmoire) et surtout dangereux ! Via le premier pointeur, on peut modifier
la donne. Et, sans que le second nait t modifi, la valeur sur laquelle il
pointera ne sera plus la mme. Exemple :
1
2
3

Ptr2 . all := 5 ;
Ptr1 := Ptr2 ;
Ptr1 ! ! !
Ptr1 . all := 9 ;
maintenant !

ils

-- Ptr2 pointe sur " 5 "


-- On modifie l ' adresse contenue dans
-- Ouais ! Ptr1 pointe sur " 9 "
-- Sauf que du coup , Ptr2 aussi puisqu '
-- pointent sur le m me emplacement ! !

Donc, vitez ce genre dopration autant que possible. Prfrez plutt :


1
2

Ptr2 . all := 5 ;
Ptr1 . all := Ptr2 . all ;
adresse

3
4

cette adresse
Ptr1 . all := 9 ;

5
6

pas de probl me .

-- Ptr2 pointe sur " 5 "


-- Ptr1 pointe toujours sur la m me
-- On a simplement modifi le contenu de
-- Maintenant Ptr1 pointe sur " 9 " !
-- Mais comme Ptr1 et Ptr2 pointent sur
-- deux adresses distinctes , il n ' y a

Une erreur viter


Voici un code dangereux (pourquoi votre avis ?) :

1
2

if ptr /= null and ptr . all > 0


then ...

Eh bien parce que si ptr ne pointe sur rien (sil vaut NULL), lorsque vous effectuerez ce
test, le programme testera galement si Ptr.all est positif (suprieur 0). Or si Ptr
ne pointe sur rien du tout alors Ptr.all nexiste pas ! ! ! Nous devrons donc prendre la
prcaution suivante (comme nous lavions vu avec les tableaux) :
1
2

if ptr /= null and then ptr . all > 0


then ...

246

LIBRATION DE LA MMOIRE

Libration de la mmoire
Un programme (un peu) gourmand
Avant de commencer cette partie, je vais vous demander de copier et de compiler le
code ci-dessous :
1

with ada . text_io ;

use ada . Text_IO ;

2
3
4
5
6
7
8
9
10
11
12
13
14

procedure aaa is
type T_Pointeur is access Integer ;
P : T_Pointeur ;
c : character ;
begin
for i in 1 .. 8 loop
for j in 1 .. 10 ** i loop
P := new integer '( i * j ) ;
end loop ;
get ( c ) ; skip_line ;
end loop ;
end aaa ;

Comme vous pouvez le voir, ce code ne fait rien de bien compliqu : il cre un pointeur
auquel il affecte 10 valeurs successives, puis 100 (102 = 100), puis 1 000 (103 = 1000),
puis 10 000 (104 = 10000). . . et ainsi de suite. Mais lintrt de ce programme nest pas
l. Nous allons regarder sa consommation mmoire. Pour cela, ouvrez un gestionnaire
de tche :




si vous tes sous Windows : appuyez sur les touches Alt +Ctrl + Suppr


puis cliquez sur longlet Processus. Le programme apparatra en tte de liste.
si vous tes sous Linux ou Mac : un gestionnaire (appel Moniteur dactivit
sous Mac) est gnralement disponible dans votre liste de programmes, mais vous
pouvez galement ouvrir une console et taper la commande top.
Lancez votre programme aaa.exe et admirez la quantit de mmoire utilise : environ
780 Ko au dmarrage mais aprs chaque saisie du caractre C, la mmoire utilise
augmente de manire exponentielle (quadratique en fait ). Voici une capture dcran
(figure 17.4) avec 200 Mo de mmoire utilise par ce petit programme (mais on peut
faire bien mieux).
Mais comment se fait-il quun si petit programme puisse utiliser autant de
mmoire ?
Cest ce que nous allons expliquer maintenant avant de fournir une solution ce problme qui ne nous est jamais arriv avec nos bonnes vieilles variables.
247

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE

Figure 17.4 Consommation mmoire du programme aaa

Un problme de mmoire
Reprenons nos schmas prcdents car ils taient un peu trop simplistes. Lorsque vous
lancez votre programme, lordinateur va rserver des emplacements mmoire. Pour
le code du programme tout dabord, puis pour les variables que vous aurez dclar
entre IS et BEGIN. Ainsi, avant mme deffectuer le moindre calcul ou affichage, votre
programme va rquisitionner la mmoire dont il aura besoin : cest cela que sert
cette fameuse partie dclarative et le typage des variables. On dit que les variables sont
stockes en mmoire statique, car la taille de cette mmoire ne va pas varier.
cela, il faut ajouter un autre type de mmoire : la mmoire automatique. Au
dmarrage, lordinateur ne peut connatre les choix que lutilisateur effectuera. Certains
choix ne ncessiteront pas davantage de mmoire, dautres entraneront lexcution de
sous-programmes disposant de leurs propres variables ! Le mme procd de rservation
aura alors lieu dans ce que lon appelle la pile dexcution : noter que cette pile a
cette fois une taille variable dpendant du droulement du programme et des choix de
lutilisateur (voir figure 17.5).
Mais ce procd a un dfaut : il gaspille la mmoire ! Toute variable dclare ne disparat
qu la fin de lexcution du programme ou sous-programme qui la gnr. Ni votre
systme dexploitation ni votre compilateur ne peuvent deviner si une variable est
devenue inutile. Elle continue donc dexister malgr tout. linverse, ce procd ne
permet pas de dclarer des tableaux de longueur indtermine : il faut connatre la
taille avant mme lexcution du code source ! Contraignant.
248

LIBRATION DE LA MMOIRE

Figure 17.5 Mmoire statique et pile dexcution

Cest pourquoi existe un dernier type de mmoire appel Pool ou Tas. Ce pool, comme
la pile dexcution, na pas de taille prdfinie. Ainsi, lorsque vous dclarez un pointeur
P dans votre programme principal, lordinateur rserve suffisamment demplacements
en mmoire statique pour pouvoir enregistrer une adresse. Puis, lorsque votre programme lit :

P := new integer ; -- suivie ventuellement d ' une valeur

Celui-ci va automatiquement allouer, dans le Pool, suffisamment demplacements mmoires pour enregistrer un Integer. On parle alors dallocation dynamique. Donc si
nous rptons cette instruction (un peu comme nous lavons fait dans le programme
donn en dbut de sous-partie), lordinateur rservera plusieurs emplacements mmoire
dans le Pool (voir figure 17.6).
noter quavec un seul pointeur (en mmoire statique) on peut rserver
plusieurs adresses dans le pool (mmoire dynamique) !

249

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE

Figure 17.6 Allocation dynamique de mmoire

Rsolution du problme
Unchecked_Deallocation
Il est donc important lorsque vous manipuler des pointeurs de penser aux oprations
effectues en mmoire par lordinateur. Allouer dynamiquement de la mmoire est
certes intressant mais peut conduire de gros gaspillages si lon ne rflchit pas
la porte de notre pointeur : tant que notre pointeur existe en mmoire statique, les
emplacements allous en mmoire dynamique demeurent et saccumulent.
La mmoire dynamique ntait pas sense rgler ce problme de gaspillage
justement ?
Bien sr que oui. Nous disposons dune instruction dallocation de mmoire (NEW),
il nous faut donc utiliser une instruction de dsallocation. Malheureusement celle-ci
nest pas aise mettre en uvre et est risque. Nous allons devoir faire appel une
procdure : Ada.Unchecked_Deallocation ! Mais attention, il sagit dune procdure
gnrique (faite pour tout type de pointeur) qui est donc inutilisable en tant que telle.
Il va falloir lui crer une sur jumelle, que nous appellerons Free, prvue pour notre
type T_Pointeur. Voici le code corrig :
1

with ada . text_io ;

250

use ada . Text_IO ;

LIBRATION DE LA MMOIRE
2

with Ada . U n c h e ck ed_ De all oca ti on ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

procedure aaa is
type T_Pointeur is access Integer ;
P : T_Pointeur ;
c : character ;
procedure free is new Ada . U nch ec ked _D eal loc at ion ( Integer ,
T_Pointeur ) ;
begin
for i in 1 .. 8 loop
for j in 1 .. 10 ** i loop
P := new integer '( i * j ) ;
free ( P ) ;
end loop ;
get ( c ) ; skip_line ;
end loop ;
end aaa ;

Vous remarquerez tout dabord qu la ligne 2, jindique avec WITH que mon programme
va utiliser la procdure Ada.Unchecked_Deallocation (pas de USE, cela naurait aucun
sens puisque ma procdure est gnrique). Puis, la ligne 8, je cre une procdure Free
partir de Ada.Unchecked_Deallocation : on dit que lon instancie (mais nous verrons
tout cela plus en dtail dans la partie IV). Pour cela, il faut prciser tout dabord le
type de donne qui est point puis le type de Pointeur utilis.
Si nous avions deux types de pointeurs diffrents, il faudrait alors instancier
deux procdures Free diffrentes.
Enfin, la ligne 13, jutilise ma procdure Free pour librer la mmoire du Pool. Mon
pointeur P ne pointe alors plus sur rien du tout et il est rinitialis Null. Vous pouvez
compiler ce code et le tester : notre problme de fuite de mmoire a disparu !
Risques inhrents la dsallocation
Il est toutefois risqu dutiliser Ada.Unchecked_Deallocation et la plupart du temps,
je ne lutiliserai pas. Imaginez que vous ayez deux pointeurs P1 et P2 pointant sur un
mme espace mmoire :
1

...

2
3
4
5
6

...

P1 := new integer '( 15 ) ;


P2 := P1 ;
free ( P1 ) ;
Put ( P2 . all ) ;

Le compilateur considrera ce code comme correct mais que se passera-t-il ? Lorsque P1


est dsallou la ligne 4, il est remis Null mais cela signifie aussi que lemplacement
251

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE


vers lequel il pointait disparat. Et P2 ? Il pointe donc dsormais vers un emplacement
mmoire qui a disparu et il ne vaut pas Null ! Voil donc pourquoi on vitera la
dsallocation lorsque cela est possible.
Une autre mthode
Je veux bien, mais tu nous exposes un problme, tu nous donne la solution
et ensuite tu nous dis de ne surtout pas lemployer ! Je fais quoi moi ?
Une autre solution consiste rflchir (oui, je sais cest dur ) la porte de notre
type T_pointeur. En effet, lorsque le bloc dans lequel ce type est dclar (la procdure
ou la fonction le plus souvent) se termine, le type T_pointeur disparat et le pool de
mmoire quil avait engendr disparat lui aussi. Ainsi, pour viter tout problme avec
Ada.Unchecked_Deallocation, il peut tre judicieux dutiliser un bloc de dclaration
avec linstruction DECLARE.
1

with ada . text_io ;

use ada . Text_IO ;

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

procedure aaa is
c : character ;
begin
for i in 1 .. 8 loop
for j in 1 .. 10 ** i loop
declare
type T_Ptr is access Integer ;
P : T_Ptr ;
begin
P := new integer '( i * j ) ;
end ;
end loop ;
get ( c ) ; skip_line ;
end loop ;
end aaa ;

Exercices
Exercice 1
nonc
Crez un programme Test_Pointeur qui :
cre trois pointeurs Ptr1, Ptr2 et Ptr3 sur des integer ;
demande lutilisateur de saisir les valeurs pointes par Ptr1 et Ptr2 ;
252

EXERCICES
enregistre dans lemplacement mmoire point par Ptr3, la somme des deux valeurs saisies prcdemment.
Je sais, cest bidon comme exercice et vous pourriez le faire les yeux ferms avec des
variables. Sauf quici, je vous demande de manipuler non plus des variables, mais des
pointeurs !
Solution
1
2
3

WITH Ada . Text_IO ;


USE Ada . Text_IO ;
WITH Ada . Integer_Text_IO ;
USE Ada . Integer_Text_IO ;
WITH Ada . U n c h e ck ed_ De all oca ti on ;

4
5
6
7
8
9
10
11
12

procedure Test_Pointeur is
type T_Pointeur is access Integer ;
procedure Free is new Ada . U nch ec ked _D eal loc at ion ( Integer ,
T_Pointeur ) ;
Ptr1 , Ptr2 , Ptr3 : T_Pointeur ;
begin
Ptr1 := new Integer ;
Put ( " Quelle est la premiere valeur pointee ? " ) ;
get ( Ptr1 . all ) ; skip_line ;

13
14
15
16

Ptr2 := new Integer ;


Put ( " Quelle est la seconde valeur pointee ? " ) ;
get ( Ptr2 . all ) ; skip_line ;

17
18
19
20
21
22
23
24

Ptr3 := new Integer '( Ptr1 . all + Ptr2 . all ) ;


Free ( Ptr1 ) ;
re nos deux premiers pointeurs
Free ( Ptr2 ) ;
Put ( " Leur somme est de " ) ;
Put ( Ptr3 . all ) ;
Free ( Ptr3 ) ;
re le troisi me , mais est - ce vraiment utile ?
end Test_Pointeur ;

-- on lib

-- on lib

Exercice 2
nonc
Deuxime exercice, vous allez devoir crer un programme Inverse_pointeur qui saisit
une premire valeur pointe par Ptr1, puis une seconde pointe par Ptr2 et qui inversera
les pointeurs. Ainsi, la fin du programme, Ptr1 devra pointer sur la seconde valeur
et Ptr2 sur la premire. Attention, les valeurs devront tre saisies une et une seule fois,
elles seront ensuite fixes !
Une indication : vous aurez srement besoin dun troisime pointeur pour effectuer cet
change.
253

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE


Solution

1
2
3

WITH Ada . Text_IO ;


USE Ada . Text_IO ;
WITH Ada . Integer_Text_IO ;
USE Ada . Integer_Text_IO ;
WITH Ada . Un che ck ed_ De all oca ti on ;

procedure Inverse_Pointeur is
type T_Pointeur is access Integer ;
procedure Free is new Ada . U nch ec ked _D eal lo cat ion ( Integer ,
T_Pointeur ) ;
Ptr1 , Ptr2 , Ptr3 : T_Pointeur ;
begin
Ptr1 := new Integer ;
Put ( " Quelle est la premiere valeur pointee ? " ) ;
get ( Ptr1 . all ) ; skip_line ;

5
6
7
8
9
10
11
12
13

Ptr2 := new Integer ;


Put ( " Quelle est la seconde valeur pointee ? " ) ;
get ( Ptr2 . all ) ; skip_line ;

14
15
16
17

Ptr3 := Ptr1 ;
Ptr1 := Ptr2 ;
Ptr2 := Ptr3 ; Free ( Ptr3 ) ;
lib rer Ptr1 ou Ptr2 !

18
19
20

-- Attention ne pas

21
22
23
24
25
26

Put ( " Le premier pointeur pointe sur : " ) ;


Put ( Ptr1 . all ) ; Free ( Ptr1 ) ;
Put ( " et le second sur : " ) ;
Put ( Ptr2 . all ) ; Free ( Ptr2 ) ;
end Inverse_Pointeur ;

Ce bte exercice dinversion est important. Imaginez quau lieu dinverser


des Integer, il faille inverser des tableaux dun bon million dlments (des
donnes financires ou scientifiques par exemple), chaque lment comportant
de trs nombreuses informations. Cela peut tre extrmement long dinverser
chaque lment dun tableau avec llment correspondant de lautre. Avec
des pointeurs, cela devient un jeu denfant puisquil suffit dchanger deux
adresses, ce qui ne reprsente que quelques octets.
Vous tes parvenus la fin de la premire moiti de ce double-chapitre. Comme je vous
lavais dit en introduction, si des doutes subsistent, nhsitez pas relire ce chapitre ou
poster vos questions avant que les difficults ne saccumulent. La seconde moiti du
chapitre sera en effet plus complexe. Elle vous apprendra crer des pointeurs sur des
variables, des constantes, des programmes. . . ou crer des fonctions ou procdures
manipulant des pointeurs. Le programme est encore charg.
254

EXERCICES

En rsum
Avant dutiliser un pointeur, vous devez indiquer le type de donnes qui sera
point avec le terme ACCESS.
Un pointeur est une variable contenant ladresse dun emplacement mmoire :
vous devez distinguer le pointeur et llment point.
Modifier la valeur dun pointeur peut engendrer la perte de ces donnes. Pour
viter cela utilisez un autre pointeur ou, laide de Unchecked_Deallocation(),
dtruisez ces donnes.
Les pointeurs sont des variables trs lgres, mais qui exigent davantage de rflexion quant vos instructions et variables afin dviter un encombrement inutile
de la mmoire, la perte des donnes ou la modification impromptue des variables.

255

CHAPITRE 17. LES POINTEURS I : ALLOCATION DYNAMIQUE

256

Chapitre

18

Les pointeurs II
Difficult :
Aprs un premier chapitre prsentant la thorie sur les pointeurs (ou accesseurs) et lallocation dynamique, nous allons dans cette seconde partie nous intresser aux diffrentes
possibilits offertes par le langage Ada : comment pointer sur une variable ou une constante
dj cre ? Comment crer des pointeurs constants ? Comment passer un pointeur en argument dans une fonction ou une procdure ? Comment crer puis manipuler des pointeurs
sur des programmes ? Cest ce quoi nous allons tenter de rpondre dans ce chapitre avant
deffectuer quelques exercices dapplication.

257

CHAPITRE 18. LES POINTEURS II


noter que la troisime sous-partie (Pointeur sur un programme) est indique
comme optionnelle. Elle est particulirement complique et pourra tre lue
plus tard, quand vous aurez acquis une certaine aisance avec les pointeurs.
Les notions qui y sont traites sont importantes mais pas ncessaires pour
la partie III. Il sera toutefois bon dy revenir avant dentamer la partie IV et
notamment le chapitre sur les algorithmes de tri et la complexit.

Cas gnral
Pointeurs gnraliss : pointer sur une variable
Pour linstant, nous avons t obligs de pointer sur des valeurs dfinies par vos propres
soins. Nous voudrions dsormais pouvoir pointer sur une valeur gnre par lutilisateur
ou par le programme, sans que le programmeur en ait eu pralablement connaissance.
Une difficult se pose : lorsque vous crez un pointeur sur 7 , vous demandez lordinateur de crer dans une zone non utilise de la mmoire un emplacement contenant
la valeur 7. Or, pour pointer sur une variable, il ne faudra rien crer ! La variable est
cre ds le dmarrage de votre programme dans une zone de la mmoire qui a t
pralablement rquisitionne et dont la taille ne varie pas (cest cela que servent
les dclarations). Il faut donc revoir notre type T_Pointeur pour spcifier quil peut
pointer sur tout !
1

Type T_Pointeur is access all integer ;

Linstruction ALL permet dindiquer que lon peut pointer sur un emplacement mmoire
cr en cours de programme (on parle de mmoire dynamique, comme pour lallocation dynamique), mais aussi sur un emplacement mmoire dj existant (on parle de
mmoire statique) comme une variable.
La dclaration des pointeurs se fera normalement. En revanche, les variables que vous
autoriserez tre pointes devront tre clairement spcifies pour viter dventuels
problmes grce linstruction ALIASED.
1
2

Ptr : T_Pointeur ;
N : aliased integer ;

Et maintenant, comment je rcupre ladresse de ma variable ?

Cest simple, grce un attribut : ACCESS !


1
2
3
4

...
begin
N := 374 ;
Ptr := N ' access ;
N

258

--N prend une valeur , ici 374


-- Ptr prend comme valeur l ' adresse de

CAS GNRAL
Ainsi, Ptr.all correspondra la variable N ! Retenez donc ces trois choses :
le type T_Pointeur doit tre gnralis avec ALL ;
les variables doivent tre spcifies avec ALIASED ;
ladresse (ou plus exactement, la rfrence) peut tre rcupre avec lattribut
ACCESS.

Pointeur sur une constante et pointeur constant


Pointeur sur une constante
De la mme manire, il est possible de crer des pointeurs pointant sur des constantes.
Pour cela, notre type T_Pointeur doit tre dfini ainsi grce linstruction CONSTANT :
1

type T_Pointeur is access constant Integer ;

Notre pointeur et notre constante seront donc dclares ainsi :


1
2

Ptr : T_Pointeur ;
N : aliased constant integer := 35 ;

Ptr est dclar de la mme manire que dhabitude. Quant notre constante N, elle
doit tre spcifie comme pouvant tre pointe grce linstruction ALIASED ; elle doit
tre indique comme constante avec linstruction CONSTANT et elle doit tre indique
comme de type Integer (bien sr) valant 35 (par exemple). Puis, nous pourrons affecter
ladresse mmoire de N notre pointeur Ptr :
1

Ptr := N ' access ;

Attention, ce qui est constant ici, ce nest pas le pointeur mais la valeur pointe ! Il
est donc possible par la suite de modifier la valeur de notre pointeur, par exemple en
crivant le code suivant :
1
2

Ptr := N ' access ;


Ptr := M ' access ;
--M doit bien - s r tre d fini comme
aliased constant integer

En revanche il nest pas possible de modifier la valeur pointe car cest elle qui doit
tre constante. Vous ne pourrez donc pas crire le code suivant :
1
2
3

Ptr := N ' Access ;


Ptr . all := 15 ;
Ptr . all := N + 1 ;

Pointeur constant
Il est toutefois possible de crer des pointeurs constants. Redfinissons de nouveau
notre type T_Pointeur et nos objets et variables :
259

CHAPITRE 18. LES POINTEURS II


1
2
3

type T_Pointeur is access all integer ;


N : integer ;
Ptr : constant T_Pointeur := N ' access ;

Dans ce cas-ci, cest le pointeur qui est contant : il pointera sur lemplacement mmoire
de la variable N et ne bougera plus. Maintenant, rien nempche N (et donc son emplacement mmoire) de prendre diffrentes valeurs (dailleurs, pour lheure, Ptr pointe
sur N mais N na aucune valeur). Exemple doprations possibles :
1
2

N := 5 ;
Ptr . all := 9 ;

Voici un exemple doprations interdites ds lors que notre pointeur est constant :
1
2

Ptr := M ' access ;


Ptr := new integer '( 13 ) ;

Noubliez pas : ici cest le pointeur qui est constant et donc ladresse ne doit pas changer
(par contre ce qui se trouve cette adresse peut changer, ou pas).

Pointeur sur pointeur


Lide semble saugrenue et pourtant elle fera lobjet de notre avant dernier chapitre :
crer un pointeur sur un pointeur sur un integer ! Nous devrons donc crer deux types de
pointeurs distincts : un type pointeur sur integer et un type pointeur sur pointeur
sur integer !
1
2

type T _Po inteur_Integer is access Integer ;


type T _ Po inteur_Pointeur is access T_Pointeur_Integer ;

3
4
5

PtrI : T_ Pointeur_Integer ;
PtrP : T _ Pointeur_Pointeur ;

-- PtrI pour Pointeur - Integer


-- PtrP pour Pointeur - Pointeur

Cela devrait nous permettre de crer une sorte de liste de pointeurs qui se pointent
les uns les autres (mais nous verrons cela plus en dtail dans lavant dernier chapitre) !
Attention, cela apporte quelques subtilits :
1
2

PtrI := new Integer ;


PtrI . all := 78 ;

3
4
5

PtrP := new T_Pointeur_Integer ;


PtrP . all := PtrI ;

PtrP est un pointeur sur un pointeur, donc PtrP.all est lui aussi un pointeur, mais
un pointeur sur un entier cette fois ! Si je souhaite modifier cet entier, deux solutions
soffrent moi : soit utiliser PtrI.all, soit utiliser PtrP de la manire suivante :
1

PtrP . all . all := 79 ;

Il serait mme judicieux de reprendre notre code et de ne pas crer de pointeur PtrI.
Tout devrait pouvoir tre fait avec PtrP. Voyons cela en exemple :
260

POINTEUR COMME PARAMTRE


1
2
3
4
5
6
7
8
9

...

type T _Po inteur_Integer is access Integer ;


type T _ Po i nteur_Pointeur is access T_Pointeur_Integer ;
PtrP : T _ Pointeur_Pointeur ;
-- PtrP pour Pointeur Pointeur
begin
PtrP := new T_Pointeur_Integer ;
PtrP . all := new Integer ;
PtrP . all . all := 78 ;
...

Avec une seule variable PtrP, nous pouvons manipuler 3 emplacements mmoire :
lemplacement mmoire du pointeur sur pointeur PtrP ;
lemplacement mmoire du pointeur sur integer PtrP.all ;
lemplacement mmoire de linteger PtrP.all.all qui vaut ici 78 !
Et avec un pointeur sur pointeur sur pointeur sur pointeur sur pointeur. . . nous pourrions peut-tre parvenir crer, avec une seule variable, une liste dinformations lies
les unes aux autres et de longueur infinie (ou tout du moins non contrainte, il suffirait
dutiliser linstruction NEW chaque fois que lon souhaiterait allonger cette liste. Ces
listes sont la principale application des pointeurs que nous verrons dans ce cours et
fera lobjet dun prochain chapitre ! Alors encore un peu de patience.

Pointeur comme paramtre


Avant de passer aux pointeurs sur les programmes et aux exercices finaux, il nous
reste un dernier point traiter : comment transmettre un pointeur comme paramtre
une fonction ou une procdure ? Supposons que nous ayons deux procdures Lire()
et Ecrire() et une fonction Calcul()ncessitant un pointeur sur Integer en paramtre.
Voyons tout dabord comment dclarer nos procdures et fonctions sur des prototypes :
1
2
3

procedure Lire ( Ptr : T_Pointeur ) ;


procedure Ecrire ( Ptr : T_Pointeur ) ;
function Calcul ( Ptr : T_Pointeur ) return Integer ;

Voici une autre faon de dclarer nos sous-programmes, permettant de saffranchir du


type T_Pointeur :
1
2
3

procedure Lire ( Ptr : access Integer ) ;


procedure Ecrire ( Ptr : access Integer ) ;
function Calcul ( Ptr : access Integer ) return Integer ;

Avec cette seconde faon de procder, il suffira de fournir en paramtre nimporte quel
type de pointeur sur Integer, quil soit de type T_Pointeur ou autre. Par la suite, nous
pourrons appeler nos sous-programmes de diffrentes faons :
1

...

261

CHAPITRE 18. LES POINTEURS II


2
3
4
5
6
7
8
9

Ptr : T_Pointeur ;
N : aliased Integer := 10 ;
BEGIN
Ptr := new Integer ;
Lire ( Ptr ) ;
Ecrire (N ' access ) ;
N := Calcul ( new integer '( 25 ) ) ;
...

Chacun de ces appels est correct. Mais il faut prendre une prcaution : le pointeur que
vous transmettez vos fonctions/procdures doit absolument tre initialis et ne pas
valoir NULL ! Faute de quoi, vous aurez le droit un joli plantage de votre programme.
Seconde remarque dimportance : les pointeurs transmis en paramtres sont de mode
IN. Autrement dit, la valeur du pointeur (ladresse sur laquelle il pointe) ne peut
pas tre modifie. Cependant, rien nempche de modifier la valeur pointe. Ptr nest
accessible quen lecture ; Ptr.all est accessible en lecture ET en criture ! Cest donc
comme si nous transmettions Ptr.all comme paramtre en mode IN OUT. Continuons
la rflexion : pour les fonctions, il est normalement interdit de fournir des paramtres en
mode OUT ou IN OUT, mais en fournissant un pointeur comme paramtre, cela permet
donc davoir, dans une fonction, un paramtre Ptr.all en mode IN OUT !
Les pointeurs permettent ainsi de passer outre la rigueur du langage Ada.
Cest la fois un gain de souplesse mais galement une grosse prise de risque.
Dailleurs, bien souvent, les projets Ada ncessitant une trs haute scurit
sinterdisent lemploi des pointeurs.
La nouvelle norme Ada2012 permet dsormais aux fonctions de manipuler
des paramtres en mode IN OUT. Mais mon propos sadressant au plus grand
nombre, je tiens dabord compte des normes les plus rpandues Ada95 et
Ada2005.

Pointeur sur un programme (optionnel)


Cette dernire partie thorique savre plus complique que les autres. Si vous pensez
ne pas tre encore suffisamment au point sur les pointeurs (joli jeu de mot :- ), je vous
en dconseille la lecture pour linstant et vous invite vous exercer davantage. Si vous
tes suffisamment aguerris, alors nous allons passer aux chose trs, TRS srieuses !

Un exemple simple
Commenons par prendre un exemple simple (simpliste mme, mais rassurez-vous,
lexemple suivant sera bien plus ardu). Nous disposons de trois fonctions appeles
Double( ), Triple( ) et Quadruple( ) dont voici les spcifications :
262

POINTEUR SUR UN PROGRAMME (OPTIONNEL)


1
2
3

function Double ( n : integer ) return integer ;


function Triple ( n : integer ) return integer ;
function Quadruple ( n : integer ) return integer ;

Peu nous importe de savoir comment elles ont t implmentes, ce que lon sait cest
quelles ncessitent un paramtre de type integer et renvoient toutes un integer qui
est, au choix, le double, le triple ou le quadruple du paramtre fournit. Seulement
nous voudrions maintenant crer une procdure Table( ) qui affiche le double, le triple
ou le quadruple de tous les nombres entre a et b (a et b tant deux nombres choisis
par lutilisateur, la fonction choisie ltant elle aussi par lutilisateur). Nous aurions
besoin que les fonctions double( ), triple( ) ou quadruple( ) puissent tre transmise
la procdure Table( ) comme de simples paramtres, ce qui viterait que notre sousprocdure ait se soucier du choix effectu par lutilisateur. Nous voudrions ce genre
de prototype :
1
2

procedure Table ( MaFonction : function ;


a , b : integer ) ;

Malheureusement, cela nest pas possible en tant que tel. Pour arriver cela, nous
allons devoir transmettre en paramtre, non pas une fonction, mais un pointeur sur
une fonction. Donc premire chose : comment dclarer un pointeur sur une fonction ?
Cest un peu compliqu car quest-ce quune fonction ? Cest avant tout des paramtres
dun certain type qui vont tre modifis afin dobtenir un rsultat dun certain type.
Ainsi, si un pointeur pointe sur une fonction un paramtre, il ne pourra pas pointer
sur une fonction deux paramtres. Si un pointeur pointe sur une fonction renvoyant
un integer, il ne pourra pas pointer sur une fonction renvoyant un float. Donc nous
devons dfinir un type T_Ptr_Fonction qui prenne en compte tout cela : nombre et
type des paramtres, type de rsultat. Ce qui nous amne la dclaration suivante :
1
2
3
4

type T_Ptr_Fonction is access function ( n : integer ) return


integer ;
f : T_Ptr_Fonction ;
--f est la notation math matique usuelle pour fonction
-- mais n ' oubliez pas qu ' il s ' agit uniquement d ' un pointeur !

Dfinissons maintenant notre procdure Table :


1
2
3
4
5
6
7
8
9
10

procedure Table ( MaFonction : T_Ptr_Fonction ;


a , b : integer ) is
begin
for i in a .. b loop
put ( i ) ;
put ( " : " ) ;
put ( MaFonction . all ( i ) ) ;
new_line ;
end loop ;
end Table ;

Heureusement, le langage Ada nous permet de remplacer la ligne 7 par la ligne cidessous pour plus de commodit et de claret :
263

CHAPITRE 18. LES POINTEURS II


1

put ( MaFonction ( i ) ) ;

Toutefois, noubliez pas que MaFonction nest pas une fonction ! Cest un pointeur sur
une fonction ! Ada nous fait grce du .all pour les fonctions pointes la condition
que les paramtres soient crits. Enfin, il ne reste plus qu appeler notre procdure
Table( ) dans la procdure principale :
1
2
3
4
5
6
7
8

-- Choix de la fonction traiter


put ( " Souhaitez - vous afficher les doubles ( 2 ) , les triples ( 3 )
ou les quadruples ( 4 ) ? " ) ;
get ( choix ) ; skip_line ;
case choix is
when 2 = > f := double ' access ;
when 3 = > f := triple ' access ;
when others = > f := quadruple ' access ;
end case ;

9
10
11
12

-- Choix des bornes de l ' intervalle


put ( " A partir de " ) ; get ( a ) ; skip_line ;
put ( " jusqu ' " ) ; get ( b ) ; skip_line ;

13
14
15

-- Affichage de la table de a b de la fonction f choisie


Table (f ,a , b ) ;

En procdant ainsi, lajout de fonctions Quintuple( ), Decuple( ), Centuple( ). . . fonctionnant sur le mme principe que les prcdentes se fera trs facilement sans modifier
le code de la fonction Table( ), mais uniquement la portion de code o lutilisateur
doit faire un choix entre les fonctions. Cela peut paratre anodin notre niveau, mais
ce petit gain de clart et defficacit devient trs important lors de projets plus complexes faisant intervenir plusieurs programmeurs. Celui rdigeant le programme Table(
) na pas besoin dattendre que celui rdigeant les fonctions Double( ), Triple( ). . . ait
termin, il peut dors et dj coder sa procdure en utilisant une fonction f thorique.
Il est galement possible de crer des pointeurs sur des procdures. Bien
videmment, il est alors inutile de mentionner le rsultat lors de la dclaration
du type Pointeur sur procdure . Toutefois, prenez soin de vrifier que les
modes des paramtres (IN, OUT, IN OUT) correspondent.

Un exemple de la vie courante

Je ne vois toujours pas lintrt de crer des pointeurs sur des fonctions ?

Ah bon ? Pourtant vous utilisez en ce moment mme ce genre de procd. Alors mme
que vous lisez ces lignes, votre ordinateur doit grer une liste de programmes divers et
264

POINTEUR SUR UN PROGRAMME (OPTIONNEL)






varis. Il vous suffit dappuyer sur Alt +Ctrl + Suppr sous Windows puis de cliquer


sur longlet Processus pour voir la liste des processus en cours. Sous Linux, ouvrez la
console et tapez la commande ps (pour process status).
Comment votre systme dexploitation fait-il pour grer plusieurs processus ? Entre
Explorer, votre navigateur internet, votre antivirus, lhorloge. . . sans compter les programmes que vous pourriez encore ouvrir ou fermer, il faut bien que votre systme dexploitation (que ce soit Windows, Linux, MacOS. . .) puisse grer une liste changeante
de programmes ! Cest, trs schmatiquement, ce que nous permettent les pointeurs :
grer des listes de longueurs indfinies (voir le prochain chapitre) de pointeurs vers
des programmes alternativement placs en mmoire vive ou traits par le processeur.
Rassurez-vous, je ne compte pas vous faire dvelopper un systme dexploitation dans
votre prochain TP, je vous propose l simplement une illustration de notre travail.

Un exemple trs. . . mathmatique


Ce troisime et dernier exemple sadresse ceux qui ont des connaissances mathmatiques un peu plus pousses (disons, niveau terminale scientifique). En effet, les
fonctions sont un outil mathmatique trs puissant et trs courant. Et deux oprations
essentielles peuvent-tre appliques aux fonctions : la drivation et lintgration. Je
souhaiterais traiter ici lintgration. Ou plutt, une approximation de lintgrale dune
fonction par la mthode des trapzes.
Pour rappel, et sans refaire un cours de Maths, lintgrale entre a et b dune fonction f
correspond laire comprise entre la courbe reprsentative de la fonction (entre a et b)
et laxe des abscisses (voir figure 18.1). Pour approximer cette aire, plusieurs mthodes
furent inventes, notamment la mthode des rectangles qui consiste approcher laire
de cette surface par une srie de rectangles. La mthode des trapzes consiste quant
elle lapproximer par une srie de trapzes rectangles.
Dans lexemple prcdent, on a cherch laire sous la courbe entre 1 et 4. Lintervalle [ 1 ;
4 ] a t partag en 3 sous-intervalles de longueur 1. La longueur de ces sous-intervalles
est appele le pas. Comment calculer laire du trapze ABBA ? De la manire sui0
0
vante : AA +2 BB AB. Et on remarquera que AA0 = f (1), BB 0 = f (1 + pas),
CC 0 = f (1 + 2 pas) . . .
Nous allons donc dfinir quelques fonctions dont voici les prototypes :
1
2
3

function carre ( x : float ) return float ;


function cube ( x : float ) return float ;
function inverse ( x : float ) return float ;

Nous allons galement dfinir un type T_Pointeur :


1

type T_Pointeur is access function ( x : float ) return float ;

Nous aurons besoin dune fonction Aire_Trapeze et dune fonction Integrale dont voici
les prototypes :
1

-- aire_trapeze () ne calcule l ' aire que d ' un seul trap ze

265

CHAPITRE 18. LES POINTEURS II

Figure 18.1 Graphique de la fonction f

266

POINTEUR SUR UN PROGRAMME (OPTIONNEL)


2
3
4
5

--f est la fonction tudi e


--x le " point de d part " du trap ze
-- pas correspond au pas , du coup , x + pas est le " point d ' arriv e
" du trap ze
function aire_trapeze ( f : T_Pointeur ; x : float ; pas : float )
return float ;

6
7
8
9
10
11
12
13
14

-- integrale () effectue la somme des aires de tous les trap zes


ainsi que le calcul du pas
--f est la fonction tudi e
-- min et max sont les bornes
-- nb_inter est le nombre de sous - intervalles et donc de trap
zes ,
-- ce qui correspond galement la pr cision de la mesure
function integrale ( f : T_pointeur ;
min , max : float ;
nb_inter : integer ) return float is

Je vous laisse le soin de rdiger seuls ces deux fonctions. Cela constituera un excellent
exercice sur les pointeurs mais aussi sur les intervalles et le pas. Il ne reste donc plus
qu appeler notre fonction integrale( ) :
1
2
3
4
5

--a , b sont des float saisis par l ' utilisateur repr sentant les
bornes de l ' intervalle d ' int gration
--i est un integer saisi par l ' utilisateur repr sentant le
nombre de trap zes souhait s
integrale ( carre ' access ,a ,b , i ) ;
integrale ( cube ' access ,a ,b , i ) ;
integrale ( inverse ' access ,a ,b , i ) ;

Bon allez ! Je suis bon joueur, je vous transmets tout de mme un code source possible.
Attention, ce code est trs rudimentaire, il ne teste pas si la borne infrieure est bel
et bien infrieure la borne suprieure, rien nest vrifi quant la continuit ou
au signe de la fonction tudie. . . bref, il est minemment perfectible pour un usage
mathmatique rgulier. Mais l, pas la peine dinsister, vous le ferez vraiment tous
seuls.
1
2

with ada . Text_IO , ada . Integer_Text_IO , ada . Float_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO , ada . Float_Text_IO ;

3
4

procedure integration is

5
6
7
8

-- - - - - - - - - - - - - - - - - - - - - - - - - -Fonctions de x
--- - - - - - - - - - - - - - - - - - - - - - - - - -

9
10
11
12
13

function carre ( x : float ) return float is


begin
return x ** 2 ;
end carre ;

14

267

CHAPITRE 18. LES POINTEURS II


15
16
17
18

function inverse ( x : float ) return float is


begin
return 1 . 0 / x ;
end inverse ;

19
20
21
22
23

function cube ( x : float ) return float is


begin
return x ** 3 ;
end cube ;

24
25
26
27

-- - - - - - - - - - - - - - - - -Types
--- - - - - - - - - - - - - - - - -

28
29

type T_Pointeur is access function ( x : float ) return float


;

30
31
32
33

-- - - - - - - - - - - - - - - - - - - - - - - -Fonctions de f
--- - - - - - - - - - - - - - - - - - - - - - - -

34
35
36
37
38

function aire_trapeze ( f : T_Pointeur ; x : float ; pas :


float ) return float is
begin
return (( f . all ( x ) + f ( x + pas ) ) / 2 . 0 ) * pas ;
end aire_trapeze ;

39
40
41
42
43
44
45
46
47
48
49

function integrale ( f : T_pointeur ; min , max : float ; nb_inter


: integer ) return float is
res : float := 0 . 0 ;
pas : float ;
begin
pas := ( max - min ) / float ( nb_inter ) ;
for i in 0 .. nb_inter - 1 loop
res := res + aire_trapeze (f , min + float ( i ) * pas , pas ) ;
end loop ;
return res ;
end integrale ;

50
51
52
53

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Proc dure principale


--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

54
55
56
57
58
59
60

268

a , b : float ;
i : integer ;
begin
put ( " Entre quelles valeurs souhaitez - vous integrer les
fonctions ? " ) ;
get ( a ) ; get ( b ) ; skip_line ;
put ( " Combien de trapezes souhaitez - vous construire ? " ) ;

EXERCICES
61
62
63
64
65
66
67
68

get ( i ) ; skip_line ;
put ( "L ' integrale de la fonction carre est : " ) ;
put ( integrale ( carre ' access ,a ,b , i ) , Exp = > 0 ) ; new_line ;
put ( "L ' integrale de la fonction inverse est : " ) ;
put ( integrale ( inverse ' access ,a ,b , i ) , Exp = > 0 ) ; new_line ;
put ( "L ' integrale de la fonction cube est : " ) ;
put ( integrale ( cube ' access ,a ,b , i ) , Exp = > 0 ) ; new_line ;
end integration ;

Exercices
Exercice 1
nonc
Voici un programme tout ce quil y a de plus simple :
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10
11

procedure programme is
n , m : integer ;
begin
Put ( " Choisissez un nombre n : " ) ; get ( n ) ; skip_line ;
Put ( " Choisissez un nombre m : " ) ; get ( m ) ; skip_line ;
Put ( " La somme des nombres choisis est " ) ;
Put ( n + m ) ;
end programme ;

Vous devez modifier ce code de faon changer le contenu des variables n et m, sans
jamais modifier les variables elles-mmes. Ainsi, le rsultat affich sera faux.
Solution
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6
7
8

procedure programme is
n , m : aliased integer ;
type T_Pointeur is access all integer ;
P : T_Pointeur ;
begin

9
10
11
12
13

Put ( " Choisissez un nombre n : " ) ; get ( n ) ; skip_line ;


P := n ' access ;
P . all := P . all + 1 ;
Put ( " Choisissez un nombre m : " ) ; get ( m ) ; skip_line ;

269

CHAPITRE 18. LES POINTEURS II


14
15
16
17
18

P := m ' access ;
P . all := P . all * 3 ;
Put ( " La somme des nombres choisis est " ) ;
Put ( n + m ) ;
end programme ;

Exercice 2
nonc
Reprenez lexercice prcdent en remplaant les deux variables n et m par un tableau
T de deux entiers puis, en modifiant ses valeurs uniquement laide dun pointeur.
Solution
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

procedure programme is
type T_Tableau is array ( 1 .. 2 ) of integer ;
type T_Pointeur is access all T_Tableau ;
T : aliased T_Tableau ;
P : T_Pointeur ;
begin
Put ( " Choisissez un nombre n : " ) ; get ( T ( 1 ) ) ; skip_line ;
Put ( " Choisissez un nombre m : " ) ; get ( T ( 2 ) ) ; skip_line ;
P := T ' access ;
P . all ( 1 ) := P . all ( 1 ) * 18 ;
P . all ( 2 ) := P . all ( 2 ) / 2 ;
Put ( " La somme des nombres choisis est " ) ;
Put ( T ( 1 ) + T ( 2 ) ) ;
end programme ;

Exercice 3
nonc
Reprenez le premier exercice, en remplaant les deux variables n et m par un objet de
type structur comportant deux composantes entires puis, en modifiant ses composantes uniquement laide dun pointeur.
Solution
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5

270

procedure programme is
type T_couple is

EXERCICES
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

record
n , m : integer ;
end record ;
type T_Pointeur is access all T_couple ;
Couple : aliased T_couple ;
P : T_Pointeur ;
begin
Put ( " Choisissez un nombre n : " ) ; get ( couple . n ) ; skip_line
;
Put ( " Choisissez un nombre m : " ) ; get ( couple . m ) ; skip_line
;
P := Couple ' access ;
P . all . n := P . all . n ** 2 ;
P . all . m := P . all . m mod 3 ;
Put ( " La somme des nombres choisis est " ) ;
Put ( Couple . n + Couple . m ) ;
end programme ;

Il est galement possible (mais je vous dconseille pour linstant de le faire afin dviter
toute confusion) dcrire les lignes suivantes :
1
2

P . n := P . n ** 2 ;
P . m := P . m mod 3 ;

Cest un raccourci que permet le langage Ada. Attention toutefois ne pas confondre
le pointeur P et lobjet point !

Exercice 4
nonc
Reprenez le premier exercice. La modification des deux variables n et m se fera cette
fois laide dune procdure dont le(s) paramtres seront en mode IN.
Solution
1
2

with ada . Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Integer_Text_IO ;

3
4
5
6
7
8

procedure programme is
procedure modif ( P : access integer ) is
begin
P . all := P . all * 5 + 8 ;
end modif ;

9
10
11

n , m : aliased integer ;
begin

12
13
14

Put ( " Choisissez un nombre n : " ) ; get ( n ) ; skip_line ;


modif (n ' access ) ;

271

CHAPITRE 18. LES POINTEURS II


15
16
17
18
19

Put ( " Choisissez un nombre m : " ) ; get ( m ) ; skip_line ;


modif (m ' access ) ;
Put ( " La somme des nombres choisis est " ) ;
Put ( n + m ) ;
end programme ;

Exercice 5 (Niveau Scientifique)


nonc
Rdigez un programme calculant le coefficient directeur de la scante la courbe reprsentative dune fonction f en deux points A et B. partir de l, il sera possible
de raliser une seconde fonction qui donnera une valeur approche de la drive en un
point de la fonction f.
Par exemple, si f (x) = x2 , alors la fonction Secante(f,2,7) renverra
La fonction Derivee(f,5) renverra quant elle
petit afin damliorer la prcision du calcul.

(5+h)2 52
h

72 22
72

o h devra tre suffisamment

Solution
1
2

with ada . Text_IO , ada . Float_Text_IO , ada . Integer_Text_IO ;


use ada . Text_IO , ada . Float_Text_IO , ada . Integer_Text_IO ;

3
4

procedure derivation is

5
6

type T_Ptr_Fonction is access function ( x : float ) return


float ;

7
8
9
10

function carre ( x : float ) return float is begin


return x ** 2 ;
end carre ;

11
12
13
14

function cube ( x : float ) return float is begin


return x ** 3 ;
end cube ;

15
16
17
18

function inverse ( x : float ) return float is begin


return 1 . 0 / x ;
end inverse ;

19
20
21
22
23
24
25

function secante ( f : T_Ptr_Fonction ;


a : float ;
b : float ) return float is
begin
return ( f ( b ) -f ( a ) ) /( b - a ) ;
end secante ;

26
27

272

function derivee ( f : T_Ptr_Fonction ;

= 9.

EXERCICES
28
29
30
31
32

x : float ;
precision : float := 0 . 000001 ) return float

is
begin
return secante (f ,x , x + precision ) ;
end derivee ;

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

choix : integer := 1 ;
f : T_Ptr_Fonction ;
x : float ;
begin
loop
put ( " Quelle fonction souhaitez - vous deriver ? Inverse ( 1 ) ,
Carre ( 2 ) ou Cube ( 3 ) . " ) ;
get ( choix ) ; skip_line ;
case choix is
when 1 = > f := inverse ' access ;
when 2 = > f := carre ' access ;
when 3 = > f := cube ' access ;
when others = > exit ;
end case ;
put ( " Pour quelle valeur de x souhaitez - vous connaitre la
derivee ? " ) ;
get ( x ) ; skip_line ;
put ( " La derivee vaut environ " ) ; put ( derivee (f , x ) , Exp = >0 ,
Aft = > 3 ) ; put ( " pour x = " ) ; put (x , Exp = >0 , Aft = > 2 ) ;
new_line ; new_line ;
end loop ;
end derivation ;

Il est possible de demander lutilisateur de rgler la prcision lui-mme, mais jai


retir cette option car elle alourdissait lutilisation du programme sans apporter de
substantiels avantages.
Bien, si vous ralisez ces exercices cest que vous devriez tre venus bout de ces deux
chapitres prouvants. Il est fort possible que vous soyez encore assaillis de nombreuses
questions. Cest pourquoi je vous conseille de pratiquer. Ce cours vous a propos de
nombreux exercices, il nest peut-tre pas inutile de reprendre des exercices dj faits
(affichage de tableau, de rectangles. . .) et de les retravailler laide des pointeurs car
cette notion est rellement complique. Nhsitez pas non plus relire le cours tte
repose.
Si daventure vous pensez matriser les pointeurs, alors un vritable test vous attend
au chapitre 10. Nous mettrons les pointeurs en pratique en crant avec nos petites
mains un nouveau type de donne et les outils pour le manipuler : les types abstraits
de donne ! L encore, ce sera un chapitre complexe, mlant thorie et pratique. Mais
avant cela nous allons aborder une notion difficile galement : la rcursivit.
273

CHAPITRE 18. LES POINTEURS II

En rsum
Pour pointer sur des variables (en mmoire statique) vous devrez indiquer, lors
de la dclaration du type, IS ACCESS ALL.
Pour quune variable puisse tre pointe, il faudra lindiquer par le terme ALIASED
Avec un pointeur constant, les donnes pointes peuvent tre modifies mais pas
ladresse. Avec un pointeur sur une constante ce sera le contraire.
Le passage en paramtre dun pointeur une fonction permet davoir accs aux
donnes pointes en lecture comme en criture. Un paramtre en mode ACCESS
est quivalent un paramtre en mode IN OUT.
Il est possible de dclarer des pointeurs sur des fonctions ou des procdures.

274

Chapitre

19

Fonctions et procdures II : la
rcursivit
Difficult :
Le chapitre que vous vous apprtez lire est lui aussi compliqu, et pourtant vous naurez
pas de nouvelle instruction ou de nouveau type composite connatre.

Mais alors quel intrt de faire ce chapitre ?

275

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


Nous allons aborder une notion dalgorithmique importante et qui nous servira pour
le prochain chapitre : la rcursivit. Ceux qui mnent (ou ont men) des tudes
scientifiques devraient tre familiariss avec un terme similaire : la rcurrence !
Mais il ny a pas derreur, la rcursivit est une notion trs proche du raisonnement
par rcurrence vu au lyce. Pour ceux qui nont pas men ce genre dtude, nayez
aucune crainte, nous allons tout prendre la base, comme toujours.

Une premire dfinition


On dit quun programme est rcursif si pour parvenir au rsultat voulu il se remploie
lui-mme. Cela peut sillustrer par la figure 19.1.

Figure 19.1 Triangle de Sierpinski


Pour raliser le triangle de sierpinski (premier exemple), on relie les milieux des 3 cts
du grand triangle de manire former trois triangles noirs et un triangle blanc
lenvers par rapport aux autres. Puis on reproduit cette opration sur les triangles
noirs, et encore, et encore . . . jusquau nombre dsir (Cette figure fait partie de la
grande famille des fractales qui ont pour principe de rpter linfini sur elles-mmes
la mme opration).
Idem pour le nautile (figure 19.2), on comprend que pour dvelopper une coquille
30 alvoles, le nautile doit avoir dvelopp 29 alvoles puis en crer seulement une
supplmentaire de la mme manire quil a cr les prcdentes. Le nautile rpte ainsi
sur lui-mme linfini ou un grand nombre de fois, la mme opration : ajouter une
alvole . Un dernier exemple : lponge de Menger, une autre fractale inspire du carr
de Sierpinski. On a dtaill les 4 premires tapes la figure 19.3.
Est-il besoin de rexpliquer cette fractale ?
276

UNE PREMIRE DFINITION

Figure 19.2 Nautile

Figure 19.3 ponge de Menger

277

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT

Exemple dalgorithme rcursif


Bon, cest gentil les illustrations, mais part les coquillages et la gomtrie,
a donne quoi en programmation ?
Imaginons que vous deviez crer un programme affichant un petit bonhomme devant
monter N marches dun escalier et que vous disposez pour cela dune procdure appele MonterUneMarche. Il y aura deux faons de procder. Tout dabord la manire
itrative :
1

PROC DURE MonterALaMarche ( n ) :

POUR i ALLANT de 1 N
|
MonterUneMarche ;
FIN DE BOUCLE

3
4
5
6
7

FIN DE PROC DURE

Ou alors on emploie une mthode rcursive :


1

PROC DURE MonterALaMarche ( n ) :

SI n > 0
|
MonterALaMarche (n - 1 ) ;
|
MonterUneMarche ;
FIN DE SI

3
4
5
6
7
8

FIN DE PROC DURE

La mthode rcursive est un peu bizarre non ? Une explication simpose. Si on veut
monter 0 marche, cest facile, il ny a rien faire. On a gagn ! En revanche, dans le cas
contraire, il faut dj avoir mont n-1 marches puis en gravir une autre. La procdure
MonterMarche fait donc appel elle-mme pour une marche de moins. Prenons un
exemple, si lon souhaite monter 4 marches :
i) 4 marches > 0 ! Donc on doit monter dj 3 marches :
a) 3 marches > 0 ! Donc on doit monter dj 2 marches :
1) 2 marches > 0 ! Donc on doit monter dj 1 marche :
(i) 1 marche > 0 ! Donc on doit monter dj 0 marche :
(a) 0 marche ! ! ! cest gagn !
(b) L on sait faire : le rsultat renvoy est que marche = 0.
(ii) On incrmente marche : le rsultat renvoy est que marche = 1.
2) On incrmente marche : le rsultat renvoy est que marche = 2.
b) On incrmente marche : le rsultat renvoy est que marche = 3.
ii) On incrmente marche : le rsultat renvoy est que marche = 4.
278

NOTRE PREMIRE FONCTION RCURSIVE


Ce procd est un peu plus compliqu comprendre que la faon itrative (avec les
boucles LOOP). Attention ! Comme pour les boucles itratives, il est possible dengendrer
une boucle rcursive infinie ! Il est donc trs important de rflchir la condition de
terminaison. En rcursivit, cette condition est en gnral le cas trivial, le cas le plus
simple quil soit, la condition initiale : lge 0, la marche n0, la lettre a ou la case
n1 dun tableau. Comme pour les boucles itratives, il est parfois utile de drouler
un exemple simple la main pour tre sr quil ny a pas derreur (souvent lerreur se
fait sur le cas initial).

Notre premire fonction rcursive


nonc
Nous allons crer une fonction Produit(a,b) qui effectuera le produit (la multiplication)
de deux nombres entiers naturels (cest--dire positifs) a et b. Facile me direz-vous. Sauf
que jajoute une difficult : il sera interdit dutiliser de symbole *. Nous devrons donc
implmenter cette fonction uniquement avec des additions et de manire rcursive. Cet
exemple peut paratre compltement idiot. Toutefois, il vous permettra de raliser votre
premier algorithme rcursif et vous verrez que dj, ce nest pas de la tarte.
Si vous souhaitez vous lancez seuls, allez-y, je vous y invite. Cest dailleurs la meilleure
faon de comprendre les difficults. Sinon, je vous donne quelques indications tout de
suite, avant de vous rvler une solution possible.

Indications
Le cas trivial
Tout dabord quel est le cas trivial, vident ? Autrement dit, quelle sera la condition
de terminaison de notre boucle rcursive ?
Le cas le plus simple est le cas Produit(a,0) ou Produit(0,b) puisquils renvoient invariablement 0.
Je rappelle aux tourdis que 3 \times 0 = 0 , 0 \times 769 = 0 . . .

Il y a donc deux cas traiter : si a = 0 ET/OU si b = 0.


Commutativit

Euh. . . cest quoi encore a ? Je comptais pas faire Bac +10 ! ! !

279

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


Pas dinquitude. La proprit de commutativit signifie juste que les nombres dune
multiplication peuvent tre commuts , cest--dire changs. Autrement dit a b =
b a. En effet, que vous criviez 2 3 ou 3 2, cela reviendra au mme : le rsultat
vaudra 6 ! Cest une proprit vidente dont ne bnficient pas la soustraction ou la
division par exemple.
Si cest si vident, pourquoi en parler ?

Eh bien pour rgler un problme simple : pour effectuer Produit(2,3), cest--dire 2 3,


va-t-on faire 2 + 2 + 2 (cest--dire 3 fois 2) ou 3 + 3 (cest--dire 2 fois
3) ? Le deuxime choix semble plus rapide car il ncessitera moins dadditions et donc
moins de rcursions (boucles rcursives). Il serait donc judicieux de traiter ce cas avant
de commencer. Selon que a sera infrieur ou pas b, on effectuera Produit(a,b) ou
Produit(b,a).

Mise en uvre
Supposons que a soit plus petit que b. Que doit faire Produit(a,b) (hormis le test de
terminaison et dinversion de a et b) ? Prenons un exemple : Produit(3,5). Nous partons
dun rsultat temporaire gal 0 (res = 0).
3 > 0. Donc notre rsultat temporaire est augment de 5 (res = 5) et on fait le
produit de 2 par 5 : Produit(2,5).
2 > 0. Donc notre rsultat temporaire est augment de 5 (res = 10) et on
fait le produit de 1 par 5 : Produit(1,5).
1 > 0. Donc notre rsultat temporaire est augment de 5 (res = 15) et
on fait le produit de 0 par 5 : Produit(0,5).
0 ! ! ! Facile. On renvoie le rsultat temporaire.
Se pose alors un souci : comment transmettre la valeur temporaire de Res, notre rsultat ? La fonction Produit na que deux paramtres a et b ! Lide, est de ne pas
surcharger la fonction Produit inutilement avec des paramtres inutiles pour lutilisateur final. Deux solutions soffrent vous : soit vous crez un paramtre Res initialis
0 (lutilisateur naura ainsi pas besoin de le renseigner) ; soit vous crez une sousfonction (Pdt, Mult, Multiplication, appelez-la comme vous voudrez) qui, elle, aura
trois paramtres.
Nous allons voir juste aprs que la seconde faon a ma prfrence.

280

NOTRE PREMIRE FONCTION RCURSIVE


Tests et temps processeur
Il est temps de faire un bilan : Produit(a,b) doit tester si a est plus petit que b ou gal
car sinon il doit lancer Produit(b,a). Puis il doit tester si a vaut 0, auquel cas il renvoie
le rsultat, sinon il doit lancer Produit(a-1,b).
Nouveau problme, car en excutant Produit(a-1,b), notre programme va nouveau
tester si a-1 < b ! Or si a tait plus petit que b, a - 1 ne risque pas de devenir plus grand.
On effectue donc un test inutile. Ce pourrait tre anodin, sauf quun test prend du temps
au processeur et de la mmoire, et ce temps-processeur et cette mmoire seraient plus
utiles pour autre chose. Si nous demandons le rsultat de Produit(7418,10965), notre
programme devra effectuer 7418 tests inutiles ! Quel gchis.
Deuxime et dernier bilan :
Notre fonction Produit(a,b) se contentera de vrifier si a est plus petit ou gal b.
Et cest tout. Elle passera ensuite le relais une sous-fonction (que jappellerais
Pdt).
Notre sous-fonction Pdt sera la vraie fonction rcursive. Elle admettra trois paramtres : a, b et res. Le paramtre a devra tre imprativement plus petit que
b (cest le travail de la fonction-mre) et res permettra de transmettre la valeur
du rsultat dune rcursion lautre.

Une solution possible


Le code ci-dessous ne constitue en aucun cas la seule et unique solution, mais seulement
une solution possible :
1

function Produit (a , b : natural ) return natural is

2
3
4
5
6
7

-- - - - - - - - - - - - - - - - - - - - - - - - -Ins rer ici


--- le code source de
--la fonction Pdt
--- - - - - - - - - - - - - - - - - - - - - - - - -

8
9
10
11
12
13
14

begin
if a <= b
then return Pdt (a ,b , 0 ) ;
else return Pdt (b ,a , 0 ) ;
end if ;
end Produit ;

Pour plus de clart, jai crit une fonction Produit non rcursive, mais faisant appel
une sous-fonction Pdt dont le code source nest pas crit. Cela permet la fois deffectuer la disjonction de cas et surtout de fournir une fonction Produit avec seulement
deux paramtres. Rassurez-vous, je vous donne tout de mme le code de la sous-fonction
rcursive :
281

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


1
2
3
4
5
6
7

function Pdt (a ,b , res : natural ) return natural is


begin
if a = 0
then return res ;
else return Pdt (a -1 , b , res + b ) ;
end if ;
end Pdt ;

Lutilisateur final naura jamais accs directement la fonction Pdt. Celle-ci


tant crite au sein mme de la fonction Produit, sa dure de vie est restreinte
la fonction Produit. Impossible dy accder autrement.

Algorithme de recherche par dichotomie


Voici dsormais un nouveau programme raliser au cours duquel la rcursivit va
prendre tout son sens. Le principe est dimplmenter un programme Dichotomie() qui
recherche un nombre entier dans un tableau pralablement class par ordre croissant
(et sans doublons).

Principe
Par exemple, nous souhaiterions savoir o se trouve le nombre 9 dans le tableau suivant :
1
0

2
2

3
3

4
5

5
7

6
9

7
15

8
20

9
23

Une mthode de bourrin consiste passer tous les nombres du tableau en revue, de
gauche droite. Dans cet exemple, il faudra donc tester les 6 premiers nombres pour
savoir que le 6me nombre est un 9 ! Comme je vous lai dit, 6 tests, cest une mthode
de bourrin. Je vous propose de le faire en 3 tapes seulement avec la mthode par
dichotomie (prononcez dikotomie ).
a veut dire quoi dichotomie ?

Ce mot vient du grec ancien et signifie couper en deux . On retrouve le suffixe


tomie prsent dans les mots appendicectomie (opration consistant couper lappendice), mammectomie (opration consistant couper un sein pour lutter contre le
cancer du mme nom), lobotomie (opration consistant couper et retirer une partie
du cerveau). . . Bref, on va couper notre tableau en deux parties (pas ncessairement
gales). Pour mieux comprendre, nous allons drouler un algorithme par dichotomie
avec lexemple ci-dessus.
282

ALGORITHME DE RECHERCHE PAR DICHOTOMIE


Tout dabord, on regarde le nombre du milieu , cest--dire le numro 5 (opration :
1 + 9
= 5)
2
1
0

2
2

3
3

4
5

5
7

6
9

7
15

8
20

9
23

Comme 7 est plus petit que 9, on recommence mais seulement avec la partie suprieure
du tableau car comme le tableau est ordonn, il ne peut y avoir de 9 avant !
6
9

7
15

8
20

9
23

On prend nouveau le nombre du milieu . Lopration 6+9


2 donne 7,5 ! Donc nous
allons considrer que le nombre du milieu est le n7 (je sais, ce nest pas mathmatiquement rigoureux, mais cest bien pour cela que jutilise des guillemets depuis le
dbut).
6
9

7
15

8
20

9
23

Le nombre obtenu (15) est plus grand que 9, on recommence donc avec la partie infrieure du tableau.
6
9
Le nombre du milieu est le n6 (logique) et il vaut 9. Cest gagn ! Notre rsultat
est que le 9 est la 6me case .
6
9
Avec cet algorithme, je nai test que la 5me, la 7me et la 6me valeur du tableau, soit
seulement 3 tests au lieu de 6 pour la mthode bourrin (on parle de force brute). Et
cet cart peut trs vite saccrotre notamment pour rechercher de grandes valeurs dans
un grand tableau. Nous verrons dans le chapitre sur la complexit des algorithmes que
le premier algorithme a une complexit en O(n), tandis que le second a une complexit
en O(log(n)). Oui, je sais, pour vous cest du Chinois. Nous expliquerons cela plus tard,
retenez seulement que cela signifie que pour un petit tableau, lintrt de la dichotomie
est discutable ; en revanche pour un grand ou trs grand tableau, il ny a pas photo sur
la plus grande efficacit de la dichotomie.
283

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT

Mise en uvre
Nous allons dsormais implmenter notre programme Dichotomie(). Toutefois, vous
aurez remarqu quil utilise des tableaux de tailles diffrentes. Nous avons donc un
souci car nous ne savons dclarer que des tableaux de taille fixe. Deux solutions sont
possibles :
Regarder le chapitre de la partie 4 sur la gnricit pour pouvoir faire des tableaux
dont on ne connat pas pralablement la taille. Le problme, cest que lon risque
de vite se mlanger les pinceaux, dautant plus que ce chapitre est dans une partie
encore plus dure.
Faire en sorte que notre programme manipule un tableau T de taille fixe mais
aussi deux variables Min et Max qui indiqueront la plage du tableau qui nous
intresse (cherche dans le tableau T entre lindice Min et lindice Max ). Nous
retiendrons pour linstant, vous vous en doutez, cette seconde solution. Toutefois,
il sera judicieux de reprendre ce programme aprs avoir vu la gnricit.
Autre question : que donne la division 15/2 en Ada ? Rponse :
Reponse :

7_

Puisque nous divisons deux integer, Ada nous renvoie galement un integer. Or vous
savez bien que cette division ne tombe pas juste et quelle devrait donner 7,5 ! Mais
Ada doit faire un choix : soit la valeur approche lunit prs par excs (8) soit la
valeur approche lunit prs par dfaut (7). Le choix a t fait de renvoyer la valeur
par dfaut car elle correspond la partie entire du nombre dcimal. Toutefois, si vous
vouliez savoir durant la programmation de cet algorithme, si un nombre est pair ou
impair, je vous rappelle quil existe lopration MOD !
Exemple : 15 mod 2 = 1 (Donc 15 est impair) ; 18 mod 2 = 0 (Donc 18 est pair).
Dernier problme rgler : que faire sil ny a pas de 9 ? Renvoyer une valeur particulire ? Ce nest pas la meilleure ide. Lutilisateur final nest pas sens le savoir.
Renvoyer lindice de la valeur la plus proche ? Pourquoi pas mais si lutilisateur ne
connat pas cette nuance, cela peut poser problme. Le mieux serait de renvoyer deux
rsultats : un boolen Existe indiquant si la valeur existe ou non dans le tableau et une
variable Indice indiquant la valeur trouve (seulement si Existe vaut true).
Ds lors, plusieurs solutions soffrent vous : vous pouvez crer un type structur
(avec RECORD) pour renvoyer deux rsultats en mme temps. Ou vous pouvez crer
une procdure avec deux paramtres en mode OUT ou encore utiliser un pointeur sur
boolen comme paramtre votre fonction pour savoir savoir si le rsultat existe. A
vous de choisir.

Solution
Voici une solution possible pour le programme Dichotomie() :
284

ALGORITHME DE RECHERCHE PAR DICHOTOMIE


1

Procedure Dichotomie is

2
3
4

type T_Tableau is array ( 1 .. 50 ) of integer ;


choisir la taille du tableau
type T_Ptr_Bool is access boolean ;

-- vous de

5
6
7
8
9

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Ins rer ici le code source --de la fonction r cursive


--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

10
11
12
13

T : T_Tableau ;
Existe : T_Ptr_Bool ;
Indice : integer ;

14
15

begin

16
17
18
19
20

Init ( T ) ;
initialiser ce tableau
Existe := new boolean ;
et initialisation du pointeur
Existe . all := false ;
Indice := Dicho (T , 13 , Existe , T ' first , T ' last ) ;
cherche le nombre 13 dans le tableau T

-- vous d '
-- cr ation
-- on

21
22
23
24
25
26
27

if Existe . all
then put ( " Le nombre 13 est l ' indice numero " ) ;
put ( Indice ) ;
else put ( " Le nombre 13 n ' est pas pr sent dans le tableau " ) ;
end if ;
end Dichotomie ;

Dans le programme ci-dessus, on recherche le nombre 13 dans un tableau de 50 cases. La


procdure dinitialisation nest pas crite, je vous laisse le faire (pensez que le tableau
doit tre ordonn !). Voici maintenant le code de la fonction Dicho() :
1
2
3
4

function Dicho ( T : T_Tableau ;


N : integer ;
Existe : T_Ptr_Bool ;
Min , Max : integer ) return integer is

5
6

Milieu : integer ;

7
8

begin

9
10

Milieu := ( Min + Max ) / 2 ;

11
12
13
14

if T ( Milieu ) = N
nombre N cherch
then Existe . all := true ;
return Milieu ;

-- Cas o on trouve le

285

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


15

elsif Min = Max


-- Cas o l ' on n ' a pas
trouv N et o on ne peut plus continuer
then Existe . all := false ;
-- On indique que N n '
existe pas et on renvoie 0 .
return 0 ;

16
17
18
19

elsif T ( Milieu ) > N


-- Cas o on peut continuer
avec un sous - tableau
then return Dicho (T ,N , Existe , Min , Milieu - 1 ) ;
else return Dicho (T ,N , Existe , Milieu +1 , Max ) ;
end if ;

20
21
22
23
24
25

end Dicho ;

Nous avons cette fois ralis un programme o lutilisation de la rcursivit est vraiment pertinente. quoi reconnat-on quun programme pourrait tre trait de manire
rcursive ? Tout dabord il faut avoir besoin de rpter certaines actions. La question
est plutt : pourquoi choisir un algorithme rcursif plutt que itratif ? La rponse est
en grande partie dans la dfinition mme de la rcursivit. Il faut se trouver face
un problme qui ne se rsolve quen remployant les mmes procds sur un mme
objet. Les algorithmes rcursifs ncessitent gnralement de disjoindre deux cas : le cas
simple, trivial et le cas complexe. Ce dernier peut tre lui-mme subdivis en plusieurs
autres cas (a<b et a>b ; n pair et n impair. . .).

Quelques exercices
Exercice 1
nonc
Rdigez une fonction rcursive Factorielle( ) qui calcule. . . la factorielle du nombre
donn en argument. Pour exemple, la factorielle de 7 est donne ainsi :
7 ! = 7 6 5 4 3 2 1. Ainsi, votre fonction Factorielle(7) renverra
5040.
Solution
1
2
3
4
5
6
7

function factorielle ( n : integer ) return integer is


begin
if n > 0
then return n * factorielle (n - 1 ) ;
else return 1 ;
end if ;
end factorielle ;

286

QUELQUES EXERCICES
partir de 17 ! , des problmes commencent apparatre : des rsultats sont
ngatifs ce qui est impossible puisquon ne multiplie que des nombres positifs.
De plus, les rsultats ne grossissent plus.
Alors pourquoi ces rsultats ? Cest ce que lon appelle loverflow. Je vous ai souvent
mis en garde : linfini en informatique nexiste pas, les machines ont beau tre ultrapuissantes, elles ont toujours une limite physique. Par exemple, les integer sont cods
par le langage Ada sur 32 bits, cest--dire que lordinateur ne peut retenir que des
nombres forms de maximum 32 chiffres (ces chiffres ntant que des 0 ou des 1, cest
ce que lon appelle le code binaire, seul langage comprhensible par un ordinateur).
Pour tre plus prcis mme, le 1er chiffre (on dit le premier bit) correspond au signe :
0 pour positif et 1 pour ngatif. Il ne reste donc plus que 31 bits pour coder le nombre.
Le plus grand integer enregistrable est donc 231 1 = 2 147 483 647. Si jamais nous
enregistrions un nombre plus grand encore, il ncessiterait plus de bits que lordinateur
ne peut en fournir, cest ce que lon appelle un dpassement de capacit ou overflow.
Du coup, le seul bit supplmentaire possible est celui du signe + ou -, ce qui explique
les erreurs obtenues partir de 17.

Exercice 2
nonc
Rdigez une fonction rcursive Puissance(a,n) qui calcule la puissance n du nombre a
(cest--dire an ), mais seulement en utilisant des multiplications.
Solution
1
2
3
4
5
6
7

function puissance (a , n : integer ) return integer is


begin
if n > 0
then return a * puissance (a ,n - 1 ) ;
else return 1 ;
end if ;
end puissance ;

Si vous cherchez calculer 17^{11} , vous obtiendrez l aussi un rsultat


ngatif du un overflow.

Exercice 3
nonc
Rdigez des fonction rcursives Affichage( ) et Minimum( ). Chacune devra parcourir
un tableau, soit pour lafficher soit pour indiquer lindice du plus petit lment du
tableau.
287

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


Solution

type T_Tableau is array ( 1 .. 10 ) of integer ;

2
3
4
5
6
7
8
9
10
11
12
13

Procedure Affichage ( T : T_Tableau ) is


procedure Afg ( T : T_Tableau ; dbt : integer ) is
begin
put ( T ( dbt ) ,1 ) ; put ( " ; " ) ;
if dbt < T ' last
then Afg (T , dbt + 1 ) ;
end if ;
end Afg ;
begin
Afg (T ,T ' first ) ;
end Affichage ;

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

function Minimum ( T : T_Tableau ) return integer is


function minimum ( T : T_Tableau ; rang , res , min : integer )
return integer is
-- rang est l ' indice que l ' on va tester
-- res est le r sultat temporaire , l ' emplacement du
minimum trouv jusque l
-- min est le minimum trouv jusque l
min2 : integer ;
res2 : integer ;
begin
if T ( rang ) < min
then min2 := T ( rang ) ;
res2 := rang ;
else min2 := min ;
res2 := res ;
end if ;
if rang = T ' last
then return res2 ;
else return minimum (T , rang +1 , res2 , min2 ) ;
end if ;
end minimum ;
begin
return minimum (T ,T ' first +1 ,T ' first , T (T ' first ) ) ;
end Minimum ;

Il existe deux fonctions minimum, lune rcursive et lautre non ! Mais cela ne
pose pas de problme car le compilateur constate que le nombre de paramtres
est diffrent. Il est donc facile de les distinguer. De plus, dans votre procdure
principale, seule la fonction minimum non rcursive sera accessible.
288

QUELQUES EXERCICES

Exercice 4
nonc
Rdigez une fonction rcursive Effectif(T,x) qui parcourt un tableau T la recherche
du nombre dapparition de llment x. Ce nombre dapparition est appel effectif.
En le divisant par le nombre total dlment dans le tableau, on obtient la frquence
(exprime en pourcentages si vous la multipliez par 100). Cela vous permettra ainsi de
rdiger une seconde fonction appele Frquence(T,x). Le tableau T pourra contenir ce
que vous souhaitez, il peut mme sagir dun string.

Solution
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

function effectif ( T : T_Tableau ; x : integer ) return integer is


function effectif ( T : T_Tableau ; x : integer ; rang , eff :
integer ) return integer is
eff2 : integer ;
begin
if T ( rang ) = x
then eff2 := eff + 1 ;
else eff2 := eff ;
end if ;
if rang =T ' last
then return eff2 ;
else return effectif (T ,x , rang +1 , eff2 ) ;
end if ;
end effectif ;
begin
return effectif (T ,x ,T ' first , 0 ) ;
end effectif ;

17
18
19
20
21

function frequence ( T : T_Tableau ; x : integer ) return float is


begin
return float ( effectif (T , x ) ) / float (T ' length ) * 100 . 0 ;
end frequence ;

L encore, je cre deux fonctions effectif() de manire ce que lemploi


de la premire (non rcursive) soit facilit (peu de paramtres). La seconde
fonction effectif( ), rcursive et coince dans la premire, ncessite plus
de paramtres qui nont pas tre connus par lutilisateur final de ce code.

Nous venons de crer plusieurs fonctions ou procdures lies aux tableaux.


Peut-tre pourriez-vous les ajouter votre package P_Integer_Array.
289

CHAPITRE 19. FONCTIONS ET PROCDURES II : LA RCURSIVIT


Bien, nous avons dsormais termin ce chapitre sur la rcursivit. Cette notion va
nous tre utile au prochain chapitre puisque nous allons aborder un nouveau type
composite : les types abstraits de donnes. Ce chapitre fera appel aux pointeurs, aux
types structurs et la rcursivit. Ce sera le dernier chapitre thorique de cette partie
et probablement le plus compliqu, donc si vous avez encore des difficults avec lune
de ces trois notions, je vous invite relire les chapitres prcdents et vous entraner
car les listes sont des objets un peu plus compliqus manipuler.
Jespre que ce chapitre vous aura permis davoir une ide assez prcise de ce quest la
rcursivit et de lintrt quelle peut reprsenter en algorithmique. Pour disposer dun
second point de vue, je vous conseille galement la lecture du tutoriel de bluestorm disponible sur OpenClassrooms : http://www.fr.openclassrooms.com/informatique/
cours/la-recursivite-1. Toutefois, sil est plus complet concernant la notion de rcursivit, ce tutoriel nest pas rdig en Ada, vous devrez donc vous contenter de ses
explications.

En rsum
Les boucles rcursives permettent, comme les boucles itratives, de rpter une
tche. Il suffit pour cela quune procdure ou une fonction sappelle elle-mme.
Un programme rcursif doit envisager plusieurs cas et notamment le cas trivial
qui mettra fin la boucle rcursive. Une fois ce cas vident tabli, vous devez
rflchir lopration effectuer pour passer ltape suprieure.
Les algorithmes rcursifs ncessitent plus dentranement et de rflexion que les
algorithmes itratifs. Nous verrons au prochain chapitre que certaines structures
sadaptent mieux ce type de raisonnement. Cest pourquoi vous devez malgr
tout vous entraner la rcursivit, malgr les difficults quelle peut prsenter.

290

Chapitre

20

Les Types Abstraits de Donnes : listes,


files, piles. . .
Difficult :
Dans ce chapitre, nous allons aborder un nouveau type composite : les Types Abstraits de
Donnes, aussi appels TAD. De quoi sagit-il ? Eh bien pour le comprendre, il faut partir
du problme qui amne leur cration. Nous avons vu au dbut de la partie III le type
ARRAY. Les tableaux sont trs pratiques mais ont un norme inconvnient : il sont contraints.
Leur taille est fixe au dbut du programme et ne peut plus bouger. Imaginons que nous
ralisions un jeu daventure. Notre personnage dcouvre des trsors au fil de sa qute, quil
ajoute son coffre trsor. Ce serait idiot de crer un objet coffre de type Tableau. Sil est
prvu pour contenir 100 items et que lon en dcouvre 101, il sera impossible denregistrer
notre 101me item dans le coffre ! Il serait judicieux que le coffre puisse contenir autant de
trsors quon le souhaite.

291

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
Mais il existe bien des unbounded_strings ? ! ? Alors il y a peut-tre des unbounded_array ?
Eh bien non, pas tout fait. Nous allons donc devoir en crer nous mmes, avec nos
petits doigts : des listes dlments possiblement infinies. Cest ce que lon appelle les
TAD. Nous ferons un petit tour dhorizon thorique de ce que sont les Types Abstraits
de Donnes. Puis nous crerons dans les parties suivantes quelques types abstraits
avant de voir ce que le language Ada nous propose. Nous crerons galement les outils
minimums ncessaires leur manipulation avant de voir un exemple dutilisation.

Quest-ce quun Type Abstrait de Donnes ?


Un premier cas
Comme je viens de vous le dire, les Types Abstraits de Donnes peuvent simaginer
comme des listes dlments. Les tableaux constituent un type possible de TAD.
Mais ce qui nous intresserait davantage, ce serait des listes infinies . Commenons
par faire un point sur le vocabulaire employ. Lorsque je parle de liste , je commets un
abus de langage. En effet, il existe plusieurs types abstraits, et les listes (je devrais parler
plus exactement de "listes chanes" par distinction avec les tableaux) ne constituent
quun type abstrait parmi tant dautres. Mais pour mieux comprendre ce que peut tre
un TAD, nous allons pour linstant nous accommoder de cet abus (je mettrai donc le
mot liste entre guillemets). Voici une reprsentation possible dun Type Abstrait de
Donne :
Liste vide : un emplacement mmoire a t rquisitionn pour la liste, mais
il ne contient rien (voir figure 20.1).

Figure 20.1 Liste vide


Liste contenant un lment : le premier emplacement mmoire contient
un nombre (ici 125) et pointe sur un second emplacement mmoire, encore vide
pour linstant (voir figure 20.2).

Figure 20.2 Liste contenant un lment


292

QUEST-CE QUUN TYPE ABSTRAIT DE DONNES ?


Liste contenant 3 lments : trois emplacements mmoires ont t rquisitionns pour contenir des nombres qui se sont ajouts les uns la suite des autres.
Chaque emplacement pointe ainsi sur le suivant. Le troisime pointe quant lui
sur un quatrime emplacement mmoire vide (voir figure 20.3).

Figure 20.3 Liste contenant 3 lments

Autres cas
Lexemple ci-dessus est particulier, plusieurs gards. Il nexiste pas quun seul type de
liste (et je ne compte pas les traiter toutes). La liste vue prcdemment dispose
de certaines proprits quil nous est possible de modifier pour en laborer dautres
types.
Contraintes de longueur
La liste vue ci-dessus na pas de contrainte de longueur. On peut y ajouter autant
dlments que la mmoire de lordinateur peut en contenir mais il est impossible
dobtenir le 25me lment directement, il faut pour cela la parcourir partir du dbut.
Les tableaux sont quant eux des listes contraintes et indexes de manire accder
directement tout lment, mais la taille est donc dfinie ds le dpart et ne peut plus
tre modifie par la suite. Mais vous aurez compris que dans ce chapitre, les tableaux
et leurs contraintes de longueur ne nous intressent gure.
Chanage
Chaque lment pointe sur son successeur. On dit que la liste est simplement
chane. Une variante serait que chaque lment pointe galement sur son prdcesseur.
On dit alors que la liste est doublement chane, comme la figure 20.4.

Figure 20.4 Liste doublement chane

293

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
Linarit
Chaque lment ne pointe que sur un seul autre lment. Il est possible de diversifier
cela en permettant chaque lment de pointer sur plusieurs autres (que ce soit un
nombre fixe ou illimit dlments). On parlera alors darbre, comme la figure 20.5
et non plus de liste .

Figure 20.5 Arbre binaire (deux successeurs possibles)

Mthode dajout
Chaque lment ajout la t en fin de liste . Cette mthode est dite FIFO pour
First In/First Out, cest--dire premier arriv, premier servi (voir figure 20.6). Le
second lment ajout sera donc en deuxime position, le troisime lment en troisime
position. . . notre liste est ce que lon appelle une file (en Franais dans le texte).
Cest ce qui se passe avec les imprimantes relies plusieurs PC : le premier appuyer
sur Impression aura la priorit sur tous les autres. Logique, me direz-vous.
Mais ce nest pas obligatoirement le cas. La mthode inverse est dite LIFO pour
Last In/First Out , cest--dire dernier arriv, premier servi (voir figure 20.7). Le
premier lment ajout sera videmment le premier de la liste . Lorsque lon ajoute
un second lment, cest lui qui devient le premier de la liste et lex-premier devient
le second. Si on ajoute un troisime lment, il sera le premier de la liste , lexpremier devient le second, lex-second (lex-ex-premier) devient le troisime. . . on dira
alors que notre liste est une pile. On peut se reprsenter la situation comme une
pile dassiette : la dernire que vous ajoutez devient la premire de la pile. Lhistorique
de navigation de votre navigateur internet agit tel une pile : lorsque vous cliquez sur
prcdent, la premire page qui saffichera sera la dernire laquelle vous aviez accd.
294

QUEST-CE QUUN TYPE ABSTRAIT DE DONNES ?

Figure 20.6 File : ajout dlment selon la mthode FIFO

La liste des modifications apportes un document est galement gre telle une
pile : cliquez sur Annuler et vous annulerez la dernire modification.

Figure 20.7 Pile : ajout dlment selon la mthode LIFO

295

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
Cyclicit
Le dernier lment de notre liste ne pointe sur rien du tout ! Or nous pourrions faire
en sorte quil pointe sur le premier lment. Il ny a alors ni dbut, ni fin : notre liste
est ce que lon appelle un cycle (voir figure 20.8). Il est bien sr possible de combiner

Figure 20.8 Cycle de quatre lments


diffrentes proprits : arbres partiellement cycliques, cycles doublement chans. . .
pour laborer de nouvelles structures.

Primitives
Mais un type abstrait de donnes se dfinit galement par ses primitives. De quoi
sagit-il ? Les primitives sont simplement les fonctions essentielles sappliquant notre
structure. Ainsi, pour une file, la fonction Ajouter_a_la_fin( ) constitue une primitive.
Pour une pile, ce pourrait tre une fonction Ajouter_sur_la_pile( ).
En revanche, une fonction Ranger_a_l_envers(p : pile ) ou Mettre_le_chantier(f :
file) ou Reduire_le_nombre_de_branche(a :arbre) ne sont pas des primitives : ce ne
sont pas des fonctions essentielles au fonctionnement et lutilisation de ces structures
(mme si ces fonctions ont tout fait le droit dexister).
Donc crer un TAD tel une pile, une file. . . ce nest pas seulement crer un nouveau
type dobjet, cest galement fournir les primitives qui permettront au programmeur
final de le crer, de le dtruire, de le manipuler. . . sans avoir besoin de connatre sa
296

LES PILES
structure. Cest cet ensemble TAD-Primitives qui constitue ce que lon appelle une
Structure de donnes.
Mais alors, quest-ce que cest exactement quune liste (sans les guillemets) ?
Tu nous a mme parl de liste chane : cest quoi ?
Une liste chane est une structure de donnes (cest dire un type abstrait de donnes
fourni avec des primitives) linaire et non contrainte. Elle peut tre cyclique ou non,
simplement ou doublement chane. Laccs aux donnes doit pouvoir se faire librement
(ni en FIFO, ni en LIFO) : il doit tre possible dajouter/modifier/retirer nimporte
quel lment sans avoir pralablement retir ceux qui le prcdaient ou le suivaient.
Toutefois, il faudra trs souvent parcourir la liste chane depuis le dbut avant de
pouvoir effectuer un ajout, une modification ou une suppression.
Pour mieux comprendre, je vous propose de crer quelques types abstraits de donnes
(que je nommerai dsormais TAD, je sais je suis un peu fainant ). Je vous propose
de construire successivement une pile puis une file avant de nous pencher sur les listes
chanes. Cela ne couvrira srement pas tout le champ des TAD (ce cours ny suffirait
pas) mais devrait vous en donner une bonne ide et vous livrer des outils parmi les
plus utiles tout bon programmeur.

Les piles
Nous allons tout dabord crer un type T_Pile ainsi que les primitives associes. Pour
faciliter la rutilisation de ce type composite, nous crirons tout cela dans un package
que nous appellerons P_Piles. Vous remarquerez les quelques subtilits dcriture :
P_Pile pour le package ; T_Pile pour le type ; Pile pour la variable. Ce package comportera donc le type T_Pile et les primitives. Nous ajouterons ensuite quelques procdures et fonctions utiles (mais qui ne sont pas des primitives).

Cration du type T_Pile


Tout dabord, rappelons succinctement ce que lon entend par pile . Une pile est un
TAD linaire, non cyclique, simplement chan et non contraint. Tout ajout/suppression
se fait par la mthode LIFO : Last In, First Out ; cest dire que le programmeur final
ne peut accder qu llment plac sur le dessus de la pile. Alors, comment crer une
liste infinie de valeurs lies les unes aux autres ? Nous avions dors et dj approch cette
ide lors du prcdent chapitre sur les pointeurs. Il suffirait dun pointeur pointant sur
un pointeur pointant sur un pointeur pointant sur un pointeur. . . Mais, premier souci,
o et comment enregistre-t-on nos donnes sil ny a que des pointeurs ? Une ide ?
Avec un type structur bien sr ! Schmatiquement, nous aurions ceci :
1
2
3

DECLARATION DE MonType :
Valeur : integer , float ou que sais - je ;
P o i n t e u r _ v e r s _ l e _ pr o c h a i n : pointeur vers MonType ;

297

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
4

FIN DE DECLARATION

Nous pourrions galement ajouter une troisime composante (appele Index ou Indice)
pour numroter nos valeurs. Nous devrons galement dclarer notre type de pointeur
ainsi :
1

TYPE T_Pointeur IS ACCESS MonType ;

Sauf que, deuxime souci, si nous le dclarons aprs MonType, le compilateur va se


demander quoi correspond la composante Pointeur_vers_le_prochain. Et si nous
dclarons T_Pointeur avant MonType, le compilateur ne comprendra pas vers quoi il
est sens point.
Comment rsoudre ce dilemme ? Cest le problme de luf et de la poule : qui doit apparatre en premier ? Rflchissez. L encore, vous connaissez la solution. Nous lavons
employe pour les packages : il suffit demployer les spcifications ! Il suffit que nous
dclarions MonType avec une spcification (pour rassurer le compilo ), puis le type
T_Pointeur et enfin que nous dclarions vraiment MonType. Compris ? Alors voil le
code correspondant :
1

TYPE T_Cellule ;
l ment de la pile

-- T_Cellule correspond un

2
3

TYPE T_Pile IS ACCESS T_Cellule ;


pointeur sur un l ment

-- T_Pile correspond au

4
5
6
7
8
9
10

TYPE T_Cellule IS
RECORD
Valeur : Integer ; -- Ici on va cr er une pile d ' entiers
Index
: Integer ; -- Le dernier l ment aura l ' index N 1
Suivant : T_Pile ; -- Le suivant correspond une sous pile
END RECORD ;

La composante Index est faite pour pouvoir indexer les lments de notre
liste, les numroter en quelques sortes. La cellule en bas de la pile (le
premier lment ajout) portera toujours le numro 1. La cellule le plus au
dessus de la pile aura donc un index correspondant au nombre dlments
contenus dans la pile.

Cration des primitives


Lide, en crant ce package, est de concevoir un type T_Pile et toutes les primitives
ncessaires son utilisation de sorte que tout programmeur utilisant ce package nait
plus besoin de connatre la structure du type T_Pile : il doit tout pouvoir faire avec les
primitives fournies sans avoir mettre les mains dans le cambouis (Nous verrons plus
tard comment lui interdire tout accs ce cambouis avec la privatisation de nos
298

LES PILES
packages). De quelles primitives a-t-on alors besoin ? En voici une liste avec le terme
anglais gnralement utilis et une petite explication :
Empiler (Push) : ajoute un lment sur le dessus de la pile. Cet lment devient
alors le premier et son indice (composante Index) est suprieur dune unit
lex-premier lment.
Dpiler (Pop) : retire le premier lment de la pile, la premire cellule. Cette
fonction renvoie en rsultat la valeur de cette cellule.
Pile_Vide (Empty) : renvoie TRUE si la pile est vide (cest--dire si pile =
NULL). Primitive importante car il est impossible de dpiler une pile vide.
Longueur (Length) : renvoie la longueur de la pile, cest dire le nombre de
cellules quelle contient.
Premier (First) : renvoie la valeur de la premire cellule de la pile sans la dpiler.
Bien, il ne nous reste plus qu crer ces primitives. Commenons par la procdure
Push(P,n). Elle va recevoir une pile P, qui nest rien dautre quun pointeur vers la
cellule situe sur le dessus de la pile. P sera en mode IN OUT. Elle recevra galement
un Integer n (puisque nous avons choisi les Integer, mais nous pourrions tout aussi
bien le faire avec des float ou autre chose). Elle va devoir crer une cellule (et donc un
index), la faire pointer sur la cellule situe sur le dessus de la pile avant dtre elle-mme
pointe par P. Compris ? Alors allez-y !
Pensez bien grer tous les cas : la pile P est vide (P = NULL) et la
pile P nest pas vide ! Car je vous rappelle que si P = NULL, alors crire
P.all entranera une erreur.
1
2
3
4
5
6
7
8
9
10
11

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) IS


Cell : T_Cellule ;
BEGIN
Cell . Valeur := N ;
IF P /= NULL
THEN Cell . Index := P . All . Index + 1 ;
Cell . Suivant := P . All ' ACCESS ;
ELSE Cell . Index := 1 ;
END IF ;
P := NEW T_Cellule '( Cell ) ;
END Push ;

Attention, il ne surtout pas crire le code suivant :


1

P := Cell ' access ;

Pourquoi ? Simplement parce que lobjet Cell a une dure de vie limite la procdure
Push. Une fois cette dernire termine, lobjet Cell de type T_Cellule disparat et notre
pointeur pointerait alors sur. . . rien du tout !
Votre procdure Push() est cre ? Enregistrez votre package et crez un programme
afin de le tester. Exemple :
299

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
1
2

with ada . Text_IO , Ada . Integer_Text_IO , P_Pile ;


use ada . Text_IO , Ada . Integer_Text_IO , P_Pile ;

Procedure test is
P : T_Pile ;
BEGIN
Push (P , 3 ) ; Push (P , 5 ) ; Push (P , 1 ) ; Push (P , 13 ) ;

4
5
6
7
8
9
10
11
12
13

Put ( P . all . Index ) ;


new_line ;
Put ( P . Suivant . Index ) ;
) ; new_line ;
Put ( P . Suivant . suivant . Index ) ;
suivant . Valeur ) ; new_line ;
Put ( P . suivant . suivant . suivant . Index ) ;
Suivant . Suivant . Valeur ) ;
end Test ;

Put ( P . all . Valeur ) ;


Put ( P . Suivant . Valeur
Put ( P . Suivant .
Put ( P . Suivant .

Maintenant, la procdure Pop(P,N). Je ne reviens pas sur les paramtres ou sur le sens
de cette fonction, jen ai dj parl. Je vous rappelle toutefois que cette procdure doit
retourner, non pas un objet de type T_Cellule, mais une variable de type Integer,
et rtrcir la pile. Les paramtres devront donc tre en mode OUT (voire IN OUT).
Dernier point, si la pile est vide, le programmeur qui utilisera votre package (appelons-le
programmeur final ) ne devrait pas la dpiler, mais il lui revient de le vrifier laide
de la primitive Empty : ce nest pas le rle de nos primitives de grer les ventuelles
btises du programmeur final. En revanche, cest vous de grer le cas o la pile ne
contiendrait quune seule valeur et deviendrait vide dans le code de votre primitive.
1
2
3
4
5
6
7
8
9

PROCEDURE pop ( P : IN OUT T_Pile ; N : OUT Integer ) IS


BEGIN
N := P . all . valeur ;
-- ou P . valeur
--P . all est cens exister , ce sera
au programmeur final de le v rifier
IF P . all . suivant /= NULL
THEN P := P . suivant ;
ELSE P := null ;
END IF ;
END Pop ;

Maintenant que vos deux procdures principales sont cres (je vous laisse le soin de les
tester) nous navons plus que trois primitives (assez simples) coder : Empty( ), First( )
et Length( ). Toutes trois sont des fonctions ne prenant quun seul paramtre : une pile
P. Elles retourneront respectivement un boolen pour la premire et un integer pour les
deux suivantes. Je vous cris ci-dessous le code source de P_Pile.adb et P_Pile.ads.
Merci qui ?
1

PACKAGE BODY P_Pile IS

2
3
4

300

PROCEDURE Push (
P : IN OUT T_Pile ;

LES PILES
5
6
7
8
9
10
11
12
13
14
15
16
17

N : IN
Integer ) IS
Cell : T_Cellule ;
BEGIN
Cell . Valeur := N ;
IF P /= NULL
THEN
Cell . Index := P . All . Index + 1 ;
Cell . Suivant := P . All ' ACCESS ;
ELSE
Cell . Index := 1 ;
END IF ;
P := NEW T_Cellule '( Cell ) ;
END Push ;

18
19
20
21
22
23
24
25
26
27
28
29
30
31

PROCEDURE Pop (
P : IN OUT T_Pile ;
N :
OUT Integer ) IS
BEGIN
N := P . All . Valeur ;
-- ou P . valeur
--P . all est cens exister , ce sera au programmeur final
de le v rifier
IF P . All . Suivant /= NULL
THEN
P := P . Suivant ;
ELSE
P := NULL ;
END IF ;
END Pop ;

32
33
34
35
36
37
38
39
40
41
42
43

FUNCTION Empty (
P : IN
T_Pile )
RETURN Boolean IS
BEGIN
IF P = NULL
THEN
RETURN True ;
ELSE
RETURN False ;
END IF ;
END Empty ;

44
45
46
47
48
49
50
51

FUNCTION Length ( P : T_Pile ) RETURN Integer IS


BEGIN
IF P = NULL
THEN RETURN 0 ;
ELSE RETURN P . Index ;
END IF ;
END Length ;

52
53

FUNCTION First ( P : T_Pile ) RETURN Integer IS

301

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
BEGIN
RETURN P . Valeur ;
END First ;

54
55
56
57
58

END P_Pile ;
PACKAGE P_Pile IS

1
2

TYPE T_Cellule ;
un l ment de la pile

-- T_Cellule correspondant

TYPE T_Pile IS ACCESS ALL T_Cellule ; -- T_Pile correspondant


au pointeur sur un l ment

5
6

TYPE T_Cellule IS
RECORD
Valeur : Integer ; -- On cr e une pile d ' entiers
Index
: Integer ;
Suivant : T_Pile ; -- Le suivant correspond une " sous
- pile "
END RECORD ;

7
8
9
10
11
12
13

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;

14
15

PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;

16
17

FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;

18
19
20
21
22

FUNCTION Length ( P : T_Pile ) RETURN Integer ;


FUNCTION First ( P : T_Pile ) RETURN Integer ;
END P_Pile ;

Jouons avec le package P_Pile


Notre package est enfin fini. Notre TAD est prt lemploi et jimpose maintenant une
rgle : interdiction de manipuler nos piles autrement qu laide de nos 5 primitives !
Donc, plus de P.valeur, seulement des First(P) ! Plus de P.suivant, il faudra faire avec
Pop(P) ! Cela va quelque peu compliquer les choses mais nous permettra de comprendre
comment se manipulent les TAD sans pour autant partir dans des programmes de 300
lignes. Lobjectif avec notre type T_Pile, sera de crer une procdure Put(p : T_Pile)
qui se charge dafficher une pile sous forme de colonne. Attention, le but de ce cours
nest pas de crer toutes les fonctions ou procdures utiles tous les TAD. Il serait par
exemple intressant de crer une procdure ou une fonction get(P). Mais nous nallons
nous concentrer que sur une seule procdure chaque fois. Libre vous de combler les
manques.
Concernant notre procdure Put(P), deux possibilits soffrent nous : soit nous employons une boucle itrative (LOOP, FOR, WHILE), soit nous employons une boucle
302

LES PILES
rcursive. Vous allez vous rendre compte que la rcursivit sied parfaitement aux TAD
(ce sera donc ma premire solution), mais je vous proposerai galement deux autres
faons de procder (avec une boucle FOR et une boucle WHILE). Voici le premier code,
avec la mthode rcursive :
1
2
3
4
5
6
7
8
9
10
11

procedure Put ( P : T_Pile ) is


Pile : T_Pile := P ;
N : integer ;
begin
if not Empty ( Pile )
then pop ( Pile , N ) ;
Put ( N ) ;
new_line ;
Put ( Pile ) ;
end if ;
end Put ;

Regardons un peu ce code. Tout dabord, cest un code que jaurais tout fait pu
vous proposer avant le chapitre sur les pointeurs : qui peut deviner en voyant ces lignes
comment est constitu le type T_Pile ? Personne, car nous avons fourni tous les moyens
pour ne pas avoir besoin de le savoir (cest ce que lon appelle lencapsulation, encore
une notion que nous verrons dans la partie IV). Que fait donc ce code ? Tout dabord
il teste si la pile est vide ou non. Ce sera toujours le cas. Si elle est vide, cest fini ;
sinon, on dpile la pile afin de pouvoir afficher son premier lment puis on demande
dafficher la nouvelle pile, prive de sa premire cellule. Jaurais pu galement crire
Put(First(Pile)) avant de dpiler, mais cela maurait contraint manipuler deux fois
la pile sans pour autant conomiser de variable.
Dailleurs, tu aurais aussi pu conomiser une variable ! Ton objet Pile na
aucun intrt, il suffit de dpiler P et dafficher P !
Non ! Surtout pas ! Tout dabord, je vous rappelle que P est obligatoirement en mode
IN puisquil est transmis la fonction Empty(). Or, la procdure pop( ) a besoin
dun paramtre en mode OUT pour pouvoir dpiler. Il y aurait un conflit. Ensuite, en
supposant que le compilateur ne bronche pas, que deviendrait P aprs son affichage ?
Eh bien P serait vide, on aurait perdu toutes les valeurs qui la composait, simplement
pour un affichage. Cest pourquoi jai cr un objet Pile qui sert ainsi de roue de
secours . Voici maintenant une solution itrative :
1
2
3
4
5

while not Empty ( Pile ) loop


put ( First ( P ) ) ;
pop ( Pile , N ) ;
new_line ;
end loop ;

On rpte ainsi les oprations tant que la liste nest pas vide : afficher le premier lment, dpiler. Enfin, voici une seconde solution itrative (dernire solution propose) :
1

for i in 1 .. Length ( P ) loop

303

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
2
3
4
5

put ( first ( Pile ) ) ;


pop ( Pile , N ) ;
new_line ;
end loop ;

Ce qui est utilis ici, ce nest pas le fait que la liste soit vide, mais le fait que sa longueur
soit nulle. Les oprations sont toutefois toujours les mmes.

Les files
Implmentation
Deuxime exemple, les files. Nous allons donc crer un nouveau package P_File. La
diffrence avec la pile ne se fait pas au niveau de limplmentation. Les files restent
linaires, non cycliques, simplement chanes et non contraintes.
1
2
3
4
5
6
7
8

TYPE T_Cellule ;
TYPE T_File IS ACCESS ALL T_Cellule ;
TYPE T_Cellule IS
RECORD
Valeur : Integer ;
Index
: Integer ;
Suivant : T_File ;
END RECORD ;

La diffrence se fait donc au niveau des primitives puisque cest la mthode dajout
qui diffre. Les files sont gres en FIFO : first in, first out. Imaginez pour cela une file
dattente une caisse : tout nouveau client (une cellule) doit se placer la fin de la
file, la caissire (le programmeur final ou le programme) ne peut traiter que le client
en dbut de file, les autres devant attendre leur tour. Alors de quelles primitives a-t-on
besoin ?
Enfiler (Enqueue) : ajoute un lment la fin de la file. Son indice est donc
suprieur dune unit lancien dernier lment.
Dfiler (Dequeue) : renvoie le premier lment de la file et le retire. Cela implique de renumroter toute la file.
File_Vide (Empty) ; Longueur (Length) ; Premier (First) : mme sens et
mme signification que pour les piles.
Notre travail consistera donc modifier notre package P_Pile en consquence. Attention ne pas aller trop vite ! Les changements, mme sils semblent a priori mineurs, ne
portent pas seulement sur les noms des primitives. Lajout dune cellule notamment,
nest plus aussi simple quauparavant. Ce travail tant relativement simple (pour peu
que vous le preniez au srieux), il est impratif que vous le fassiez par vous-mme !
Cela constituera un excellent exercice sur les pointeurs et la rcursivit. Ne consultez
mon code qu titre de correction. Cest important avant dattaquer des TAD plus
complexes.
304

LES FILES
1

PACKAGE BODY P_File IS

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

PROCEDURE Enqueue ( F : IN OUT T_File ; N : IN


Integer )
IS
Cell : T_Cellule ;
BEGIN
Cell . Valeur := N ;
Cell . Suivant := NULL ;
IF F = NULL
-- Cas o la
liste est vide
THEN Cell . Index := 1 ;
F := NEW T_Cellule '( Cell ) ;
ELSIF F . Suivant = NULL
-- Cas o l ' on
se " situe " sur le dernier l ment
THEN Cell . Index := F . Index + 1 ;
F . Suivant := NEW T_Cellule '( Cell ) ;
ELSE Enqueue ( F . Suivant , N ) ;
-- Cas o l ' on
n ' est toujours pas la fin de la file
END IF ;
END Enqueue ;

17
18

PROCEDURE Dequeue ( F : IN OUT T_File ; N :


IS

OUT Integer )

19
20
21
22
23
24
25
26

procedure Decrement ( F : IN T_File ) is


begin
if F /= null
then F . index := F . index - 1 ;
Decrement ( F . suivant ) ;
end if ;
end Decrement ;

27
28
29
30
31
32
33
34
35

BEGIN
N := F . Valeur ;
--F . all est cens exister ,
ce sera au Programmeur final de le v rifier .
IF F . Suivant /= NULL
THEN decrement ( F ) ;
-- !!! Il faut d cr menter
les indices !!!
F := F . Suivant ;
ELSE F := NULL ;
END IF ;
END Dequeue ;

36
37
38
39
40
41
42
43

FUNCTION Empty ( F : IN
T_File ) RETURN Boolean IS
BEGIN
IF F = NULL
THEN RETURN True ;
ELSE RETURN False ;
END IF ;
END Empty ;

305

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
44

FUNCTION Length ( F :
BEGIN
IF F = NULL
THEN RETURN
ELSIF F . Suivant =
THEN RETURN
ELSE RETURN
END IF ;
END Length ;

45
46
47
48
49
50
51
52
53

T_File ) RETURN Integer IS


0 ;
NULL
F . Index ;
Length ( F . Suivant ) ;

54

FUNCTION First ( F : T_File ) RETURN Integer IS


BEGIN
RETURN F . Valeur ;
END First ;

55
56
57
58
59
60

END P_File ;

PACKAGE P_Pile IS

1
2

TYPE T_Cellule ;
un l ment de la pile

-- T_Cellule correspondant

TYPE T_Pile IS ACCESS ALL T_Cellule ; -- T_Pile correspondant


au pointeur sur un l ment

5
6

TYPE T_Cellule IS
RECORD
Valeur : Integer ; -- On cr e une pile d ' entiers
Index
: Integer ;
Suivant : T_Pile ; -- Le suivant correspond une " sous
- pile "
END RECORD ;

7
8
9
10
11
12
13
14
15
16
17
18
19

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;


PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;
FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;
FUNCTION Length ( P : T_Pile ) RETURN Integer ;
FUNCTION First ( P : T_Pile ) RETURN Integer ;
END P_Pile ;

La procdure enqueue a besoin dtre rcursive car on ajoute une cellule


la fin de la liste et non au dbut ! La procdure dequeue doit non seulement
supprimer le premier lment de la file, mais galement dcrmenter tous les
indices, sans quoi le premier sera le n2 ! ? ! Enfin, la fonction Length doit
elle aussi tre rcursive pour lire lindice de la dernire cellule.

306

LES FILES

Amusons-nous encore
Comme promis, nous allons dsormais tester notre structure de donnes. Mais hors de
question de se limiter refaire une procdure Put( ) ! Ce serait trop simple. Cette fois
nous allons galement crer une fonction de concatnation !
Rooh ! Encore de la thorie ! Il a pas fini avec ses termes compliqus ?

Pas demballement ! La concatnation est une chose toute simple ! Cela consiste juste
mettre bout bout deux listes pour en faire une plus grande ! Le symbole rgulirement
utilis en Ada (et dans de nombreux langages) est le symbole & (le ET commercial,
aussi appel esperluette). Vous vous souvenez ? Nous lavions vu lors du chapitre 12 sur
les chanes de caractres. Donc vous de crer cette fonction de sorte que lon puisse
crire : File1 := File2 & File 3 ! Noubliez pas auparavant de crer une procdure put(
) pour contrler vos rsultats et prenez garde que laffichage dune file ne la dfile pas
dfinitivement !
1
2
3
4
5
6
7
8
9

procedure Put ( F : in out T_File ) is


N : integer ;
begin
for i in 1 .. length ( F ) loop
dequeue (F , N ) ;
put ( N ) ;
enqueue (F , N ) ;
-- on n ' oublie pas de r enfiler l '
lement N pour ne pas vider notre file !
end loop ;
end Put ;

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

function "& " ( left , right : in T_File ) return T_File is


N : integer ;
L : T_File := Left ;
R : T_File := Right ;
res : T_File ;
begin
for i in 1 .. length ( left ) loop
-- on place les l ments de
la file de gauche dans la file r sultat
dequeue (L , N ) ;
enqueue ( res , N ) ;
enqueue (L , N ) ;
end loop ;
for i in 1 .. length ( right ) loop
-- puis on place ceux de la
file de droite dans la file r sultat
dequeue (R , N ) ;
enqueue ( res , N ) ;
enqueue (R , N ) ;
end loop ;
return res ;

307

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
28

end ;

Cette fois, jai opt pour des algorithmes itratifs. Aprs un rapide essai, on se
rend compte que les algorithmes rcursifs ont tendance inverser nos listes.
Explication : si vous avez une liste (3 ;6 ;9) et que vous employez un algorithme rcursif
vous allez dabord dfiler le 3 pour lafficher et avant de le renfiler, vous allez relancer
votre algorithme, gardant le 3 en mmoire. Donc vous dfiler le 6 pour lafficher et avant
de le renfiler, vous allez relancer votre algorithme, gardant le 6 en mmoire. Enfin, vous
dfiler le 9 pour lafficher, la liste tant vide dsormais vous renfiler le 9, puis le 6, puis
le 3. Au final, un simple affichage fait que vous vous retrouvez non plus avec une liste
(3 ;6 ;9) mais (9 ;6 ;3). Ce problme peut tre rsolu en employant un sous-programme
qui inversera la file, mais cette faon de procder vous semble-t-elle naturelle ? Pas
moi toujours.

Les listes chanes


Quelques rappels
Comme vous vous en tes srement rendu compte en testant vos fonctions et procdures, lutilisation des files, si elle semble similaire celle des piles, est en ralit fort
diffrente. Chacun de ces TAD a ses avantages et inconvnients. Mais le principal dfaut de ces TAD est dtre oblig de dfiler/dpiler les lments les uns aprs les autres
pour pouvoir les traiter, ce qui oblige les rempiler/renfiler par la suite avec tous les
risques que cela implique quant lordre des lments. Notre troisime TAD va donc
rgler ce souci puisque nous allons traiter des listes chanes (et ce sera le dernier TAD
linaire que nous traiterons).
Comme dit prcdemment, les listes chanes sont linaires, doublement chanes, non
cycliques et non contraintes. Qui plus est, elles pourront tre traites en LIFO (comme
une pile) ou en FIFO (comme une file) mais il sera galement possible dinsrer un
lment au beau milieu de la liste. Pour cela, il devra tre possible de parcourir la
liste sans la dmonter comme nous le faisions avec les piles ou les files.
Encore un nouveau package ? Je commence me lasser, moi.

308

LES LISTES CHANES

Le package Ada.Containers.Doubly_Linked_Lists
Mise en place
Commenons par voquer le type List. Il est accesible via le package Ada.Containers.
Doubly_Linked_Lists. Pour lire les spcifications de ce package, il vous suffit douvrir
le fichier appel a-cdlili.ads situ dans le rpertoire dinstallation de GNAT. Vous trouverez normalement ce rpertoire ladresse C :\GNAT sous Windows. Il faudra ensuite
ouvrir les dossiers 2011\lib\gcc\i686-pc-mingw32\4.5.3\adainclude (ou quelque chose
de ce style, les dnominations pouvant varier dune version lautre).
Comme le package Ada.Numerics.Discrete_Random qui nous servait gnrer des
nombres alatoires, le package Ada.Containers.Doubly_Linked_Lists est un package
gnrique (nous verrons bientt la gnricit, patience), fait pour nimporte quel type
de donne et nous ne pouvons donc pas lutiliser en ltat. Nous devons tout dabord
linstancier, cest--dire crer un sous-package prvu pour le type de donnes dsir.
Exemple en image :
1
2
3
4
5
6

with Ada . Containers . Doubly_Linked_Lists ;


...
type T_Score is record
name : string ( 1 .. 3 ) := "
";
value : natural := 0 ;
end record ;

7
8
9

package P_Lists is new Ada . Containers . Doubly_Linked_Lists (


T_Score ) ;
use P_Lists ;

10
11
12

L : List ;
C : Cursor ;

Primitives
Nous avons ainsi cr un package P_Lists nous permettant dutiliser des listes chanes
contenant des lments de type T_Score. Une liste chane est de type List (sans S
la fin !). Nous disposons des primitives suivantes pour comparer deux listes, connatre
la longueur dune liste, savoir si elle est vide, la vider, ajouter un lment au dbut
(Prepend) ou lajouter la fin (Append) :
1
2
3
4

function " = " ( Left , Right : List ) return Boolean ;


comparaison de deux listes
function Length ( Container : List ) return Count_Type ;
longueur de la liste
function Is_Empty ( Container : List ) return Boolean ;
la liste est - elle vide ?
procedure Clear ( Container : in out List ) ;
vide la liste

-----

309

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
5
6
7
8
9
10
11
12

procedure Prepend
ajoute un l ment en d but de liste
( Container : in out List ;
New_Item : Element_Type ;
Count
: Count_Type := 1 ) ;
procedure Append
ajoute un l ment en fin de liste
( Container : in out List ;
New_Item : Element_Type ;
Count
: Count_Type := 1 ) ;

--

--

De mme, il est possible de connatre le premier ou le dernier lment (sans le dlister ), de le supprimer, de savoir si la liste contient un certain lment ou pas ou encore
dinverser la liste laide des primitives suivante :
1
2

function First_Element ( Container : List ) return Element_Type ;


-- renvoie le premier l ment
function Last_Element ( Container : List ) return Element_Type ;
-- renvoie le dernier l ment

3
4
5
6
7
8
9

procedure Delete_First
-- supprime le premier l ment
( Container : in out List ;
Count
: Count_Type := 1 ) ;
procedure Delete_Last
-- supprime le dernier l ment
( Container : in out List ;
Count
: Count_Type := 1 ) ;

10
11
12
13

function Contains
-- indique si la liste contient cet l ment
( Container : List ;
Item
: Element_Type ) return Boolean ;

14
15

procedure Reverse_Elements ( Container : in out List ) ;


-- renverse la liste

Bon daccord, L est une liste chane, mais que vient faire C ici ? Cest quoi
ce cursor ?
Ce curseur est l pour nous situer sur la liste, pour pointer un lment. Nous ne pouvons
travailler seulement avec le premier et le dernier lment. Supposons que nous ayons
une liste comme en figure 20.9.
Le curseur indique le second lment de la liste. Ds lors, nous pouvons lire cet lment,
le modifier, insrer un nouvel lment juste avant lui, le supprimer ou bien lchanger
avec un lment point par un second curseur avec les primitives suivantes :
310

LES LISTES CHANES

Figure 20.9 Liste de scores et curseur


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

function Element ( Position : Cursor ) return Element_Type ;


renvoie l ' l ment point
procedure Replace_Element
modifie l ' l ment point
( Container : in out List ;
Position : Cursor ;
New_Item : Element_Type ) ;
procedure Insert
re un l ment devant l ' l ment point
( Container : in out List ;
Before
: Cursor ;
New_Item : Element_Type ;
Count
: Count_Type := 1 ) ;
procedure Delete
supprime l ' l ment point
( Container : in out List ;
Position : in out Cursor ;
Count
: Count_Type := 1 ) ;
procedure Swap
change deux l ments point s
( Container : in out List ;
I, J
: Cursor ) ;

---

-- ins

--

--

Ce curseur peut tre plac au dbut, la fin de la liste ou tre dplac lment aprs
lment.
1
2

function First ( Container : List ) return Cursor ;


curseur au d but
function Last ( Container : List ) return Cursor ;
curseur la fin

-- place le

function Next ( Position : Cursor ) return Cursor ;


le curseur sur l ' l ment suivant
procedure Next ( Position : in out Cursor ) ;

--d place

-- place le

3
4
5
6
7
8

function Previous ( Position : Cursor ) return Cursor ; --d place


le curseur sur l ' l ment pr c dent
procedure Previous ( Position : in out Cursor ) ;

311

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
Enfin, il est possible dappliquer une procdure tous les lments dune liste en utilisant un pointeur sur procdure avec :
procedure Iterate ( Container : List ; Process
procedure ( Position : Cursor ) ) ;

: not null access

Une application
Nous allons crer un programme crant une liste de 5 scores et les affichant. Je vais
vous proposer deux procdures daffichage distinctes. Mais tout dabord, crons notre
liste :
Append (L ,( " mac " , 500 ) ) ;
Append (L ,( " mic " ,0 ) ) ;
Append (L ,( " bob " , 1800 ) ) ;
Append (L ,( " joe " , 5300 ) ) ;
Append (L ,( " mac " , 800 ) ) ;

1
2
3
4
5

Vous avez remarqu, jai cr la mme liste que tout lheure, avec la procdure
Append. Une autre faon de procder, mais avec la procdure Prepend donnerait cela :
Prepend (L ,( " mac " , 800 ) ) ;
Prepend (L ,( " joe " , 5300 ) ) ;
Prepend (L ,( " bob " , 1800 ) ) ;
Prepend (L ,( " mic " ,0 ) ) ;
Prepend (L ,( " mac " , 500 ) ) ;

1
2
3
4
5

Une dernire mthode (un peu tire par les cheveux), avec le curseur donnerait :
1
2
3
4
5
6
7

Append (L ,( " joe " , 5300 ) ) ;


C := first ( L ) ;
insert (L ,C ,( " mac " , 500 ) ) ;
insert (L ,( " bob " , 1800 ) ) ;
previous ( C ) ;
insert (L ,( " mic " ,0 ) ) ;
Append ( " mac " , 500 ) ;

Les appels Append(L,("mac",500)) peuvent galement


L.Append(("mac",500)). Nous verrons bientt pourquoi.

scrire

Enfin, voici une premire faon, laborieuse dafficher les lments de la liste :
1
2
3
4
5
6

procedure put ( score : T_SCore ) is


begin
Score := Element ( C ) ;
put_line ( Score . name & " a obtenu " & integer ' image ( Score .
value ) & " points . " ) ;
end put ;
...

312

LES LISTES CHANES


7
8
9
10
11
12
13
14

begin
C := first ( L ) ;
for i in 1 .. length ( L ) -1 loop
put ( Element ( c ) ) ;
next ( c ) ;
end loop ;
put ( Element ( c ) ) ;
end ;

Cette premire faon utilise le curseur et passe en revue chacun des lments les uns
aprs les autres. Attention toutefois au cas du dernier lment qui na pas de successeur !
noter galement quune procdure rcursive aurait aussi bien pu faire laffaire. Enfin,
voici une seconde faon de procder utilisant les pointeurs sur procdure cette fois :
1
2
3
4
5
6
7
8
9
10

procedure put ( C : Cursor ) is


score : T_SCore ;
begin
Score := Element ( C ) ;
put_line ( Score . name & " a obtenu " & integer ' image ( Score .
value ) & " points . " ) ;
end put ;
...
begin
iterate (L , Put ' access ) ;
end ;

En utilisant la procdure iterate(), nous nous pargnons ici un temps considrable. Cela
justifie amplement les efforts que vous avez fourni pour comprendre ce qutaient les
pointeurs sur procdure.
Lappel
iterate(L,Putaccess)
peut
tre
remplac
par
L.iterate(putaccess) pour plus de lisibilit ! Encore une fois, une
explication sera fournie trs prochainement dans la partie IV.

Le package Ada.Containers.Vectors
Mise en place
voquons maintenant le type vector. Il est accesible via le package Ada.Containers.Vectors.
Pour lire les spcifications de ce package, il vous suffit douvrir le fichier appel aconvec.ads. Encore une fois, ce package doit tre instanci de la manire suivante :
1
2
3
4

with Ada . Containers . Vectors ;


...
package P_Vectors is new Ada . Containers . Vectors ( Positive ,
T_Score ) ;
use P_Vectors ;

313

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
6

V : Vector ;

Nous crons ainsi un package P_Vectors index laide du type Positive (nombre
entiers strictement positifs) et contenant des lments de type T_Score. Nous aurions
pu utiliser le type Natural pour lindexation, mais le type Positive nous garantit que
le premier lment du Vector V sera le numro 1 et non le numro 0.
Il est possible dutiliser galement un curseur avec les Vectors, mais ce nest
pas la solution la plus vidente : mieux vaut nous servir des indices.

Primitives
Les primitives disponibles avec les Doubly_Linked_Lists sont galement disponibles
pour les Vectors :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

314

overriding function " = " ( Left , Right : Vector ) return Boolean ;


-- test d ' galit entre deux vectors
function Length ( Container : Vector ) return Count_Type ;
-- renvoie la longueur du vector
function Is_Empty ( Container : Vector ) return Boolean ;
-- renvoie true si le vector est vide
procedure Clear ( Container : in out Vector ) ;
-- vide le vector
procedure Prepend
-- ajoute un l ment au d but du vector
( Container : in out Vector ;
New_Item : Vector ) ;
procedure Append
-- ajoute un l ment la fin du vector
( Container : in out Vector ;
New_Item : Vector ) ;
function First_Element ( Container : Vector ) return Element_Type
; -- renvoie la valeur du premier l ment du vector
function Last_Element ( Container : Vector ) return Element_Type ;
-- renvoie la valeur du premier l ment du vector
procedure Delete_First
-- supprimer le premier l ment du vector
( Container : in out Vector ;
Count
: Count_Type := 1 ) ;
procedure Delete_Last
-- supprime le dernier l ment du vector
( Container : in out Vector ;
Count
: Count_Type := 1 ) ;
function Contains
-- indique si un l ment est pr sent dans le vector
( Container : Vector ;
Item
: Element_Type ) return Boolean ;

LES LISTES CHANES


22

procedure Reverse_Elements ( Container : in out Vector ) ;


-- renverse l ' ordre des l ments du vector

Dautres primitives font appel un curseur, mais je ny ferai pas rfrence (vous pouvez
les dcouvrir par vous mme). Mais elles ont en gnral leur quivalent, sans emploi du
curseur mais avec un indice.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

function Element
-- renvoie la
valeur de l ' l ment situ l ' indice sp cifi
( Container : Vector ;
Index
: Index_Type ) return Element_Type ;
procedure Replace_Element
-- supprime l
' l ment situ l ' indice sp cifi
( Container : in out Vector ;
Index
: Index_Type ;
New_Item : Element_Type ) ;
procedure Insert
-- ins re un
nouvel l ment avant l ' indice sp cifi
( Container : in out Vector ;
Before
: Extended_Index ;
New_Item : Element_Type ;
Count
: Count_Type := 1 ) ;
procedure Delete
-- supprime l
' l ment situ l ' indice sp cifi
( Container : in out Vector ;
Index
: Extended_Index ;
Count
: Count_Type := 1 ) ;
procedure Swap ( Container : in out Vector ; I , J : Index_Type ) ;
-- change deux l ments
function First_Index ( Container : Vector ) return Index_Type ;
-- renvoie l ' indice du premier l ment
function Last_Index ( Container : Vector ) return Extended_Index ;
-- renvoie l ' indice du dernier l ment
procedure Iterate
-- applique
la proc dure Process tous les l ments du Vector
( Container : Vector ;
Process
: not null access procedure ( Position : Cursor )
);

ces primitives similaires celles des listes doublement chanes, il faut encore en
ajouter quelques autres :
1
2
3
4
5

-- Fonctions de concat nation


function " & " ( Left , Right : Vector ) return Vector ;
function " & " ( Left : Vector ; Right : Element_Type ) return
Vector ;
function " & " ( Left : Element_Type ; Right : Vector ) return
Vector ;
function " & " ( Left , Right : Element_Type ) return Vector ;

315

CHAPITRE 20. LES TYPES ABSTRAITS DE DONNES : LISTES, FILES,


PILES. . .
7
8
9
10
11
12
13
14
15
16
17

procedure Set_Length
-- cr e un vecteur de longueur
donn e
( Container : in out Vector ;
Length
: Count_Type ) ;
procedure Insert_Space
-- ins re un l ment vide
avant l ' indice sp cifi
( Container : in out Vector ;
Before
: Extended_Index ;
Count
: Count_Type := 1 ) ;
function Find_Index
-- trouve l ' indice d ' un l
ment dans un tableau
( Container : Vector ;
Item
: Element_Type ;
Index
: Index_Type := Index_Type ' First ) return
Extended_Index ;

Bien sr, je ne vous ai pas dress la liste de toutes les fonctions et procdures disponibles, mais vous avez ainsi les principales. savoir : le type Element_type correspond
au type avec lequel vous aurez instanci votre package. De plus, le type Count est une
sorte de type integer.
Nous en avons fini avec ce cours semi-thorique, semi-pratique. Nous navons bien sr
pas fait le tour de tous les TAD, mais il ne vous est pas interdit (bien au contraire)
de dvelopper vos propres types comme des arbres ou des cycles. Cela constitue un
excellent exercice dentranement lalgorithmique et la programmation qui vous
poussera manipuler les pointeurs et user de programmes rcursifs.

En rsum
Les TAD ont lavantage de fournir des conteneurs de taille inconnue et donc
non contraints, contrairement aux tableaux. Leur emploi est certes moins vident
puisquil faut les crer vous-mme ou faire appel un package gnrique, mais
ils rsoudront bon nombre de problmes. Pensez lhistorique de votre navigateur internet, la liste des items dans un jeu vido ou plus simplement aux
Unbounded_Strings.
Si les TAD peuvent trs bien tre traits par une boucle itrative (LOOP, FOR
ou WHILE), leur structure chane et leur longueur potentiellement trs grande en
font des sujets de choix pour les programmes rcursifs. Et le besoin de rcursivit
ne cessera daugmenter avec la complexit de la structure : imaginez seulement
la difficult de traiter un arbre avec des boucles itratives.
Noubliez pas que ces structures ne sont rendues possibles que par lusage intensif
des pointeurs. Si les packages Ada.Container ont t cods avec srieux, cela ne
doit pas vous en faire oublier les risques : vitez, par exemple, daccder un
lment dune liste vide ou de lui retirer un lment !

316

Chapitre

21

[TP] Le jeu du serpent


Difficult :
La troisime partie se termine (Enfin ! diront certains) et nous allons donc la conclure
comme il se doit par un troisime TP ! Aprs avoir hsit entre la conception de divers
programmes, tel un diteur de carte ou un diteur de texte, je me suis dit quaprs le difficile
TP prcdent et cette longue partie III, vous aviez bien mrit un TP moins dcourageant
et plus ludique. Cest pourquoi, aprs de longues rflexions, jai opt pour un jeu : le jeu du
Serpent. Vous savez ce vieux jeu que lon avait sur les vieux portables o un serpent devait
manger sans cesse de gros pixels afin de grandir en prenant garde de ne jamais dvorer sa
propre queue ou sortir de lcran.

317

CHAPITRE 21. [TP] LE JEU DU SERPENT


Comme dhabitude, je vais vous guider dans sa mise en uvre, et comme dhabitude
tout se fera en console mais. . . avec des couleurs ! Prts ? Jai trouv une image pour
vous mettre en bouche (figure 21.1) !

Figure 21.1 Jeu du serpent

Cahier des charges


Fonctionnalits
Comme toujours, avant de nous lancer laveuglette, nous devons dfinir les fonctionnalits de notre programme, les limites du projet. Il nest pas question de crer un jeu
en 3D ou avec des boutons, des images, une base de donnes. . . nous allons faire plus
sobre.
Notre programme devra :
sexcuter en console, je vous lai dit. Toutefois, nous ferons en sorte dafficher
quelques couleurs pour gayer tout a et surtout rendre notre programme facilement jouable. Rassurez-vous, je vous fournirais un package pour cela ;
se jouer en temps rel, pas au tour par tour ! Si le joueur ne touche pas au clavier
alors son serpent ira droit dans le mur ;
grer et donc viter les bogues : serpent faisant demi-tour sur lui-mme, sortant
de la carte ou dont le corps ne suivrait pas la tte dans certaines configurations
(je ne rigole pas, vous commettrez srement ce genre derreur) ;
permettre au joueur de diriger le serpent au clavier laide des touches flches.
318

UN PACKAGE BIEN UTILE

Organisation des types et variables


Nous ne savons pas quand le joueur perdra, notre serpent devra donc pouvoir sallonger
indfiniment (en thorie). Il serait donc judicieux dutiliser les TAD vus prcdemment
pour enregitrer les diffrents anneaux de notre reptile : il sera plus ais dagrandir notre
serpent laide des primitives append ou prepend quen dclarant des tableaux ou je
ne sais quoi dautre. De mme, dplacer le serpent reviendra simplement retirer le
dernier anneau de son corps pour le placer en premier dans une nouvelle position. Vous
avez le choix entre tous les TAD que vous voulez, mais les Doubly_Linked_Lists ou
les Vectors sont les mieux adapts et comme jai une prfrence pour le type Vector,
la solution que je vous fournirai utilise donc. . . les Doubly_Linked_Lists (cherchez la
cohrence ).
Mais que contiendra notre liste ? Elle contiendra toute une suite de coordonnes : les
coordonnes des diffrentes parties du corps du serpent. Mais ce nest pas tout ! Le
serpent ne peut se limiter une liste de coordonnes, il faudra galement que notre
type T_Serpent contienne la direction de la tte du serpent.
Enfin, la taille de laire de jeu, la vitesse de dplacement du serpent ou les couleurs utilises devraient tre enregistres dans des variables (ou des constantes si besoin) toutes
inventories dans un package. En faisant cela, ces variables deviendront des variables
globales, ce qui est gnralement risqu mais clarifiera notre code et simplifiera toute
modification des paramtres du jeu. Nous reviendrons sur les variables globales dans
la prochaine partie.

Un package bien utile


Le package NT_Console
Depuis le dbut je vous dis que notre programme sera en couleur, donc il est temps de
vous fournir le package ncessaire pour utiliser des couleurs dans la console. Il ne sagit
ni dun package officiel, ni dun package de mon cr (mea maxima culpa ) mais dun
vieux package mis notre disposition par Jerry van Dijk et libre de diffusion, appel
NT_Console :
1

2
3
4
5
6
7
8
9
10
11

------------

--------------------------------------------------------------------File :
Description :
Rev :
Date :
Author :
Mail :

nt_console . adb
Win95 / NT console support
0.3
08 - june - 1999
Jerry van Dijk
jdijk@acm . org

Copyright ( c ) Jerry van Dijk , 1997 , 1998 , 1999


Billie Holidaystraat 28

319

CHAPITRE 21. [TP] LE JEU DU SERPENT


12
13
14
15
16

------

19

----

20

--

21

--

17
18

22
23

---

2324 LK LEIDEN
THE NETHERLANDS
tel int + 31 71 531 43 65
Permission granted to use for any purpose , provided this
copyright
remains attached and unmodified .
THIS SOFTWARE IS PROVIDED `` AS IS ' ' AND WITHOUT ANY EXPRESS
OR
IMPLIED WARRANTIES , INCLUDING , WITHOUT LIMITATION , THE
IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE .

--------------------------------------------------------------------

24

pragma C_Pass_By_Copy ( 128 ) ;

25
26
27

with Interfaces ; use Interfaces ;

28
29

package body NT_Console is

30
31

pragma Linker_Options ( " - luser32 " ) ;

32
33
34
35

-- - - - - - - - - - - - - - - - - - - -- WIN32 INTERFACE --- - - - - - - - - - - - - - - - - - - -

36
37
38
39
40
41
42
43
44
45
46

Beep_Error
Fill_Char_Error
Cursor_Get_Error
Cursor_Set_Error
Cursor_Pos_Error
Buff er_Info_Error
S et _ At tribute_Error
I n v a l i d_Handle_Error
F i l l _ A ttribute_Error
C u r s o r _Po sition _Erro r

:
:
:
:
:
:
:
:
:
:

exception ;
exception ;
exception ;
exception ;
exception ;
exception ;
exception ;
exception ;
exception ;
exception ;

47
48
49
50
51
52

subtype
subtype
subtype
subtype
subtype

DWORD is
HANDLE is
WORD
is
SHORT is
WINBOOL is

Unsigned_32 ;
Unsigned_32 ;
Unsigned_16 ;
Short_Integer ;
Integer ;

53
54
55

320

type LPDWORD is access all DWORD ;


pragma Convention (C , LPDWORD ) ;

UN PACKAGE BIEN UTILE


56
57
58

type Nibble is mod 2 ** 4 ;


for Nibble ' Size use 4 ;

59
60
61
62
63
64
65

type Attribute is
record
Foreground : Nibble ;
Background : Nibble ;
Reserved
: Unsigned_8 := 0 ;
end record ;

66
67
68
69
70
71
72

for Attribute use


record
Foreground at 0 range 0 .. 3 ;
Background at 0 range 4 .. 7 ;
Reserved
at 1 range 0 .. 7 ;
end record ;

73
74
75

for Attribute ' Size use 16 ;


pragma Convention (C , Attribute ) ;

76
77
78
79
80
81
82

type COORD is
record
X : SHORT ;
Y : SHORT ;
end record ;
pragma Convention (C , COORD ) ;

83
84
85
86
87
88
89
90
91

type SMALL_RECT is
record
Left
: SHORT ;
Top
: SHORT ;
Right : SHORT ;
Bottom : SHORT ;
end record ;
pragma Convention (C , SMALL_RECT ) ;

92
93
94
95
96
97
98
99
100
101

type C O N S O L E _ S C R E E N _ B U F F E R _ I N F O is
record
Size
: COORD ;
Cursor_Pos : COORD ;
Attrib
: Attribute ;
Window
: SMALL_RECT ;
Max_Size
: COORD ;
end record ;
pragma Convention (C , C O N S O L E _ S C R E E N _ B U F F E R _ I N F O ) ;

102
103
104

type P C O N S O L E _ S C R E E N _ B U F F E R _ I N F O is access all


CONSOLE_SCREEN_BUFFER_INFO ;
pragma Convention (C , P C O N S O L E _ S C R E E N _ B U F F E R _ I N F O ) ;

321

CHAPITRE 21. [TP] LE JEU DU SERPENT


105
106
107
108
109
110
111

type C ONSOLE_CURSOR_INFO is
record
Size
: DWORD ;
Visible : WINBOOL ;
end record ;
pragma Convention (C , CONSOLE_CURSOR_INFO ) ;

112
113
114

type P CONSOLE_CURSOR_INFO is access all CONSOLE_CURSOR_INFO ;


pragma Convention (C , PCONSOLE_CURSOR_INFO ) ;

115
116
117

function GetCh return Integer ;


pragma Import (C , GetCh , " _getch " ) ;

118
119
120

function KbHit return Integer ;


pragma Import (C , KbHit , " _kbhit " ) ;

121
122
123

function MessageBeep ( Kind : DWORD ) return DWORD ;


pragma Import ( StdCall , MessageBeep , " MessageBeep " ) ;

124
125
126

function GetStdHandle ( Value : DWORD ) return HANDLE ;


pragma Import ( StdCall , GetStdHandle , " GetStdHandle " ) ;

127
128
129

function GetConsoleCursorInfo ( Buffer : HANDLE ; Cursor :


P C O N S O LE_CURSOR_INFO ) return WINBOOL ;
pragma Import ( StdCall , GetConsoleCursorInfo , "
G e t C o n soleCursorInfo " ) ;

130
131
132

function SetConsoleCursorInfo ( Buffer : HANDLE ; Cursor :


P C O N S O LE_CURSOR_INFO ) return WINBOOL ;
pragma Import ( StdCall , SetConsoleCursorInfo , "
S e t C o n soleCursorInfo " ) ;

133
134
135

function S et C o ns o l eC u r so r P os i t io n ( Buffer : HANDLE ; Pos :


COORD ) return DWORD ;
pragma Import ( StdCall , SetConsoleCursorPosition , "
S e t C o n s o l eC u r so r P o si t i on " ) ;

136
137
138

function Se tC o ns ol e Te xt At t ri bu te ( Buffer : HANDLE ; Attr :


Attribute ) return DWORD ;
pragma Import ( StdCall , SetConsoleTextAttribute , "
S e t C o n s o le Te x tA tt ri b ut e " ) ;

139
140
141

function G e t C o n s o l e S c r e e n B u f f e r I n f o ( Buffer : HANDLE ; Info :


P C O N S O L E _ S C R E E N _ B U F F E R _ I N F O ) return DWORD ;
pragma Import ( StdCall , GetConsoleScreenBufferInfo , "
GetConsoleScreenBufferInfo ");

142
143

322

function F i l l C o n s o l e O u t p u t C h a r a c t e r ( Console : HANDLE ; Char


: Character ; Length : DWORD ; Start : COORD ; Written :

UN PACKAGE BIEN UTILE

144

LPDWORD ) return DWORD ;


pragma Import ( Stdcall , FillConsoleOutputCharacter , "
FillConsoleOutputCharacterA ");

145
146

147

function F i l l C o n s o l e O u t p u t A t t r i b u t e ( Console : Handle ; Attr


: Attribute ; Length : DWORD ; Start : COORD ; Written :
LPDWORD ) return DWORD ;
pragma Import ( Stdcall , FillConsoleOutputAttribute , "
FillConsoleOutputAttribute ");

148
149
150
151

WIN32_ERROR
: constant DWORD := 0 ;
I N V A L I D _ H A NDLE_VALUE : constant HANDLE := -1 ;
STD_O UTPUT_HANDLE
: constant DWORD := - 11 ;

152
153
154
155
156
157

Color_Value
: constant array ( Color_Type ) of Nibble :=
(0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ) ;
Color_Type_Value : constant array ( Nibble ) of Color_Type :=
( Black , Blue , Green , Cyan , Red , Magenta , Brown , Gray ,
Black , Light_Blue , Light_Green , Light_Cyan , Light_Red ,
Light_Magenta , Yellow , White ) ;

158
159
160
161

-- - - - - - - - - - - - - - - - - - - - - -- PACKAGE VARIABLES --- - - - - - - - - - - - - - - - - - - - - -

162
163
164
165
166
167

Output_Buffer
: HANDLE ;
Num_Bytes
: aliased DWORD ;
Num_Bytes_Access : LPDWORD := Num_Bytes ' Access ;
Buffer_Info_Rec : aliased C O N S O L E _ S C R E E N _ B U F F E R _ I N F O ;
Buffer_Info
: P C O N S O L E _ S C R E E N _ B U F F E R _ I N F O :=
Buffer_Info_Rec ' Access ;

168
169
170
171

-- - - - - - - - - - - - - - - - - - - - - - - -- SUPPORTING SERVICES --- - - - - - - - - - - - - - - - - - - - - - - -

172
173
174
175
176
177
178

procedure Get_Buffer_Info is
begin
if G e t C o n s o l e S c r e e n B u f f e r I n f o ( Output_Buffer , Buffer_Info
) = WIN32_ERROR then
raise Buffer_Info_Error ;
end if ;
end Get_Buffer_Info ;

179
180
181
182

-- - - - - - - - - - - - - - - - - - -- CURSOR CONTROL --- - - - - - - - - - - - - - - - - - -

183
184
185

function Cursor_Visible return Boolean is


Cursor : aliased CONSOLE_CURSOR_INFO ;

323

CHAPITRE 21. [TP] LE JEU DU SERPENT


186
187
188
189
190
191

begin
if GetConsoleCursorInfo ( Output_Buffer , Cursor '
Unchecked_Access ) = 0 then
raise Cursor_Get_Error ;
end if ;
return Cursor . Visible = 1 ;
end Cursor_Visible ;

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

procedure Set_Cursor ( Visible : in Boolean ) is


Cursor : aliased CONSOLE_CURSOR_INFO ;
begin
if GetConsoleCursorInfo ( Output_Buffer , Cursor '
Unchecked_Access ) = 0 then
raise Cursor_Get_Error ;
end if ;
if Visible = True then
Cursor . Visible := 1 ;
else
Cursor . Visible := 0 ;
end if ;
if SetConsoleCursorInfo ( Output_Buffer , Cursor '
Unchecked_Access ) = 0 then
raise Cursor_Set_Error ;
end if ;
end Set_Cursor ;

208
209
210
211
212
213

function Where_X return X_Pos is


begin
Get_Buffer_Info ;
return X_Pos ( Buffer_Info_Rec . Cursor_Pos . X ) ;
end Where_X ;

214
215
216
217
218
219

function Where_Y return Y_Pos is


begin
Get_Buffer_Info ;
return Y_Pos ( Buffer_Info_Rec . Cursor_Pos . Y ) ;
end Where_Y ;

220
221
222
223
224
225
226
227
228
229
230
231
232

324

procedure Goto_XY
( X : in X_Pos := X_Pos ' First ;
Y : in Y_Pos := Y_Pos ' First ) is
New_Pos : COORD := ( SHORT ( X ) , SHORT ( Y ) ) ;
begin
Get_Buffer_Info ;
if New_Pos . X > Buffer_Info_Rec . Size . X then
New_Pos . X := Buffer_Info_Rec . Size . X ;
end if ;
if New_Pos . Y > Buffer_Info_Rec . Size . Y then
New_Pos . Y := Buffer_Info_Rec . Size . Y ;
end if ;

UN PACKAGE BIEN UTILE


233
234
235
236

if S e t C on s o le C u rs o r Po s i ti o n ( Output_Buffer , New_Pos ) =
WIN32_ERROR then
raise Cursor_Pos_Error ;
end if ;
end Goto_XY ;

237
238
239
240

-- - - - - - - - - - - - - - - - - -- COLOR CONTROL --- - - - - - - - - - - - - - - - - -

241
242
243
244
245
246

function Get_Foreground return Color_Type is


begin
Get_Buffer_Info ;
return Color_Type_Value ( Buffer_Info_Rec . Attrib .
Foreground ) ;
end Get_Foreground ;

247
248
249
250
251
252

function Get_Background return Color_Type is


begin
Get_Buffer_Info ;
return Color_Type_Value ( Buffer_Info_Rec . Attrib .
Background ) ;
end Get_Background ;

253
254
255
256
257
258
259
260
261
262
263

procedure Set_Foreground ( Color : in Color_Type := Gray ) is


Attr : Attribute ;
begin
Get_Buffer_Info ;
Attr . Foreground := Color_Value ( Color ) ;
Attr . Background := Buffer_Info_Rec . Attrib . Background ;
if S e t C on so l eT ex tA t tr ib ut e ( Output_Buffer , Attr ) =
WIN32_ERROR then
raise Set_Attribute_Error ;
end if ;
end Set_Foreground ;

264
265
266
267
268
269
270
271
272
273
274

procedure Set_Background ( Color : in Color_Type := Black ) is


Attr : Attribute ;
begin
Get_Buffer_Info ;
Attr . Foreground := Buffer_Info_Rec . Attrib . Foreground ;
Attr . Background := Color_Value ( Color ) ;
if S e t C on so l eT ex tA t tr ib ut e ( Output_Buffer , Attr ) =
WIN32_ERROR then
raise Set_Attribute_Error ;
end if ;
end Set_Background ;

275
276
277

-- - - - - - - - - - - - - - - - - - -- SCREEN CONTROL --

325

CHAPITRE 21. [TP] LE JEU DU SERPENT


278

-- - - - - - - - - - - - - - - - - - -

279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

procedure Clear_Screen ( Color : in Color_Type := Black ) is


Length : DWORD ;
Attr
: Attribute ;
Home
: COORD := (0 , 0 ) ;
begin
Get_Buffer_Info ;
Length := DWORD ( Buffer_Info_Rec . Size . X ) * DWORD (
Buffer_Info_Rec . Size . Y ) ;
Attr . Background := Color_Value ( Color ) ;
Attr . Foreground := Buffer_Info_Rec . Attrib . Foreground ;
if S e tC on so l eT ex tA t tr ib u te ( Output_Buffer , Attr ) =
WIN32_ERROR then
raise Set_Attribute_Error ;
end if ;
if F i l l C o n s o l e O u t p u t A t t r i b u t e ( Output_Buffer , Attr ,
Length , Home , Num_Bytes_Access ) = WIN32_ERROR then
raise Fill_Attribute_Error ;
end if ;
if F i l l C o n s o l e O u t p u t C h a r a c t e r ( Output_Buffer , ' ' , Length
, Home , Num_Bytes_Access ) = WIN32_ERROR then
raise Fill_Char_Error ;
end if ;
if Se t C on s o le C u rs o r Po s i ti o n ( Output_Buffer , Home ) =
WIN32_ERROR then
raise Cu rsor_ Positi on_Er ror ;
end if ;
end Clear_Screen ;

302
303
304
305
306
307
308
309
310
311

-- - - - - - - - - - - - - - - - - -- SOUND CONTROL --- - - - - - - - - - - - - - - - - procedure Bleep is


begin
if MessageBeep ( 16 # FFFFFFFF #) = WIN32_ERROR then
raise Beep_Error ;
end if ;
end Bleep ;

312
313
314
315

-- - - - - - - - - - - - - - - - - -- INPUT CONTROL --- - - - - - - - - - - - - - - - - -

316
317
318
319
320
321
322

326

function Get_Key return Character is


Temp : Integer ;
begin
Temp := GetCh ;
if Temp = 16 # 00E0 # then
Temp := 0 ;

UN PACKAGE BIEN UTILE


end if ;
return Character ' Val ( Temp ) ;
end Get_Key ;

323
324
325
326

function Key_Available return Boolean is


begin
if KbHit = 0 then
return False ;
else
return True ;
end if ;
end Key_Available ;

327
328
329
330
331
332
333
334
335
336

begin

337

-- - - - - - - - - - - - - - - - - - - - - - - - -- WIN32 INITIALIZATION --- - - - - - - - - - - - - - - - - - - - - - - - -

338
339
340
341

Output_Buffer := GetStdHandle ( STD_OUTPUT_HANDLE ) ;


if Output_Buffer = INVALID_HANDLE_VALUE then
raise Invalid_Handle_Error ;
end if ;

342
343
344
345
346
347

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

end NT_Console ;

--------------------

--------------------------------------------------------------------File :
Description :
Rev :
Date :
Author :
Mail :

nt_console . ads
Win95 / NT console support
0.2
08 - june - 1999
Jerry van Dijk
jdijk@acm . org

Copyright ( c ) Jerry van Dijk , 1997 , 1998 , 1999


Billie Holidaystraat 28
2324 LK LEIDEN
THE NETHERLANDS
tel int + 31 71 531 43 65
Permission granted to use for any purpose , provided this
copyright
remains attached and unmodified .
THIS SOFTWARE IS PROVIDED `` AS IS ' ' AND WITHOUT ANY EXPRESS
OR

327

CHAPITRE 21. [TP] LE JEU DU SERPENT


20

--

21

--

22
23

---

IMPLIED WARRANTIES , INCLUDING , WITHOUT LIMITATION , THE


IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE .

--------------------------------------------------------------------

24
25

package NT_Console is

26
27
28
29

-- - - - - - - - - - - - - - - - - - - - -- TYPE DEFINITIONS --- - - - - - - - - - - - - - - - - - - - -

30
31
32

subtype X_Pos is Natural range 0 .. 79 ;


subtype Y_Pos is Natural range 0 .. 24 ;

33
34
35
36
37

type Color_Type is
( Black , Blue , Green , Cyan , Red , Magenta , Brown , Gray ,
Light_Blue , Light_Green , Light_Cyan , Light_Red ,
Light_Magenta , Yellow , White ) ;

38
39
40
41

-- - - - - - - - - - - - - - - - - - - - -- EXTENDED PC KEYS --- - - - - - - - - - - - - - - - - - - - -

42
43
44
45
46
47
48
49
50
51
52
53

328

Key_Alt_Escape
16 # 01 #) ;
Key_Control_At
16 # 03 #) ;
Key_ Alt_Backspace
16 # 0E #) ;
Key_BackTab
16 # 0F #) ;
Key_Alt_Q
16 # 10 #) ;
Key_Alt_W
16 # 11 #) ;
Key_Alt_E
16 # 12 #) ;
Key_Alt_R
16 # 13 #) ;
Key_Alt_T
16 # 14 #) ;
Key_Alt_Y
16 # 15 #) ;
Key_Alt_U
16 # 16 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

UN PACKAGE BIEN UTILE


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

Key_Alt_I
16 # 17 #) ;
Key_Alt_O
16 # 18 #) ;
Key_Alt_P
16 # 19 #) ;
Key_Alt_LBracket
16 # 1A #) ;
Key_Alt_RBracket
16 # 1B #) ;
Key_Alt_Return
16 # 1C #) ;
Key_Alt_A
16 # 1E #) ;
Key_Alt_S
16 # 1F #) ;
Key_Alt_D
16 # 20 #) ;
Key_Alt_F
16 # 21 #) ;
Key_Alt_G
16 # 22 #) ;
Key_Alt_H
16 # 23 #) ;
Key_Alt_J
16 # 24 #) ;
Key_Alt_K
16 # 25 #) ;
Key_Alt_L
16 # 26 #) ;
Key_A lt_Semicolon
16 # 27 #) ;
Key_Alt_Quote
16 # 28 #) ;
Key_A lt_Backquote
16 # 29 #) ;
Key_A lt_Backslash
16 # 2B #) ;
Key_Alt_Z
16 # 2C #) ;
Key_Alt_X
16 # 2D #) ;
Key_Alt_C
16 # 2E #) ;
Key_Alt_V
16 # 2F #) ;
Key_Alt_B
16 # 30 #) ;
Key_Alt_N
16 # 31 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

329

CHAPITRE 21. [TP] LE JEU DU SERPENT


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

330

Key_Alt_M
16 # 32 #) ;
Key_Alt_Comma
16 # 33 #) ;
Key_Alt_Period
16 # 34 #) ;
Key_Alt_Slash
16 # 35 #) ;
Key_Alt_KPStar
16 # 37 #) ;
Key_F1
16 # 3B #) ;
Key_F2
16 # 3C #) ;
Key_F3
16 # 3D #) ;
Key_F4
16 # 3E #) ;
Key_F5
16 # 3F #) ;
Key_F6
16 # 40 #) ;
Key_F7
16 # 41 #) ;
Key_F8
16 # 42 #) ;
Key_F9
16 # 43 #) ;
Key_F10
16 # 44 #) ;
Key_Home
16 # 47 #) ;
Key_Up
16 # 48 #) ;
Key_PageUp
16 # 49 #) ;
Key_Alt_KPMinus
16 # 4A #) ;
Key_Left
16 # 4B #) ;
Key_Center
16 # 4C #) ;
Key_Right
16 # 4D #) ;
Key_Alt_KPPlus
16 # 4E #) ;
Key_End
16 # 4F #) ;
Key_Down
16 # 50 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

UN PACKAGE BIEN UTILE


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

Key_PageDown
16 # 51 #) ;
Key_Insert
16 # 52 #) ;
Key_Delete
16 # 53 #) ;
Key_Shift_F1
16 # 54 #) ;
Key_Shift_F2
16 # 55 #) ;
Key_Shift_F3
16 # 56 #) ;
Key_Shift_F4
16 # 57 #) ;
Key_Shift_F5
16 # 58 #) ;
Key_Shift_F6
16 # 59 #) ;
Key_Shift_F7
16 # 5A #) ;
Key_Shift_F8
16 # 5B #) ;
Key_Shift_F9
16 # 5C #) ;
Key_Shift_F10
16 # 5D #) ;
Key_Control_F1
16 # 5E #) ;
Key_Control_F2
16 # 5F #) ;
Key_Control_F3
16 # 60 #) ;
Key_Control_F4
16 # 61 #) ;
Key_Control_F5
16 # 62 #) ;
Key_Control_F6
16 # 63 #) ;
Key_Control_F7
16 # 64 #) ;
Key_Control_F8
16 # 65 #) ;
Key_Control_F9
16 # 66 #) ;
Key_Control_F10
16 # 67 #) ;
Key_Alt_F1
16 # 68 #) ;
Key_Alt_F2
16 # 69 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

331

CHAPITRE 21. [TP] LE JEU DU SERPENT


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

332

Key_Alt_F3
16 # 6A #) ;
Key_Alt_F4
16 # 6B #) ;
Key_Alt_F5
16 # 6C #) ;
Key_Alt_F6
16 # 6D #) ;
Key_Alt_F7
16 # 6E #) ;
Key_Alt_F8
16 # 6F #) ;
Key_Alt_F9
16 # 70 #) ;
Key_Alt_F10
16 # 71 #) ;
Key_Control_Left
16 # 73 #) ;
Key_ Control_Right
16 # 74 #) ;
Key_Control_End
16 # 75 #) ;
K e y _ C o ntrol_PageDown
16 # 76 #) ;
Key_Control_Home
16 # 77 #) ;
Key_Alt_1
16 # 78 #) ;
Key_Alt_2
16 # 79 #) ;
Key_Alt_3
16 # 7A #) ;
Key_Alt_4
16 # 7B #) ;
Key_Alt_5
16 # 7C #) ;
Key_Alt_6
16 # 7D #) ;
Key_Alt_7
16 # 7E #) ;
Key_Alt_8
16 # 7F #) ;
Key_Alt_9
16 # 80 #) ;
Key_Alt_0
16 # 81 #) ;
Key_Alt_Dash
16 # 82 #) ;
Key_Alt_Equals
16 # 83 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

UN PACKAGE BIEN UTILE


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178

Ke y_ Co ntr ol_PageUp
16 # 84 #) ;
Key_F11
16 # 85 #) ;
Key_F12
16 # 86 #) ;
Key_Shift_F11
16 # 87 #) ;
Key_Shift_F12
16 # 88 #) ;
Key_Control_F11
16 # 89 #) ;
Key_Control_F12
16 # 8A #) ;
Key_Alt_F11
16 # 8B #) ;
Key_Alt_F12
16 # 8C #) ;
Key_Control_Up
16 # 8D #) ;
Ke y_ Co ntr ol_KPDash
16 # 8E #) ;
Ke y_ Co ntr ol_Center
16 # 8F #) ;
Ke y_ Co ntr ol_KPPlus
16 # 90 #) ;
Key_Control_Down
16 # 91 #) ;
Ke y_ Co ntr ol_Insert
16 # 92 #) ;
Ke y_ Co ntr ol_Delete
16 # 93 #) ;
K ey _ C on t ro l_KPSlash
16 # 95 #) ;
Ke y_ Co ntr ol_KPStar
16 # 96 #) ;
Key_Alt_EHome
16 # 97 #) ;
Key_Alt_EUp
16 # 98 #) ;
Key_Alt_EPageUp
16 # 99 #) ;
Key_Alt_ELeft
16 # 9B #) ;
Key_Alt_ERight
16 # 9D #) ;
Key_Alt_EEnd
16 # 9F #) ;
Key_Alt_EDown
16 # A0 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

333

CHAPITRE 21. [TP] LE JEU DU SERPENT


179
180
181
182
183
184

Key_ Alt_EPageDown
16 # A1 #) ;
Key_Alt_EInsert
16 # A2 #) ;
Key_Alt_EDelete
16 # A3 #) ;
Key_Alt_KPSlash
16 # A4 #) ;
Key_Alt_Tab
16 # A5 #) ;
Key_Alt_Enter
16 # A6 #) ;

: constant Character := Character ' Val (


: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (
: constant Character := Character ' Val (

185
186
187
188

-- - - - - - - - - - - - - - - - - - -- CURSOR CONTROL --- - - - - - - - - - - - - - - - - - -

189
190
191

function Cursor_Visible return Boolean ;


procedure Set_Cursor ( Visible : in Boolean ) ;

192
193
194

function Where_X return X_Pos ;


function Where_Y return Y_Pos ;

195
196
197
198

procedure Goto_XY
( X : in X_Pos := X_Pos ' First ;
Y : in Y_Pos := Y_Pos ' First ) ;

199
200
201
202

-- - - - - - - - - - - - - - - - - -- COLOR CONTROL --- - - - - - - - - - - - - - - - - -

203
204
205

function Get_Foreground return Color_Type ;


function Get_Background return Color_Type ;

206
207
208

procedure Set_Foreground ( Color : in Color_Type := Gray ) ;


procedure Set_Background ( Color : in Color_Type := Black ) ;

209
210
211
212

-- - - - - - - - - - - - - - - - - - -- SCREEN CONTROL --- - - - - - - - - - - - - - - - - - -

213
214

procedure Clear_Screen ( Color : in Color_Type := Black ) ;

215
216
217
218
219

-- - - - - - - - - - - - - - - - - -- SOUND CONTROL --- - - - - - - - - - - - - - - - - procedure Bleep ;

220
221
222

334

-- - - - - - - - - - - - - - - - - -- INPUT CONTROL --

UN PACKAGE BIEN UTILE


223

-- - - - - - - - - - - - - - - - - -

224
225
226

function Get_Key return Character ;


function Key_Available return Boolean ;

227
228

end NT_Console ;

Vous savez dsormais comment fonctionnent les packages : copiez chacun de ces textes
dans un fichier spar et enregistrez-les sous les noms NT_Console.adb et NT_Console.ads
dans le rpertoire de votre projet et noubliez pas dcrire la ligne ci-dessous en en-tte
de votre projet :
1

With NT_Console ;

Use NT_Console ;

Le contenu en dtail
Voyons maintenant le contenu de ce package. Pour cela, ouvrez le fichier NT_Console.ads
(le code source ne nous intresse pas, nous nallons observer que les spcifications). Pour
les plus craintifs, suivez le guide. Pour ceux qui peuvent sen sortir seuls (et cest faisable) lisez ce fichier par vous-mmes.
Cest lhistoire de trois types. . .
Ce package est trs bien ficel, il commence par prsenter le package : nom, nom de
lauteur, version, date de cration, droits. . . Je vous conseille de vous en inspirer pour
plus de lisibilit dans vos packages. Mais venons-en ce qui nous intresse. NT_Console
commence par dfinir trois types et seulement trois :
X_Pos : cest un Natural entre 0 et 79. Il correspond la position de votre
curseur sur une ligne de votre console : le premier emplacement est le numro 0,
le dernier est le numro 79. Autrement dit, vous pouvez afficher 80 caractres par
ligne.
Y_Pos : cest un Natural entre 0 et 24. Il correspond au numro de la ligne
laquelle se situe votre curseur. La premire ligne est la numro 0, la dernire la
numro 24 do un total de 25 lignes affichables dans la console. A elles deux,
des variables de type X_Pos et Y_Pos vous indiquent o se situe votre curseur
lcran. Attention, encore une fois, le premier emplacement est le numro (0,0)
Color_Type : cest un type numr comptant 15 noms de couleur. Cest ce
type qui va nous servir dfinir la couleur du texte ou de larrire plan. Pour
ceux qui auraient du mal avec langlais, cest comme si vous aviez ceci :
type Color_Type is
(Noir, Bleu, Vert, Cyan, Rouge, Magenta, Marron, Gris,
Bleu_Clair, Vert_Clair, Cyan_clair, Rouge_Clair,
Magenta_Clair, Jaune, Blanc);

335

CHAPITRE 21. [TP] LE JEU DU SERPENT


Puis, vient une longue (trs longue) liste de constantes correspondant aux valeurs de
certaines touches ou combinaisons de touches du clavier.
Et pour quelques fonctions de plus
Nous en venons donc lessentiel : les procdures et fonctions. Elles sont trs bien
ranges en cinq catgories : contrle du curseur, contrle de la couleur, contrle de
lcran, contrle du son et contrle des entres (entres-clavier bien sr).
Contrle du curseur
Pour savoir si le curseur est visible ou non, utilisez la fonction Cursor_Visible().
Pour dfinir si le curseur sera visible ou non utilisez la procdure Set_Cursor(). Pour
connatre la position du curseur, cest dire connatre ses coordonnes X_Pos et
Y_Pos, vous pourrez utiliser les fonctions Where_X et Where_Y (Where = o).
Enfin, pour modifier cette position, vous utiliserez la procdure Goto_XY() qui prend
en paramtre une variable de type X_Pos et une de type Y_Pos.
Contrle de la couleur
Il y a deux lments dont nous pouvons modifier la couleur laffichage : la couleur du
texte (lavant-plan ou premier plan) et la couleur du fond, du surlignage (larrire-plan).
Cest cela que correspondent Foreground (lavant-plan) et Background (arrire-plan).
Comme toujours, deux actions sont possibles : lire et crire. Si vous souhaitez connatre
la couleur de larrire plan, vous devez la lire et vous utiliserez donc la fonction
get_Background qui vous retournera la couleur de fond. Si vous souhaitez modifier
cette couleur, alors vous utiliserez la procdure Set_Background(une_couleur). Mme
chose pour lavant-plan bien entendu.
Retenez donc ceci :
Foreground = Avant-plan / Background = Arrire-plan
Get = Saisir / Set = Dfinir
Contrle de lcran
Cette section, comme la suivante, ne comporte quune seule procdure : Clear_Screen().
Cette procdure prend en paramtre une couleur (une variable de type Color_Type)
et, comme son nom lindique, nettoie lcran. Plus rien nest affich et en plus vous
pouvez changer en mme temps la couleur de fond de la console. Attention, la couleur
de fond de la console est diffrente de la couleur darrire-plan de votre texte !
Contrle du son
Une seule procdure sans grand intrt : Bleep. Cette procdure se contente dmettre
un bip, comme si Windows avait rencontr une erreur.
Contrle des entes clavier
Lorsque lutilisateur appuie sur les touches du clavier (mme sans que votre programme
ne ly ai invit), celui-ci transmet une information votre ordinateur qui la stocke en
mmoire (on parle de mmoire tampon ou de Buffer, souvenez-vous, nous en avions
336

. . . ET ENCORE UN AUTRE !
parl quand nous avions vu linstruction Skip_line). Ainsi, la mmoire tampon peut
contenir toute une srie de caractres avant mme que le programme nen ait besoin ou
bien tre vide alors que le programme attend une rponse. La fonction key_available
vous permet de savoir si une touche a t stocke en mmoire tampon. Les fonctions
et procdures get, get_line, get_immediate. . . se contentent ainsi de piocher dans la
mmoire tampon, au besoin en attendant quelle se remplisse.
La fonction get_key quant elle agit un peu la manire de la procdure get_immediate :
elle pioche immdiatement
dans la mmoire tampon sans attendre que lutilisateur va
lide la saisie avec Entre . Quel intrt ? Eh bien il y a une petite diffrence, notamment dans la gestion
 des touches spciales . Par touches spciales , jentends les
touches flches ou F10 par exemple. Essayez ce code :
1
2
3

with nt_console ;
with ada . text_io ;
with ada . Integer_Text_IO ;

use nt_console ;
use ada . Text_IO ;
use ada . Integer_Text_IO ;

4
5
6
7
8
9
10
11
12
13
14

procedure test is
c : character ;
begin
loop
c := get_key ;
put ( " valeur correspondante : " ) ;
put ( character ' pos ( c ) ) ;
new_line ;
end loop ;
end test01 ;

Vous vous rendrez compte que toute touche spciale envoie en fait deux caractres : le
caractre n0 suivi dun second. Ainsi, la flche gauche envoie le caractre n0 suivi
du caractre n75. laide de ce petit programme, vous pourrez rcuprer les numros
des touches flches qui serviront au joueur diriger le serpent.
La fonction key_available quant elle indique si le buffer est vide ou pas. Si elle
renvoie TRUE, cest quil y a au moins un caractre en mmoire tampon et que lon peut
le saisir. Chose importante, elle nattend pas que lutilisateur appuie sur un touche !
Ainsi, laide de cette fonction, nous pourrons faire en sorte que le serpent continue
avancer sans attendre que lutilisateur appuie sur une touche ! Nous nutiliserons
get_key que dans le cas o key_available aura pralablement renvoy TRUE.

. . . et encore un autre !
Pour mesurer le temps et dfinir ainsi la vitesse de jeu, nous aurons besoin dun second
package (officiel celui-l) : Ada.Calendar !
Le package Ada.calendar est fait pour nous permettre de manipuler lhorloge. Si vous
cherchez le fichier correspondant, il porte le nom a-calend.ads. Deux types essentiels
le composent : le type Duration et le type Time (mme si Time est une copie de
Duration). Duration mesure les dures, Time correspond aux dates. Ce sont tous deux
337

CHAPITRE 21. [TP] LE JEU DU SERPENT


des nombres dcimaux, puisque le temps est mesur en secondes. Attention toutefois
aux problmes de typage ! Nous allons dfinir deux variables : temps et duree.
temps : time ;
duree : Duration ;

1
2

Pour saisir la date actuelle (jour, mois, anne, seconde), il faut utiliser la fonction
clock :
temps := clock ;

Il est ensuite possible deffectuer des oprations, par exemple :


duree := clock - temps ;

En soustrayant le temps enregistr dans la variable temps au temps actuel fourni par
la fonction clock, on obtient une dure. Il est galement possible de soustraire (ou
dajouter) une dure un temps, ce qui donne alors un temps. Il est galement possible
de comparer des variables de type Time ou Duration. Enfin, pour afficher une variable
de type Time ou Duration, pensez la convertir pralablement en Float. Pour en savoir
plus sur le package Ada.Calendar, nhsitez pas plucher les fichiers a-calend.adb et
a-calend.ads, ils ne sont pas trs longs.

Quelques indications
Jouer en temps rel
Comment je fais pour jouer en temps rel ? Mon serpent ne peut pas avancer
tout seul et en mme temps attendre que je lui indique la direction prendre !
Nous allons devoir combiner les deux packages dont je viens de vous parler pour parvenir
ce petit exploit. Le serpent avance case par case, chaque avance prend un certain
temps (pour ma part, jai pris une dure de 0.2 secondes) durant lequel le joueur peut
appuyer sur les touches de son clavier pour modifier la direction du serpent. Une fois
ce temps coul, le serpent avance dune case. Cela nous amne lalgorithme suivant :
1
2
3
4

TANT QUE duree < 0 . 2 REPETER


|
attendre_une_r action_du_joueur ;
FIN REPETER
Av an ce r_l e_serpent ;

Le soucis, cest que si le joueur nappuie jamais sur le clavier, le programme


reste bloqu sur linstruction attendre_une_raction_du_joueur et on ne
sort jamais de la boucle, non ?
338

QUELQUES INDICATIONS
Cest l quintervient le package NT_Console ! Nous ne devons pas attendre que le
joueur ait saisi une touche mais simplement nous demander sil a appuy sur une
touche. Si oui, on saisit effectivement cette touche, sinon on continue parcourir notre
boucle. Cela nous amne lalgorithme suivant :
1
2
3

if Key_available
then c := get_key ;
end if ;

Ainsi, le programme ne reste pas bloqu sur get_key indfiniment. Cependant, nous
attendons que le joueur appuie sur une touche flche, pas sur une lettre ! Comme je
vous lai dit, les touches spciales envoient deux caractres conscutivement : le numro
0 puis le vritable caractre. Donc nous devons reprendre le code prcdent ainsi :
1
2
3
4
5
6

if Key_available
then if character ' pos ( get_key ) = 0
then c := get_key ;
traitement ;
end if ;
end if ;

Comment afficher un serpent et une zone de jeu en couleur ?


Pour cela, regardons la capture dcran (voir figure 21.2), elle nest pas parole dvangile
mais elle va nous guider.

Figure 21.2 Jeu du serpent


Que remarque-t-on ? Tout dabord, le fond de la console est gris, contrairement laire
de jeu qui elle est blanche et au serpent qui est dessin en bleu par-dessus laire de jeu.
339

CHAPITRE 21. [TP] LE JEU DU SERPENT


Nous avons donc trois niveau de couleur :
La couleur de fond de la console : elle sera obtenue lorsque vous utiliser la
procdure clear_screen().
La couleur de la zone de jeu : elle est obtenue en fixant la couleur darrireplan et en affichant de simples espaces. Attention ne pas afficher de caractres
avec un arrire-plan blanc en dehors de cette zone. Notamment si vous affichez
du texte comme le score ou un message de dfaite.
La couleur des items affichs sur la zone de jeu : ces items comme les
morceaux du serpent ou lobjet quil doit manger, doivent avoir un arrire-plan
blanc et un avant-plan bleu (ou nimporte quelle autre couleur de votre choix).
Pensez galement que lorsque votre serpent avancera dune case, il ne suffira pas dafficher le nouveau serpent, il faudra galement penser effacer lancien de lcran !
Mais do tu sors tes ronds, tes rectangles et tes triangles ?

Cest simple. Il suffit de raliser un petit programme qui affiche tous les caractres du
numro 0 au numro 255 ( laide des attributs pos et val). On dcouvre ainsi plein
de caractres bizarrodes comme le trfle, le cur, le pique et le carreau ou des flches,
des carrs. . . Pour les rectangles du corps du serpent, jai utilis characterval(219)
et characterval(178), pour les triangles jai utilis characterval(16), characterval(17),
characterval(30) ou characterval(31) et quant lespce de soleil, il sagit de
characterval(15). Mais ne vous contentez pas de reprendre mot mot mes propositions, faites des essais et trouvez les symboles qui vous paraissent les plus appropris.

Par o commencer ?
Nous en sommes au troisime TP, il serait bon que vous commenciez tablir vousmme votre approche du problme. Je ne vais donc pas dtailler normment cette
partie. Voici comment jaborderais ce TP (en entrecoupant chaque tape de divers
tests pour trouver dventuels bogues) :
Cration des types ncessaires pour faire un serpent.
Ralisation des procdures daffichage de laire de jeu et du serpent.
Ralisation des procdures de dplacement et dagrandissement du serpent +
actualisation ventuelle des procdures daffichage.
Ralisation des procdures permettant au Joueur de piloter le Serpent au clavier.
Mise en place des rgles : interdiction de sortir de lespace de jeu, de mordre le
corps du serpent, deffectuer des demi-tours.
Ralisation des procdures gnrant litem avaler (appel Anneau dans mon
code) et actualisation des rgles : si le serpent mange lanneau alors il grandit ,
un anneau ne peut apparatre directement sur le serpent .
340

UNE SOLUTION POSSIBLE


ventuels dbogages et ajouts de fonctionnalits.
Voil pour le plan de bataille. Il est succinct mais devrait vous fournir des objectifs
partiels facilement atteignables. Je me rpte mais nhsitez pas effectuer des tests
rguliers et approfondis pour tre sr de ne pas avoir cr de bogues. Par exemple,
ce nest pas parce que votre serpent avance correctement vers le haut, quil avancera
correctement vers la droite ou aprs avoir effectu un ou deux virages. Et en cas de
difficults persistantes, nhsitez pas poser des questions.

Une solution possible


Le fichier principal :
1
2
3
4

with nt_console ;
with Snake_Variables ;
Snake_Variables ;
with Snake_Programs ;
;
with Snake_Screen ;

use nt_console ;
use
use Snake_Programs
use Snake_Screen ;

5
6

procedure Snake is

7
8

Serpent

: T_Serpent ;

9
10
11

begin
Set_Cursor ( false ) ;

12
13
14
15

print_ecran ( snake ) ;
print_ecran ( ready ) ;
print_ecran ( start ) ;

16
17
18
19
20

Clear_screen ( Couleur_Ecran ) ;
print_plateau ;
Init_Serpent ( Serpent ) ;
Print_Serpent ( Serpent ) ;

21
22

Game ( Serpent ) ;

23
24

end Snake ;

Le package contenant quelques types et surtout les variables ncessaires au programme :


1

with nt_console ;

use nt_console ;

2
3

package Snake_Variables is

4
5
6
7

type T_Coord is record


x , y : integer := 0 ;
end record ;

341

CHAPITRE 21. [TP] LE JEU DU SERPENT


8

Longueur
Hauteur

9
10

: Natural
: Natural

:= 30 ;
:= 15 ;

11

subtype Intervalle_Alea is natural range 1 .. Longueur * Hauteur


;

12
13
14
15

HDecalage
: Natural
last - X_Pos ' first +1 - Longueur ) / 2 ;
VDecalage
: Natural
last - Y_Pos ' first +1 - Hauteur ) / 2 ;

:= ( X_Pos '

Couleur_Ecran :
Couleur_Fond :
Couleur_Texte :
;
Couleur_Anneau :

Color_Type
Color_Type
Color_Type

:= gray ;
:= white ;
:= light_blue

Color_Type

:= red ;

:= ( Y_Pos '

16
17
18
19
20
21
22
23

Duree
Score

: Duration
: Natural

touche

: Character ;

:= 0 . 2 ;
:= 0 ;

24
25
26
27

end Snake_Variables ;

Le package contenant la plupart des programmes servant au jeu :


1
2
3
4

with nt_console ;
with ada . text_io ;
with ada . Integer_Text_IO ;
Integer_Text_IO ;
with ada . Calendar ;

5
6

package body Snake_Programs is

7
8
9
10

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AFFICHAGE PLATEAU
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

11
12
13
14
15
16
17
18
19
20
21
22
23

342

procedure print_plateau is
begin
print_score ;
new_line ( Count ( VDecalage ) ) ;
for j in 1 .. Hauteur loop
set_background ( Couleur_Ecran ) ;
for i in 1 .. HDecalage loop
put ( ' ') ;
end loop ;
set_background ( Couleur_Fond ) ;
for i in 1 .. longueur loop
put ( ' ') ;

use nt_console ;
use ada . Text_IO ;
use ada .
use ada . Calendar ;

UNE SOLUTION POSSIBLE


24
25
26
27

end loop ;
new_line ;
end loop ;
end print_plateau ;

28
29
30
31
32
33
34
35
36
37
38
39
40

procedure print_score is
begin
set_foreground ( green ) ;
set_background ( Couleur_Ecran ) ;
goto_XY (0 , 0 ) ;
put ( " SCORE = " ) ; put ( Score , 4 ) ;
set_foreground ( Couleur_Texte ) ;
set_background ( Couleur_Fond ) ;
end print_score ;
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CREATION SERPENT
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

41
42
43
44
45
46
47
48
49
50
51

procedure Init_Serpent ( Serpent : in out T_Serpent ) is


begin
Append ( Serpent . corps ,( 15 , 7 ) ) ;
-- placement
des anneaux du serpent
Append ( Serpent . corps ,( 15 , 8 ) ) ;
Append ( Serpent . corps ,( 15 , 9 ) ) ;
Append ( Serpent . corps ,( 14 , 9 ) ) ;
Append ( Serpent . corps ,( 13 , 9 ) ) ;
Serpent . curseur := First ( Serpent . corps ) ; -- placement du
curseur sur la t te du serpent
Serpent . direction := (0 , - 1 ) ;
-- direction
vers le haut
end Init_Serpent ;

52
53
54
55
56
57
58
59
60
61
62
63
64

procedure move ( Serpent : in out T_Serpent ) is


coord : T_coord ;
begin
efface_queue ( Serpent ) ;
-- on
efface la queue
coord . x := Element ( Serpent . curseur ) . x + Serpent .
direction . x ;
coord . y := Element ( Serpent . curseur ) . y + Serpent .
direction . y ;
Prepend ( Serpent . corps , coord ) ;
-- on
ajoute une nouvelle t te
Serpent . curseur := Last ( Serpent . corps ) ;
Delete ( serpent . corps , Serpent . curseur ) ;
-- on
supprime la queue
Serpent . curseur := First ( Serpent . corps ) ;
print_serpent ( serpent ) ;
-- on
affiche le nouveau corps
end move ;

343

CHAPITRE 21. [TP] LE JEU DU SERPENT


65
66
67
68
69
70
71
72
73
74

procedure grow ( Serpent : in out T_Serpent ) is


coord : T_coord ;
begin
coord . x := First_Element ( Serpent . corps ) . x + Serpent .
direction . x ;
coord . y := First_Element ( Serpent . corps ) . y + Serpent .
direction . y ;
Prepend ( Serpent . corps , coord ) ;
-- on
ajoute une nouvelle t te
Serpent . curseur := First ( Serpent . corps ) ;
print_serpent ( serpent ) ;
-- on
affiche le nouveau corps
end grow ;

75
76
77
78
79
80
81
82
83
84

function est_sorti ( Serpent : T_Serpent ) return boolean is


tete : T_Coord ;
begin
tete := First_Element ( Serpent . corps ) ;
if tete . x < 1 or tete . x > Longueur or tete . y < 1 or tete .
y > hauteur
then return true ;
else return false ;
end if ;
end est_sorti ;

85
86
87
88
89
90
91
92
93
94
95
96

function est_mordu ( Serpent : T_Serpent ) return boolean is


tete : T_Coord ;
Serpent2 : T_Serpent := Serpent ;
begin
tete := First_Element ( Serpent2 . corps ) ;
Delete_first ( Serpent2 . corps ) ;
if Find ( Serpent2 . corps , tete ) /= No_Element
-- Is_In ( tete , Serpent2 . corps )
then return true ;
else return false ;
end if ;
end est_mordu ;

97
98
99
100
101
102
103
104
105
106
107

344

function a_mange ( Serpent : T_Serpent ; Anneau : T_Coord )


return boolean is
tete : T_Coord ;
begin
tete := First_Element ( Serpent . corps ) ;
if tete = Anneau
then return true ;
else return false ;
end if ;
end a_mange ;

UNE SOLUTION POSSIBLE


108
109
110

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -AFFICHAGE SERPENT
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

111
112
113
114
115
116
117
118
119
120
121
122

procedure print_tete ( Serpent : in


begin
if serpent . direction . x < 0
then put ( character ' val ( 17 ) )
gauche
elsif serpent . direction . x > 0
then put ( character ' val ( 16 ) )
droite
elsif serpent . direction . y <0
then put ( character ' val ( 30 ) )
else put ( character ' val ( 31 ) )
end if ;
end print_tete ;

T_Serpent ) is
;

-- regard vers la

-- regard vers la

;
;

-- regard vers le haut


-- regard vers le bas

123
124
125
126
127
128
129
130

procedure print_corps ( nb : natural ) is


begin
if nb mod 2 = 0
then put ( character ' val ( 219 ) ) ;
else put ( character ' val ( 178 ) ) ;
end if ;
end print_corps ;

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

procedure print_serpent ( Serpent : in out T_Serpent ) is


begin
Set_Foreground ( Couleur_Texte ) ;
for i in 1 .. length ( Serpent . corps ) loop
Goto_XY ( Element ( serpent . curseur ) . x + HDecalage -1 ,
Element ( serpent . curseur ) . y + VDecalage - 1 ) ;
if i = 1
then print_tete ( serpent ) ;
else print_corps ( integer ( i ) ) ;
end if ;
Next ( Serpent . curseur ) ;
end loop ;
Serpent . curseur := First ( Serpent . corps ) ;
end print_serpent ;

146
147
148
149
150
151
152
153
154

procedure efface_queue ( Serpent : in out T_Serpent ) is


begin
Serpent . curseur := Last ( Serpent . corps ) ;
Goto_XY ( Element ( serpent . curseur ) . x + HDecalage -1 ,
Element ( serpent . curseur ) . y + VDecalage - 1 ) ;
put ( ' ') ;
Serpent . curseur := First ( Serpent . corps ) ;
end efface_queue ;

155

345

CHAPITRE 21. [TP] LE JEU DU SERPENT


156
157
158

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -GESTION DES ANNEAUX


--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

159
160
161
162
163
164
165
166
167
168
169
170
171

function generer ( germe : generator ; Serpent : T_Serpent )


return T_Coord IS
temp1 , temp2 : Natural ;
anneau : T_Coord ;
BEGIN
loop
temp1 := random ( germe ) ; temp2 := random ( germe ) ;
Anneau := ( temp1 mod longueur + 1 , temp2 mod hauteur +
1) ;
if Find ( Serpent . corps , Anneau ) = No_Element
then return Anneau ;
end if ;
end loop ;
end generer ;

172
173
174
175
176
177
178
179

procedure print_anneau ( anneau : T_Coord ) is


begin
set_foreground ( Couleur_Anneau ) ;
Goto_XY ( anneau . x + Hdecalage -1 , anneau . y + Vdecalage - 1 ) ;
put ( character ' val ( 15 ) ) ;
set_foreground ( Couleur_Texte ) ;
end print_anneau ;

180
181
182
183

-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PROGRAMME DE JEU
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

184
185
186
187
188
189
190
191

procedure Erreur ( Message : in String ) is


begin
Goto_XY ( HDecalage +5 , VDecalage + Hauteur + 2 ) ;
set_background ( Couleur_Ecran ) ;
set_foreground ( light_Red ) ;
Put ( Message ) ; delay 1 . 0 ;
end Erreur ;

192
193
194
195
196
197
198
199
200
201
202

346

procedure un_tour ( Serpent : in out T_Serpent ) is


choix_effectue : boolean := false ;
Temps
: constant Time := clock ;
New_direction : T_Coord ;
begin
while clock - temps < duree loop
if not choix_effectue and then key_available and then
character ' pos ( get_key ) = 0
then touche := get_key ;
case character ' pos ( touche ) is
when 72 = > New_direction := (0 , - 1 ) ; -- haut

UNE SOLUTION POSSIBLE


203

gauche
204

droite

205
206
207
208
209
210
211
212
213
214

when 75 = > New_direction := ( -1 , 0 ) ; -when 77 = > New_direction := (1 , 0 ) ; --

when 80 = > New_direction := (0 , 1 ) ; -- bas


when others = > null ;
end case ;
if New_direction . x /= Serpent . direction . x and
New_direction . y /= Serpent . direction . y
then Serpent . direction := New_direction ;
choix_effectue := true ;
end if ;
end if ;
end loop ;
end un_tour ;

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

procedure game ( Serpent : in out T_Serpent ) is


germe : generator ;
Anneau : T_Coord ;
begin
reset ( germe ) ;
Anneau := generer ( germe , Serpent ) ;
Print_anneau ( Anneau ) ;
loop
un_tour ( Serpent ) ;
-- test pour savoir si le serpent grandit ou avance
if a_mange ( serpent , anneau )
then grow ( serpent ) ;
Score := Score + 100 ;
Print_score ;
Anneau := generer ( germe , serpent ) ;
Print_anneau ( Anneau ) ;
else move ( serpent ) ;
end if ;
-- test pour savoir si le serpent meurt
if est_sorti ( serpent )
then Erreur ( " Vous " & character ' val ( 136 ) & " tes
sortis ! " ) ; exit ;
elsif est_mordu ( serpent )
then Erreur ( " Vous vous " & character ' val ( 136 ) & "
tes mordu ! " ) ; exit ;
end if ;
end loop ;
end game ;

242
243

1
2

end Snake_Programs ;
with Snake_Variables ;
Snake_Variables ;
with ada . Numerics . Discrete_Random ;

use

347

CHAPITRE 21. [TP] LE JEU DU SERPENT


3

with ada . Containers . Doubly_Linked_Lists ;


;

use ada . Containers

package Snake_Programs is

5
6

package P_Liste is new Ada . Containers . Doubly_Linked_Lists (


T_coord ) ;
use P_Liste ;

7
8
9

package P_Aleatoire is new ada . Numerics . Discrete_Random (


Intervalle_Alea ) ;
use P_Aleatoire ;

10
11
12

Type T_Serpent is record


corps
: List ;
Curseur
: Cursor ;
direction : T_Coord ;
end record ;

13
14
15
16
17
18

procedure print_plateau ;
procedure print_score ;

19
20
21

procedure Init_Serpent ( Serpent : in out T_Serpent ) ;


procedure move ( Serpent : in out T_Serpent ) ;
procedure grow ( Serpent : in out T_Serpent ) ;
function est_sorti ( Serpent : T_Serpent ) return boolean ;
function est_mordu ( Serpent : T_Serpent ) return boolean ;

22
23
24
25
26
27

procedure
procedure
procedure
procedure

28
29
30
31

print_tete ( Serpent : in T_Serpent ) ;


print_corps ( nb : natural ) ;
print_serpent ( Serpent : in out T_Serpent ) ;
efface_queue ( Serpent : in out T_Serpent ) ;

32

function generer ( germe : generator ; Serpent : T_Serpent )


return T_Coord ;
procedure print_anneau ( anneau : T_Coord ) ;

33
34
35

procedure Erreur ( Message : in String ) ;


procedure un_tour ( Serpent : in out T_Serpent ) ;
procedure game ( Serpent : in out T_Serpent ) ;

36
37
38
39
40

end Snake_Programs ;

Enfin, le package servant laffichage de mes crans de titre :


1
2
3

with ada . text_IO ;


with NT_Console ;
With Snake_Variables ;

4
5
6

348

package body Snake_screen is

use ada . Text_IO ;


use NT_Console ;
use Snake_Variables ;

UNE SOLUTION POSSIBLE


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

procedure print_line ( line : string ) is


begin
for i in line ' range loop
case line ( i ) is
when 'R ' = > set_background ( Red ) ;
put ( ' ') ;
set_background ( Couleur_Ecran )
when 'G ' = > set_background ( Green ) ;
put ( ' ') ;
set_background ( Couleur_Ecran )
when 'Y ' = > set_background ( Yellow ) ;
put ( ' ') ;
set_background ( Couleur_Ecran )
when '# ' = > set_background ( Black ) ;
put ( ' ') ;
set_background ( Couleur_Ecran )
when others = > put ( ' ') ;
end case ;
end loop ;
new_line ;
end print_line ;

;
;
;
;

28
29
30
31
32
33
34
35
36
37
38
39

procedure print_fichier ( name : string ) is


F : file_type ;
begin
open (F , In_File , name ) ;
clear_screen ( couleur_ecran ) ;
set_background ( Couleur_Ecran ) ;
while not end_of_file ( f ) and not end_of_page ( f ) loop
print_line ( get_line ( f ) ) ;
end loop ;
close ( f ) ;
end print_fichier ;

40
41
42
43
44
45
46
47
48

procedure print_ecran ( Ecran : T_Ecran ) is


begin
case Ecran is
when SNAKE = > print_fichier ( " Snake . pic " ) ; delay 2 . 5 ;
when START = > print_fichier ( " Start . pic " ) ; delay 1 . 0 ;
when READY = > print_fichier ( " Ready . pic " ) ; delay 1 . 5 ;
end case ;
end print_ecran ;

49
50

end Snake_Screen ;
package Snake_Screen is

2
3
4
5

type T_Ecran is ( START , READY , SNAKE ) ;


procedure print_line ( line : string ) ;
procedure print_fichier ( name : string ) ;

349

CHAPITRE 21. [TP] LE JEU DU SERPENT


procedure print_ecran ( Ecran : T_Ecran ) ;

6
7
8

end Snake_Screen ;

Pour finir, je vous transmets galement les fichiers .pic (en fait des fichiers textes)
servant de support ces fameux crans titres (je suis preneur pour tout cran titre
pouvant remplacer celui qui ressemble vaguement un serpent ) :
1

GGGGGGGG
YG
G
YG
Y
YG
GY
GG
GY
GGY
GGGGGGGGGGGY
YGGYGYGYGYGYG
GGGGY
GGY
GGY
GG
GG
YGG
GGGG
YYGGGGGG
YYGGGGGG
YYGGGGG
YYYG

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1

####
#
#
#
#
####
# #
# #
#
#

3
4
5
6
7
8
9
1

#####
#
#### #
# ###
#
#
#
# #
# #
#
#
# # #
#
# #
#
#
###
# # #
#
#
#
#
##### #
#
#
#
#
#
# #
# #
##### #
# #### #
#

2
3
4
5
6
7
8
9

350

#####
#
#### #####
#
#
#
#
#
#
# # #
#
#
###
#
# # ####
#
#
#
##### # #
#
#
#
#
#
# # #
#
###
#
#
# #
#
#
#
#

###

#
#
#
#
#

#
#
#
#
#

#
#
#
#
#

UNE SOLUTION POSSIBLE

En rsum
Pistes damlioration :
Proposer diffrent niveaux de difficult : en faisant varier la vitesse, la taille de
laire de jeu. . .
Ajouter des adversaires ou des obstacles.
Proposant lenregistrement des meilleurs scores.
Proposer des bonus comme des anneaux comptant le double de points ou permettant de rduire votre taille.

351

CHAPITRE 21. [TP] LE JEU DU SERPENT

352

Quatrime partie

Ada : Notions avances et


Programmation Oriente Objet

353

Chapitre

22

Algorithmique : tri et complexit


Difficult :
Lors du chapitre sur les tableaux (partie III), nous avions abord lalgorithme de tri par
slection. Cet algorithme consistait trier un tableau dentiers du plus petit au plus grand
en comparant progressivement les lments dun tableau avec leurs successeurs, quitte
effectuer un change (nhsitez pas relire cet exercice la page 11 si besoin). Comme
je vous lavais alors dit, cet algorithme de tri est trs rudimentaire et sa complexit tait
forte.
Cest quoi la complexit ? Comment peut-on mieux trier un tableau ?

355

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


Ce sont les deux questions auxquelles nous allons tenter de rpondre. Nous commencerons par voquer quelques algorithmes de tri (sans chercher tre exhaustif) : tri
par slection (rappel), tri bulles, tri par insertion, tri rapide, tri fusion et tri par
tas. Je vous expliquerai le principe avant de vous proposer un implmentation possible.
Ensuite, nous expliquerons en quoi consiste la complexit dun algorithme et aborderons les aspects mathmatiques ncessaires de ce chapitre. Enfin, nous tudierons la
complexit de ces diffrents algorithmes de manire empirique en effectuant quelques
mesures.
Ce chapitre est davantage un chapitre dalgorithmique que de programmation.
Aucune nouvelle notion de programmation ne sera aborde. En revanche,
nous tudierons diffrentes techniques de tri, parfois surprenantes. Qui plus
est, ltude de complexit, si elle se veut empirique, fera toutefois appel des
notions mathmatiques peut-tre compliques pour certains. Pour toutes ces
raisons, ce chapitre nest pas obligatoire pour la poursuite du cours.

Algorithmes de tri lents


De nombreux domaines, notamment scientifiques, ont besoin dalgorithmes efficaces :
traitement du gnome humain, mtorologie, positionnement par satellite, oprations
financires, calcul intensif. . . Ces domaines ne peuvent se permettre dattendre des
heures un rsultat qui pourrait tre obtenu en quelques minutes par de meilleurs algorithmes. Pour bien comprendre cette notion defficacit, commenons par voquer
des algorithmes de tris parmi les plus lents (cette notion de lenteur sera prcise et
nuance plus tard) : tri par slection, tri par insertion et tri bulles. Nous aurons
par la suite besoin de quelques programmes essentiels : initialisation et affichage dun
tableau, change de valeurs. Je vous fournis de nouveau le code ncessaire ci-dessous.
1
2
3
4
5
6
7
8
9
10
11
12
13

function init return T_Tableau is


T : T_Tableau ;
subtype Intervalle is integer range 1 .. 100 ;
package Aleatoire is new Ada . numerics . discrete_random (
Intervalle ) ;
use Aleatoire ;
Hasard : generator ;
begin
Reset ( Hasard ) ;
for i in T ' range loop
T ( i ) := random ( Hasard ) ;
end loop ;
return T ;
end init ;

14
15
16

356

-- Afficher affiche les valeurs d ' un tableau sur une m me


ligne

ALGORITHMES DE TRI LENTS


17
18
19
20
21
22

procedure Afficher ( T : T_Tableau ) is


begin
for i in T ' range loop
Put ( T ( i ) ,4 ) ;
end loop ;
end Afficher ;

23
24

-- Echanger est une proc dure qui change deux valeurs : a


vaudra b et b vaudra a

25
26
27
28
29
30
31
32

procedure Echanger ( a : in out integer ; b : in out integer )


is
c : integer ;
begin
c := a ;
a := b ;
b := c ;
end Echanger ;

Tri par slection (ou par extraction)


Principe
1
7

2
3

3
18

4
13

5
7

Nous allons trier le tableau ci-dessus par slection. Le principe est simple : nous allons
chercher le minimum du tableau et lchanger avec le premier lment :
Rang 1
1
7

2
3

3
18

4
13

5
7

1
3

2
7

3
18

4
13

5
7

Puis on ritre lopration : on recherche le minimum du tableau (priv toutefois du


premier lment) et on lchange avec le second (ici pas dchange ncessaire).
Rang 2
1
3

2
7

3
18

4
13

5
7
357

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


On ritre nouveau lopration : on recherche le minimum du tableau (priv cette
fois des deux premiers lments) et on lchange avec le troisime.
Rang 3
1
3

2
7

3
18

1
3

2
7

3
7

4
13

4
13

5
7

5
18

On ritre ensuite avec le quatrime lment et on obtient ainsi un tableau entirement ordonn par ordre croissant. Cest lun des algorithmes les plus simples et intuitifs. Pour aller plus loin ou pour davantage dexplications, vous pouvez consulter le
tutoriel de K-Phoen (http://wwww.fr.openclassrooms.com/informatique/cours/
le-tri-par-selection) ce sujet (langage utilis : C).

Mise en uvre
Je vous fournis nouveau le code source de lalgorithme de tri par slection quelque
peu modifi par rapport la fois prcdente :
1
2
3
4
5
6
7
8
9
10
11
12

function RangMin ( T : T_Tableau ; debut : integer ; fin :


integer ) return integer is
Rang : integer := debut ;
Min : integer := T ( debut ) ;
begin
for i in debut .. fin loop
if T ( i ) < Min
then Min := T ( i ) ;
Rang := i ;
end if ;
end loop ;
return Rang ;
end RangMin ;

13
14
15
16
17
18
19
20
21

358

function Trier ( Tab : T_Tableau ) return T_Tableau is


T : T_Tableau := Tab ;
begin
for i in T ' range loop
Echanger ( T ( i ) ,T ( RangMin (T ,i ,T ' last ) ) ) ;
end loop ;
return T ;
end Trier ;

ALGORITHMES DE TRI LENTS

Tri par insertion


Principe
Le principe du tri par insertion est le principe utilis par tout un chacun pour ranger
ses cartes : on les classe au fur et mesure. Imaginons que dans une main de 5 cartes,
nous ayons dj tri les trois premires, nous arrivons donc la quatrime carte. Que
fait-on ? Eh bien cest simple, on la sort du paquet de cartes pour linsrer la bonne
place dans la partie des cartes dj tries. Exemple :
1
5

2
9

3
10

4
6

5
7

1
5

2
6

3
9

4
10

5
7

Il faudra ensuite soccuper du cinquime lment mais l nest pas la difficult, car vous
vous en doutez peut-tre, mais il est impossible dinsrer un lment entre deux autres
dans un tableau. Il ny a pas de case n1,5 ! Donc rinsrer le quatrime lment dans
le tableau consistera en fait dcaler tous ceux qui lui sont suprieurs dun cran vers
la droite. Pour aller plus loin ou pour davantage dexplications, vous pouvez consulter
le cours de bluestorm (http ://fr.openclassrooms.com/informatique/cours/le-tri-parinsertion) ce sujet (langage utilis : C) et sa suite : http://www.fr.openclassrooms.
com/informatique/cours/tri-par-insertion-le-retour-ocaml utilisant les listes
(langage utilis : OCaml).
Mise en uvre
Je vous invite rdiger cet algorithme par vous-mme avant de consulter la solution
que je vous propose, cest la meilleure faon de savoir si vous avez compris le principe
ou non (dautant plus que cet algorithme est relativement simple).
1
2
3
4
5
6
7
8
9
10
11

function tri_insertion ( T : T_Tableau ) return T_Tableau is


Tab : T_Tableau := T ;
begin
for i in T ' first + 1 .. T ' last loop
for j in reverse T ' first + 1 .. i loop
exit when Tab (j - 1 ) <= Tab ( j ) ;
echanger ( Tab (j - 1 ) , Tab ( j ) ) ;
end loop ;
end loop ;
return Tab ;
end Tri_insertion ;

Linstruction EXIT situe au cur des deux boucles peut tre remplace par un IF :
1

if Tab (j - 1 ) <= Tab ( j )

359

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


2
3
4

then exit ;
else echanger ( Tab (j - 1 ) , Tab ( j ) ) ;
end if ;

Tri bulles (ou par propagation)


Principe
Voici enfin le plus mauvais algorithme. Comme les prcdents il est plutt intuitif
mais nest vraiment pas efficace. Lalgorithme de tri par propagation consiste inverser
deux lments successifs sils ne sont pas classs dans le bon ordre et recommencer
jusqu ce que lon parcourt le tableau sans effectuer une seule inversion. Ainsi, un
petit lment sera chang plusieurs fois avec ses prdcesseurs jusqu atteindre son
emplacement dfinitif : il va se propager peu peu, ou remonter vers la bonne
position lentement, comme une bulle dair remonte peu peu la surface (jai lme
dun pote aujourdhui ).
Je ne pense pas quun exemple soit ncessaire. Lide (simplissime) est donc de parcourir
plusieurs fois le tableau jusqu obtenir la solution. Vous vous en doutez, cette mthode
est assez peu volue et demandera de nombreux passages pour obtenir un tableau
rang. Pour aller plus loin ou pour davantage dexplications, vous pouvez consulter
le cours de shareman (http://www.fr.openclassrooms.com/informatique/cours/
le-tri-a-bulles) ce sujet (langage utilis : C++).
Mise en uvre
L encore, vous devriez tre capable en prenant un peu de temps, de coder cet algorithme par vous-mme.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

function Tri_Bulles ( T : T_Tableau ) return T_Tableau is


Tab : T_Tableau := T ;
Echange_effectue : boolean := true ;
begin
while Echange_effectue loop
-- possibilit d ' afficher le tableau en crivant ici
-- Afficher ( Tab ) ;
Echange_effectue := false ;
for i in Tab ' first .. Tab ' last - 1 loop
if Tab ( i ) > Tab ( i + 1 )
then echanger ( Tab ( i ) , Tab ( i + 1 ) ) ;
Echange_effectue := true ;
end if ;
end loop ;
end loop ;
return Tab ;
end Tri_Bulles ;

360

ALGORITHMES DE TRI PLUS RAPIDES


Si vous faites quelques essais en affichant Tab chaque itration, vous vous rendrez vite
compte quavec cet algorithme le plus grand lment du tableau est immdiatement
propager la fin du tableau ds le premier passage. Il serait donc plus judicieux de
modifier les bornes de la deuxime boucle en crivant :
1

Tab ' first .. Tab ' last - 1 - k

O k correspondrait au nombre de fois que le tableau a t parcouru. Autre amlioration


possible : puisque les dplacements des grands lments vers la droite se font plus vite
que celui des petits vers la gauche (dplacement maximal de 1 vers la gauche), pourquoi
ne pas changer de sens une fois sur deux : parcourir le tableau de gauche droite, puis
de droite gauche, puis de gauche droite. . . cest ce que lon appelle le Tri Cocktail
(Shaker Sort), un poil plus efficace.
Enfin, une dernire amlioration mrite dtre signale (mais non traite), cest le tri
CombSort http://www.prevert.upmf-grenoble.fr/Prog/Tris/triCombSort.html.
Il sagit dun tri bulles nettement amlior puisquil peut rivaliser avec les meilleurs
algorithmes de tri. Lide, plutt que de comparer des lments successifs, est de comparer des lments plus loigns afin dviter les tortues, cest--dire ces petits lments
qui ne se dplacent que dun seul cran vers la gauche avec le tri bulles classique. Ce
cours nayant pas vocation rdiger tous les algorithmes de tri possibles et imaginables,
consultez le lien ci-dessus ou Combsort sur wikipedia pour en savoir plus.

Algorithmes de tri plus rapides


Tri rapide (ou Quick Sort)
Principe
Lalgorithme de tri rapide (ou Quick Sort) a un fonctionnement fort diffrent des algorithmes prcdents et un peu plus compliqu (certains diront beaucoup plus). Le
principe de base est simple mais bizarre a priori : on choisit une valeur dans le tableau
appele pivot (nous prendrons ici la premire valeur du tableau) et on dplace avant elle
toutes celles qui lui sont infrieures et aprs elle toutes celles qui lui sont suprieures.
Puis, on ritre le procd avec la tranche de tableau infrieure et la tranche de tableau
suprieure ce pivot.
Voici un exemple :
18

13

15

Nous prenons le premier nombre en pivot : 13 et nous plaons les nombres 9 et 7 avant
le 13, les nombres 15 et 18 aprs le 13.
9

13

15

18

Il ne reste plus ensuite qu ritrer lalgorithme sur les deux sous-tableaux.


361

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


9

15

18

Le premier aura comme pivot 9 et sera rorganis, le second aura comme pivot 15 et
ne ncessitera aucune modification. Il restera alors deux sous-sous-tableaux.
7
18
Comme ces sous-sous-tableaux se rsument une seule valeur, lalgorithme sarrtera.
Cet algorithme est donc rcursif et lune des difficults est de rpartir les valeurs de part
et dautre du nombre pivot. Revenons, notre tableau initial. Nous allons chercher le
premier nombre plus petit que 13 de gauche droite avec une variable i, et le premier
nombre plus grand que 13 de droite gauche avec une variable j, jusqu ce que i et j
se rencontrent.
18

13

15

On trouve 18 et 7. On les inverse et on continue.


7

13

15

18

Le rangement est presque fini, il ne reste plus qu inverser le pivot avec le 9.


9

13

15

18

Pour aller plus loin ou pour davantage dexplication, vous pouvez consulter le tutoriel de
GUILOooo (http://fr.openclassrooms.com/informatique/cours/le-tri-rapide-qsort)
ce sujet (langages utiliss : C et OCaml).
Mise en uvre
Cet algorithme va tre plus compliqu mettre en uvre. Je vous conseille dessayer
par vous-mme afin de faciliter la comprhension de la solution que je vous fournis :
1

function tri_rapide ( T : vecteur ) return vecteur is

2
3
4
5
6
7

362

procedure tri_rapide ( T : in out vecteur ; premier , dernier :


integer ) is
index_pivot : integer := ( premier + dernier ) / 2
;
j
: integer := premier + 1 ;
begin
if premier < dernier

ALGORITHMES DE TRI PLUS RAPIDES


then echanger ( T ( premier ) ,T ( index_pivot ) ) ;
index_pivot := premier ;
for i in premier + 1 .. dernier loop
if T ( i ) < T ( index_pivot )
then echanger ( T ( i ) ,T ( j ) ) ;
j := j + 1 ;
end if ;
end loop ;
echanger ( T ( index_pivot ) ,T (j - 1 ) ) ;
index_pivot := j - 1 ;

8
9
10
11
12
13
14
15
16
17
18

tri_rapide (T , premier , Index_pivot - 1 ) ;


tri_rapide (T , Index_pivot +1 , dernier ) ;

19
20
21
22

end if ;
end tri_rapide ;

23
24

Tab : Vecteur := T ;

25
26
27
28
29

begin
tri_rapide ( Tab , Tab ' first , Tab ' last ) ;
return Tab ;
end tri_rapide ;

Nous verrons un peu plus tard pourquoi jai du utiliser une procdure plutt quune
fonction pour effectuer ce tri.

Tri fusion (ou Merge Sort)


Principe
Ah ! Mon prfr. Le principe est simple mais rudement efficace (un peu moins que le
tri rapide toutefois). Prenons le tableau ci-dessous :
8

13

10

Lide est de scinder ce tableau en cinq cases disjointes que lon va refusionner
ensuite, mais dans le bon ordre. Par exemple, les deux premires cases, une fois refusionnes donneront :
5

13

Les deux suivantes donneront :

Et la dernire va rester seule pour linstant. Ensuite, on va refusionner nos deux


minitableaux (la dernire case restant encore seule) :
363

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


5

13

Et enfin, on fusionne ce dernier tableau avec la dernire case reste seule depuis le
dbut :
5

10

13

Vous laurez compris, le tri se fait au moment de la fusion des sous-tableaux. Lide
nest pas si complique comprendre, mais un peu plus ardue mettre en uvre. En
fait, nous allons crer une fonction tri_fusion qui sera rcursive et qui sappliquera
des tranches de tableaux (les deux moitis de tableaux, puis les quatre moitis de
moitis de tableaux, puis . . .) avant de les refusionner. Pour aller plus loin ou pour
davantage dexplication, vous pouvez consulter le tutoriel de renO (http://www.fr.
openclassrooms.com/informatique/cours/le-tri-fusion) ce sujet (langage utilis : Python).
Mise en uvre
Nous aurons besoin de deux fonctions pour effectuer le tri fusion : une fonction fusion
qui fusionnera deux tableaux tris en un seul tableau tri ; et une fonction tri_fusion qui
se chargera de scinder le tableau initial en sous-tableaux tris avant de les refusionner
laide de la prcdente fonction.
1
2
3
4
5
6
7
8
9
10

function fusion ( T1 , T2 : Vecteur ) return Vecteur is


T : Vecteur ( 1 .. T1 ' length + T2 ' length ) ;
i : integer := T1 ' first ;
j : integer := T2 ' first ;
begin
if T1 ' length = 0
then return T2 ;
elsif T2 ' length = 0
then return T1 ;
end if ;

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

364

for k in T ' range loop


if i > T1 ' last and j > T2 ' last
then exit ;
elsif i > T1 ' last
then T ( k .. T ' last ) := T2 ( j .. T2 ' last ) ; exit ;
elsif j > T2 ' last
then T ( k .. T ' last ) := T1 ( i .. T1 ' last ) ; exit ;
end if ;
if T1 ( i ) <= T2 ( j )
then T ( k ) := T1 ( i ) ;
i := i + 1 ;
else T ( k ) := T2 ( j ) ;
j := j + 1 ;
end if ;

ALGORITHMES DE TRI PLUS RAPIDES


26
27
28

end loop ;
return T ;
end fusion ;

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

FUNCTION Tri_fusion ( T : Vecteur ) RETURN Vecteur IS


lg : constant Integer
:= T ' length ;
BEGIN
if lg <= 1
then return T ;
else declare
T1 : Vecteur (T ' first .. T ' first - 1 + lg / 2 ) ;
T2 : Vecteur (T ' first + lg / 2 .. T ' last ) ;
begin
T1 := tri_fusion ( T (T ' first .. T ' first - 1 + lg / 2 ) ) ;
T2 := tri_fusion ( T (T ' first + lg / 2 .. T ' last ) ) ;
return fusion ( T1 , T2 ) ;
end ;
end if ;
END Tri_Fusion ;

Tri par tas


Noms alternatifs
Le tri par tas porte galement les noms suivants :
Tri arbre
Tri Maximier
Heapsort
Tri de Williams
Principe
5

12

Lide du tri par tas est de concevoir votre tableau tel un arbre, comme la figure
22.1 pour le tableau ci-dessus.
Chaque case de larbre est appele nud, le premier nud porte le nom de racine,
les derniers le nom de feuille. Si lon est rendu au nud numro n (indice dans le
tableau initial), ses deux feuilles porteront les numros 2n et 2n + 1.
Larbre nest pas tri pour autant (voir figure 22.2). Pour cela nous devrons le tamiser,
cest--dire prendre un nombre et lui faire descendre larbre jusqu ce que les nombres
en dessous lui soient infrieurs. Pour lexemple, nous allons tamiser le premier nud
(le 5).
365

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT

Figure 22.1 Reprsentation en arbre

Figure 22.2 Arbre avant tamisage de la racine

366

ALGORITHMES DE TRI PLUS RAPIDES


En dessous de lui se trouvent deux nuds : 7 et 3. On choisit le plus grand des deux :
7. Comme 7 est plus grand que 5, on les inverse (voir figure 22.3).

Figure 22.3 Aprs une premire phase de tamisage de la racine


On recommence : les deux nuds situs en dessous sont 8 et 12. Le plus grand est 12.
Comme 12 est plus grand que 5, on les inverse (voir figure 22.4).
Vous avez compris le principe du tamisage ? Trs bien. Pour trier notre arbre par tas,
nous allons le tamiser en commenant, non pas par la racine de larbre, mais par les
derniers nuds (en commenant par la fin en quelque sorte). Bien sr, il est inutile de
tamiser les feuilles de larbre : elles ne descendront pas plus bas ! Pour limiter les tests
inutiles, nous commencerons par tamiser le 3, puis le 7 et enfin le 5. Du coup, nous ne
tamiserons que la moiti des nuds de larbre (22.5)
Hein ? ! ? Plus on descend dans larbre plus les nombres sont petits, mais cest
pas tri pour autant ? !
En effet. Mais rassurez-vous, il reste encore une tape, un peu plus complexe. Nous
allons parcourir notre arbre en sens inverse encore une fois. Nous allons changer le
dernier nud de notre arbre tamis (le 3) avec le premier (le 12) qui est galement le
plus grand nombre le larbre (voir figure 22.6).
Une fois cet change effectu, nous allons tamiser notre nouvelle racine, en considrant que la dernire feuille ne fait plus partie de larbre (elle est correctement place
dsormais). Ce qui devrait nous donner le rsultat suivant (voir figure 22.7).
Cette manuvre a ainsi pour but de placer sur la dernire feuille, le nombre le plus
367

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT

Figure 22.4 Arbre aprs tamisage de la racine

Figure 22.5 Tamisage complet de larbre

368

ALGORITHMES DE TRI PLUS RAPIDES

Figure 22.6 change du dernier et du premier nud

Figure 22.7 Retamisage de larbre priv du 12

369

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


grand, tout en gardant un sous-arbre qui soit tamis. Il faut ensuite rpter cette manuvre avec lavant-dernire feuille, puis lavant-avant-dernire feuille. . . Jusqu arriver la deuxime (et oui, inutile dchanger la racine avec elle-mme, soyons logique).
Nous devrions ainsi obtenir le rsultat suivant (voir figure 22.8).

Figure 22.8 Arbre tri


Cette fois, larbre est tri dans le bon ordre, lalgorithme est termin. Il ne vous reste
plus qu limplmenter.
Mise en uvre
Vous laurez devin je lespre, il nous faut une procdure tamiser, en plus de notre
fonction Tri_tas. Commencez par la crer et lessayer sur le premier lment comme
nous lavons fait dans cette section avant de vous lancer dans votre fonction de tri.
Pensez quil doit tre possible de limiter la portion de tableau tamiser et que pour
passer dun nud n celui du dessous, il faut multiplier n par 2 (pour accder au nud
en dessous gauche) et ventuellement lui ajouter 1 (pour accder celui en dessous
droite).
1
2
3
4
5
6
7
8

370

procedure tamiser ( T
: in out vecteur ;
noeud : integer ;
max
: integer ) is
racine : integer := noeud ;
feuille : integer := 2 * noeud ;
begin
while feuille <= max loop
if feuille + 1 <= max and then T ( feuille ) <T ( feuille + 1 )

THORIE : COMPLEXIT DUN ALGORITHME


9
10
11
12
13
14
15
16
17

then feuille := feuille + 1 ;


end if ;
if T ( racine ) <T ( feuille )
then echanger ( T ( racine ) , T ( feuille ) ) ;
end if ;
racine := feuille ;
feuille := 2 * racine ;
end loop ;
end tamiser ;

18
19
20
21
22
23
24
25
26
27
28
29
30

function tri_tas ( T : vecteur ) return vecteur is


Arbre : vecteur ( 1 .. T ' length ) := T ;
begin
for i in reverse 1 .. Arbre ' length / 2 loop
tamiser ( Arbre ,i , Arbre ' length ) ;
end loop ;
for i in reverse 2 .. Arbre ' last loop
echanger ( Arbre ( i ) , Arbre ( 1 ) ) ;
tamiser ( Arbre ,1 ,i - 1 ) ;
end loop ;
return Arbre ;
end tri_tas ;

Thorie : complexit dun algorithme


Complexit
Si vous avez dors et dj test les programmes ci-dessus sur de grands tableaux, vous
avez du vous rendre compte que certains taient plus rapides que dautres, plus efficaces.
Cest ce que lon appelle lefficacit dun algorithme. Pour la mesurer, la quantifier,
on sintresse davantage son contraire, la complexit. En effet, pour mesurer la
complexit dun algorithme il suffit de mesurer la quantit de ressources quil exige
(mmoire ou temps-processeur).
En effet, le simple fait de dclarer un tableau exige de rquisitionner des emplacements mmoires qui seront rendus inutilisables par dautres programmes comme votre
systme dexploitation. Tester lgalit entre deux variables va ncessiter de rquisitionner temporairement le processeur, le rendant trs brivement inutilisable pour
toute autre chose. Vous comprenez alors que parcourir un tableau de 10 000 cases en
testant chacune des cases exigera donc de rquisitionner au moins 10 000 emplacements mmoires et dinterrompre 10 000 fois le processeur pour faire nos tests. Tout
cela ralentit les performances de lordinateur et peut mme, pour des algorithmes mal
ficels, saturer la mmoire entranant lerreur STORAGE_ERROR : EXCEPTION_STACK_OVERFLOW.
Do lintrt de pouvoir comparer la complexit de diffrents algorithmes pour ne
conserver que les plus efficaces, voire mme de prdire cette complexit. Prenons par
371

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


exemple lalgorithme dont nous parlions prcdemment (qui lit un tableau et teste
chacune de ses cases) : si le tableau a 100 cases, lalgorithme effectuera 100 tests ;
si le tableau 5000 cases, lalgorithme effectuera 5000 tests ; si le tableau a n cases,
lalgorithme effectuera n tests ! On dit que sa complexit est en O(n).

Lcriture O

a veut dire quoi ce zro ?

Ce nest pas un zro mais la lettre O ! Et O(n) se lit grand O de n . Cette notation mathmatique signifie que la complexit de lalgorithme est proportionnelle n
(le nombre dlments contenus dans le tableau). Autrement dit, la complexit vaut
grosso-modo n.
Cest pas grosso modo gal n ! Cest exactement gal n !

Prenons un autre exemple : un algorithme qui parcourt lui aussi un tableau n lments. Pour chaque case, il effectue deux tests : le nombre est-il positif ? Le nombre
est-il pair ? Combien va-t-il effectuer de tests ? La rponse est simple : deux fois plus
que le premier algorithme, cest dire 2 n. Autrement dit, il est deux fois plus
lent. Mais pour le mathmaticien ou linformaticien, ce facteur 2 na pas une importance aussi grande que cela, lalgorithme a toujours une complexit proportionnelle au
premier, et donc en O(n).
Mais alors tous les algorithmes ont une complexit en O(n) si cest comme
a ? !
Non, bien sr. Un facteur 2, 3, 20. . . peut tre considr comme ngligeable. En fait,
tout facteur constant peut tre considr comme ngligeable. Reprenons encore notre
algorithme : il parcourt toujours un tableau n lments, mais chaque case du
tableau, il reparcourt tout le tableau depuis le dbut pour savoir sil ny aurait pas une
autre case ayant la mme valeur. Combien va-t-il faire de tests ? Pour chaque case, il
doit faire n tests et comme il y a n cases, il devra faire en tout n n = n2 tests.
Le facteur nest cette fois pas constant ! On dira que cet algorithme a une complexit
en O(n2 ) et, vous vous en doutez, ce nest pas terrible du tout. Et pourtant, cest la
complexit de certains de nos algorithmes de tris ! :-
Donc, vous devez commencer comprendre quil est prfrable davoir une complexit
en O(n) quen O(n2 ). Malheureusement pour nous, en rgle gnral, il est impossible
datteindre une complexit en O(n) pour un algorithme de tri (sauf cas particulier
372

THORIE : COMPLEXIT DUN ALGORITHME


comme avec un tableau dj tri, et encore a dpend des algorithmes ). Un algorithme
de tri aura une complexit optimale quand celle-ci sera en O(n ln(n)).

Quelques fonctions mathmatiques

Quest-ce que cest encore que cette notation \ln(n) ?

Cest une fonction mathmatique que lon appelle le logarithme nprien. Les logarithmes sont les fonctions mathmatiques qui ont la croissance la plus faible (en
fait elles croissent trs trs vite au dbut puis ralentissent par la suite). Inversement,
les fonctions exponentielles sont celles qui ont la croissance la plus rapide (trs trs
lentes au dbut puis de plus en plus rapide).
Mon but ici nest pas de faire de vous des mathmaticiens, encore moins dexpliquer en
dtail les notions de fonction ou de grand O , mais pour que vous ayez une ide de
ces diffrentes fonctions, de leur croissance et surtout des complexits associes, allez
voir la figure 22.9.
Lgende : Complexit en O(en ) (e : exponentielle) Complexit en O(n2 ) Complexit en O(n log(n)) (log : logarithme) Complexit en O(n) Complexit
en O(log(n))
Plus lon va vers la droite, plus le nombre dlments trier, tester, modifier. . . augmente. Plus lon va vers le haut, plus le nombre doprations effectues augmente et
donc plus la quantit de mmoire ou le temps processeur ncessaire augmente. On voit
ainsi quune complexit en O(log(n)) ou O(ln(n)), cest peu ou prou pareil, serait parfaite (on parle de complexit logarithmique) : elle augmente beaucoup moins vite que
le nombre dlments trier ! Le rve. Sauf que a nexiste pas pour nos algorithmes
de tri, donc oubliez-la.
Une complexit en O(n) serait donc idale (on parle de complexit linaire). Cest
possible dans des cas trs particuliers : par exemple lalgorithme de tri par insertion
peut atteindre une complexit en O(n) lorsque le tableau est dj tri ou presque
tri, alors que les algorithmes de tri rapide ou fusion nen sont pas capables. Mais dans
la vie courante, il est rare que lon demande de trier des tableaux dj tris.
On comprend donc quatteindre une complexit en O(n ln(n)) est dj satisfaisant
(on parle de complexit linarithmique), on dit alors que la complexit est asymptotiquement optimale, bref, on ne peut pas vraiment faire mieux dans la majorit
des cas.
Une complexit en O(n2 ), dite complexit quadratique, est courante pour des algorithmes de tri simples (ou simplistes), mais trs mauvaise. Si au dpart elle ne diffre
pas beaucoup dune complexit linaire (en O(n)), elle finit par augmenter trs vite
mesure que le nombre dlments trier augmente. Donc les algorithmes de tri de
complexit quadratique seront rservs pour de petits tableaux.
373

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT

Figure 22.9 Croissance de plusieurs fonctions

374

MESURES DE COMPLEXIT DES ALGORITHMES


Enfin, la complexit exponentielle (en O(en )) est la pire chose qui puisse vous arriver.
La complexit augmente vite grand V, de faon exponentielle (logique me direz-vous).
Najoutez quun seul lment et vous aurez beaucoup plus doprations effectuer. Pour
un algorithme de tri, cest le signe que vous coder comme un cochon. Attention, les
jugements mis ici ne concernent que les algorithmes de tri. Pour certains problmes,
vous serez heureux davoir une complexit en seulement O(n2 ).

Mesures de complexit des algorithmes


Un algorithme pour mesurer la complexit. . . des algorithmes
Aprs cette section trs thorique, retournons la pratique. Nous allons rdiger un
programme qui va mesurer le temps dexcution des diffrents algorithmes avec des
tableaux de plus en plus grands. Ces temps seront enregistrs dans un fichier texte de
manire pouvoir ensuite transfrer les rsultats dans un tableur (Calc ou Excel par
exemple) et dresser un graphique. Nous en dduirons ainsi, de manire empirique, la
complexit des diffrents algorithmes. Pour ce faire, nous allons de nouveau faire appel
au package Ada.Calendar et effectuer toute une batterie de tests rptition avec
nos divers algorithmes. Le principe du programme sera le suivant :
1
2
3
4
5
6
7
8
9
10
11

POUR i ALLANT DE 1 A 100 REPETER


|
Cr er Tableau Al atoire de taille i * 100
|
Mesurer Tri_Selection ( Tableau )
|
Mesurer Tri_Insertion ( Tableau )
|
Mesurer Tri_Bulles ( Tableau )
|
Mesurer Tri_Rapide ( Tableau )
|
Mesurer Tri_Fusion ( Tableau )
|
Mesurer Tri_Tas ( Tableau )
|
Afficher " Tests effectu s pour taille " i * 100
|
incr menter i
FIN BOUCLE

La boucle devrait effectuer 100 tests, ce qui sera largement suffisant pour nos graphiques. Nous ne dpasserons pas des tableaux de taille 10 000 (les algorithmes lents
devraient dj mettre de 1 4 secondes pour effectuer leur tri), car au-del, vous pourriez vous lasser. Par exemple, il ma fallu 200s pour trier un tableau de taille 100 000
avec le tri par insertion et 743s avec le tri bulles alors quil ne faut mme pas une
demi-seconde aux algorithmes rapides pour faire ce travail. Si vous le souhaitez, vous
pourrez reprendre ce principe avec des tableaux plus importants pour les algorithmes
plus rapides (vous pouvez facilement aller jusqu des tableaux de taille 1 million).
Mais que vont faire ces procdures ?

Cest simple, elles doivent :


375

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


1. ouvrir un fichier texte ;
2. lire "lheure" ;
3. excuter lalgorithme de tri correspondant ;
4. lire nouveau "lheure" pour en dduire la dure dexcution ;
5. enregistrer la dure obtenue dans le fichier ;
6. fermer le fichier.
Cela impliquera de crer un type de pointeur sur les fonctions de tri. Voici le code
source du programme :
procedure sort is

1
2
3
4

type T_Ptr_Fonction is access function ( T : Vecteur ) return


Vecteur ;
type T_Tri is ( SELECTION , INSERTION , BULLES , FUSION , RAPIDE ,
TAS ) ;

procedure mesurer ( tri : T_Ptr_Fonction ; T : Vecteur ; choix :


T_Tri ) is
temps : time ;
duree : duration ;
U : Vecteur (T ' range ) ;
F : File_Type ;
begin
case choix is
when SELECTION = > open (F , Append_file , " ./ mesures / selection
. txt " ) ;
when INSERTION = > open (F , Append_file , " ./ mesures / insertion
. txt " ) ;
when BULLES
= > open (F , Append_file , " ./ mesures / bulles .
txt " ) ;
when FUSION
= > open (F , Append_file , " ./ mesures / fusion .
txt " ) ;
when RAPIDE
= > open (F , Append_file , " ./ mesures / rapide .
txt " ) ;
when TAS
= > open (F , Append_file , " ./ mesures / tas . txt " )
;
end case ;

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

temps := clock ;
U := Tri ( T ) ;
duree := clock - temps ;
put (F , float ( duree ) , Exp = > 0 ) ;
new_line ( F ) ;
close ( F ) ;
end mesurer ;

28
29
30

376

tri : T_Ptr_Fonction ;
F : File_Type ;

MESURES DE COMPLEXIT DES ALGORITHMES


31
32

begin

33
34
35
36
37
38
39

create (F , Append_file , " ./ mesures / selection . txt " ) ;


create (F , Append_file , " ./ mesures / insertion . txt " ) ;
create (F , Append_file , " ./ mesures / bulles . txt " ) ;
create (F , Append_file , " ./ mesures / fusion . txt " ) ;
create (F , Append_file , " ./ mesures / rapide . txt " ) ;
create (F , Append_file , " ./ mesures / tas . txt " ) ;

close ( F )
close ( F )
close ( F )
close ( F )
close ( F )
close ( F )

;
;
;
;
;
;

40
41
42
43
44
45

for i in 1 .. 100 loop


declare
T : Vecteur ( 1 .. i * 100 ) ;
begin
generate (T ,0 , i * 50 ) ;

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

tri := Tri_select ' access ;


mesurer ( tri ,T , SELECTION ) ;
tri := Tri_insertion ' access ;
mesurer ( tri ,T , INSERTION ) ;
tri := Tri_bulles ' access ;
mesurer ( tri ,T , BULLES ) ;
tri := Tri_fusion ' access ;
mesurer ( tri ,T , FUSION ) ;
tri := Tri_rapide ' access ;
mesurer ( tri ,T , RAPIDE ) ;
tri := Tri_tas ' access ;
mesurer ( tri ,T , TAS ) ;
end ;
put ( " Tests effectues pour la taille " ) ; put ( i * 100 ) ;
new_line ;
end loop ;
end sort ;

Lexcution de ce programme ncessitera quelques minutes. Ne vous inquitez


pas si sa vitesse dexcution ralentit au fur et mesure : cest normal puisque
les tableaux sont de plus en plus lourds.

Traiter nos rsultats


laide du programme prcdent, vous devez avoir obtenu six fichiers textes contenant
chacun 100 valeurs dcimales.
Et jen fais quoi maintenant ? Je vais pas tout comparer la main !

377

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


Bien sr que non. Ouvrez donc un tableur (tel Excel ou Calc dOpenOffice) et copiezy les rsultats obtenus. Slectionnez les valeurs obtenues puis cliquez sur Insertion >
Diagramme afin dobtenir une reprsentation graphique des rsultats obtenus. Je vous
prsente sur la figure 22.10 les rsultats obtenus avec mon vieux PC.

Figure 22.10 Temps de traitement des 6 algorithmes


Avant mme de comparer les performances de nos algorithmes, on peut remarquer une
certaine irrgularit : des pics et des creux apparaissent et semblent (en gnral) avoir
lieu pour tous les algorithmes. Ces pics et ces creux correspondent aux pires et aux
meilleurs cas possibles. En effet, si notre programme de gnration alatoire de tableaux vient crer un tableau quasi-tri, alors le temps mis par certains algorithmes
chutera drastiquement. Inversement, certaines configurations peuvent tre rellement
problmatiques certains algorithmes : prenez un tableau quasi-tri mais dans le mauvais ordre ! Ce genre de tableau est catastrophique pour un algorithme de tri bulles
qui devra effectuer un nombre de passages excessif : il suffit, pour sen convaincre, de
regarder le dernier pic de la courbe jaune ci-dessus (tri bulles) ; dailleurs ce pic est
nettement attnu sur la courbe orange (tri par insertion) et quasi inexistant sur la
courbe bleu (tri par slection).
Qui plus est, il apparat assez nettement que le temps de traitement des algorithmes
rapides (tri rapide, tri fusion et tri par tas) est quasiment ngligeable par rapport
celui des algorithmes lents (tri par insertion, tri par slection et tri bulles), au point
que seuls ces derniers apparaissent sur le graphique.
Pour y voir plus clair, je vous propose de traiter nos chiffres avec un peu plus de finesse :
nous allons distinguer les algorithmes lents (voir figure 22.11) des algorithmes rapides
(voir figure 22.12) et nous allons lisser nos rsultats. Par lisser, jentends modifier nos
rsultats pour ne plus tre gns par ces pics et ces creux et voir apparatre une courbe
plus agrable reprsentant la tendance de fond.
Lissage des rsultats par moyenne mobile : Pour ceux qui se demanderaient ce
quest ce lissage par moyenne mobile, voici une explication succincte (ce nest pas un
cours de statistiques non plus). Au lieu de traiter nos 100 nombres, nous allons crer une
nouvelle liste de valeurs. La premire valeur est obtenue en calculant la moyenne des
378

MESURES DE COMPLEXIT DES ALGORITHMES


20 premiers nombres (du 1er au 20me), la seconde est obtenu en calculant la moyenne
des nombres entre le 2nd et le 21me, la troisime valeur est obtenue en calculant la
moyenne des nombres entre le 3me et le 22me. . . La nouvelle liste de valeurs est ainsi
plus homogne mais aussi plus courte (il manquera 20 valeurs).
Algorithmes lents

Figure 22.11 Temps de traitement des algorithmes lents


Algorithmes rapides

Figure 22.12 Temps de traitement des algorithmes rapides


Ainsi retravaills, nos rsultats sont plus lisibles et on voit apparatre quen moyenne,
lalgorithme le plus efficace est le tri rapide, puis viennent le tri par tas et le tri fusion.
Mais mme le tri fusion reste, en moyenne, en dessous de 0,025 seconde pour un tableau
10 000 lments sur mon vieil ordinateur (son score est srement encore meilleur chez
vous), ce qui est trs trs loin des performances des trois autres algorithmes. Pour les
trois plus lents, on remarque que le pire dentre eux est, en moyenne, le tri bulles,
puis viennent le tri par insertion et le tri par slection (avec des carts de performance
379

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT


toutefois trs nets entre eux trois).
Le lissage par moyenne mobile nous permet galement de voir se dgager une tendance,
une courbe thorique dbarrasses des impurets (pire cas et meilleur cas). Si lon
considre les algorithmes lents, on distingue un dbut de courbe en U, mme si le U
semble trs cras par lalgorithme de tri par slection. Cela nous fait donc penser
une complexit en O(n2 ) o n est le nombre dlments du tableau.
Et pourquoi pas en O(e^n) ?

Prenez un tableau n lments que lon voudrait trier avec le pire algorithme : le tri
bulles. Dans le pire des cas, le tableau est tri en sens inverse et le plus petit lment
est donc situ la fin. Et comme le tri bulle ne dplace le plus petit lment que dun
seul cran chaque passage, il faudra donc effectuer n1 passages pour que ce minimum
soit sa place et le tableau tri. De plus, chaque itration, lalgorithme doit effectuer
n 1 comparaison (et au plus n 1 changes). Do un total de (n 1) (n 1)
comparaisons. Or, (n 1) (n 1) = n2 2 n + 1, donc la complexit est de
lordre de n2 . Dans le pire cas, lalgorithme de tri bulles a une complexit en O(n2 ).
Cette famille dalgorithme a donc une complexit quadratique et non exponentielle.
Considrons maintenant nos trois algorithmes rapides, leur temps dexcution est parfaitement ngligeable ct des trois prcdents, leurs courbes croissent peu : certaines
semblent presque tre des droites. Mais ne vous y trompez pas, leur complexit nest
srement pas linaire, ce serait trop beau. Elle est toutefois linarithmique cest--dire
en O(n ln(n)). Ces trois algorithmes sont donc asymptotiquement optimaux.
Diffrentes variantes de ces algorithmes existent. Les qualificatifs de lent ,
rapide , pire ou meilleur sont donc prendre avec des pincettes,
car certains peuvent tre trs nettement amliors ou sont trs utiles dans
des cas particuliers.
Par exemple, notre algorithme de tri fusion est un tri en place : nous navons pas crer
de second tableau pour effectuer notre tri afin dconomiser la mmoire, denre rare si
lon trie de trs grands tableaux (plusieurs centaines de millions de cases). Une variante
consiste ne pas leffectuer en place : on cre alors un second tableau et lopration
de fusion gagne en rapidit. Dans ce cas, lalgorithme de tri fusion acquirent une
complexit comparable celle du tri rapide en ralisant moins de comparaisons, mais
devient gourmande en mmoire.
De la mme manire, nous avons voqu le Combsort, qui est une amlioration du
tri bulles et qui permet notre pire algorithme de ctoyer les performances des
meilleurs . Encore un dernier exemple, le lent algorithme de tri par insertion atteint une complexit linaire en O(n) sur des tableaux quasi-tris alors que lalgorithme
de tri rapide garde sa complexit linarithmique en O(n ln(n)).

380

MESURES DE COMPLEXIT DES ALGORITHMES

En rsum
LAlgorithmique constitue une branche, peut-tre la plus complexe, des Mathmatiques et de lInformatique.
Vous devez donc vous interroger sur lefficacit des algorithmes que vous rdigez.
Effectuez des essais grandeur nature avec de grandes quantits de donnes et
comptez le nombre doprations et de tests ncessaires pour juger de la pertinence
de votre mthode.

381

CHAPITRE 22. ALGORITHMIQUE : TRI ET COMPLEXIT

382

Chapitre

23

Variables III : Gestion bas niveau des


donnes
Difficult :
Le chapitre que nous allons aborder est trs thorique et traite de diverses notions ayant
trait la mmoire de lordinateur : comment est-elle gre ? Comment les informations y
sont-elles reprsentes ? Etc. Nous allons passer le plus clair du chapitre ne pas parler de
programmation (trange me direz-vous pour un cours de programmation ) mais ce nest
que pour mieux revenir sur les types Integer, Character et Float, sur de nouveaux types
(Long_Long_Float, Long_Long_Integer. . . ) ou sur certaines erreurs vues au fil du cours.
Chaque partie de ce chapitre abordera la manire dont lordinateur reprsente les nombres
entiers, dcimaux ou les caractres en mmoire. Ce sera loccasion de parler binaire, hexadcimal, virgule flottante. . . avant de soulever certains problmes lis ces reprsentations
et den dduire quelques bonnes pratiques.

383

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES

Reprsentation des nombres entiers


Le code binaire
Nous allons essayer, au cours des sections suivantes, de comprendre comment lordinateur sy prend pour reprsenter les nombres entiers, les caractres ou les nombres
dcimaux, car cela a des implications sur la faon de programmer. En effet, depuis le
dbut, nous faisons comme sil tait normal que lordinateur sache compter de - 2 147
483 648 2 147 483 647, or je vous rappelle quun ordinateur nest jamais quun amas
de mtaux et de silicone. Alors comment peut-il compter ? Et pourquoi ne peut-il pas
compter au-del de 2 147 483 647 ? Que se passe-t-il si lon va au-del ? Comment aller
au del ? Reprenons tout la base.
Tout dabord, un ordinateur fonctionne llectricit (jusque l rien de rvolutionnaire)
et quavec cette lectricit nous avons deux tats possibles : teint ou allum.
Eh ! Tu me prends pour un idiot ou quoi ?

Bien sr que non, mais cest grce ces deux tats de llectricit que les Hommes ont
pu crer des machines aussi perfectionnes capables denvoyer des vidos lautre bout
du monde. En effet, chaque tat peut-tre associ un nombre.

Figure 23.1 O

Figure 23.2 1
Lorsque le courant passe, cela quivaut au chiffre 1 (voir figure 23.2), lorsquil ne passe
pas, ce chiffre est 0 (voir figure 23.1). De mme lorsquun accumulateur est charg, cela
signifie quun 1 est en mmoire, sil est dcharg, le chiffre 0 est enregistr en mmoire.
Nos ordinateurs ne disposent donc que de deux chiffres 0 et 1. Nous comptons quant
nous avec 10 chiffres (0, 1, 2. . . 8, 9) : on dit que nous comptons en base 10 ou
dcimale. Les ordinateurs comptent donc en base 2, aussi appel binaire. Les chiffres
1 et 0 sont appels bits, pour binary digits cest dire chiffre binaire.
384

REPRSENTATION DES NOMBRES ENTIERS

Mais, comment faire un 2 ou un 3 ?

Il faut procder en binaire comme nous le faisons en base 10 : lorsque lon compte
jusqu 9 et que lon a puis tous nos chiffres, que fait-on ? On remet le chiffre des
units 0 et on augmente la dizaine. Eh bien cest pareil pour le binaire : aprs 0 et 1,
nous aurons donc 10 puis 11. On peut ensuite ajouter une centaine et recommencer.
Voici une liste des premiers nombres en binaire (je vous invite essayer de la complter
par vous-mme, ce nest pas compliqu) :
Dcimal
0
1
2
3
4
5
6
7
8
9
10

Binaire
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010

Conversions entre dcimal et binaire


Vous aurez remarqu les rptitions : pour les bits des units , on crit un 0 puis un
1, puis un 0. . . pour les bits des dizaines , on crit deux 0 puis deux 1, puis deux
0. . . pour les bits des centaines , on crit quatre 0, puis quatre 1, puis quatre 0. . .
et ainsi de suite en doublant chaque fois le nombre de 0 ou de 1. Mais il y a une
ombre au tableau. Nous nallons pas faire un tableau allant jusqu 2 147 483 647 pour
pouvoir le convertir en binaire ! Nous devons trouver un moyen de convertir rapidement
un nombre binaire en dcimal et inversement.
Conversion binaire vers dcimal
Imaginons que nous recevions le signal de la figure 23.3.

Figure 23.3 Reprsentation dun message binaire


385

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


Ce message correspond au nombre binaire 10110 qui se dcompose en 0 units ,
1 dizaine , 1 centaine , 0 millier et 1 dizaine de millier . Ce qui scrirait
normalement :
10110 = (1 10000) + (0 1000) + (1 100) + (1 10) + (0 1)
10110 = (1 104 ) + (0 103 ) + (1 102 ) + (1 101 ) + (0 100 )
Sauf quen binaire, on ne compte pas avec dix chiffres mais avec seulement
deux !
Il ny a pas de dizaines ou de centaines mais plutt des deuxaines , quatraines , huitaines . . . Donc en binaire notre nombre correspond en fait :
10110 : (1 24 ) + (0 23 ) + (1 22 ) + (1 21 ) + (0 20 )
Ainsi, il suffit donc deffectuer ce calcul pour trouver la valeur en base 10 de 10110 et
de connatre les puissances de deux :
10110 : (1 16) + (0 8) + (1 4) + (1 2) + (0 1) = 16 + 4 + 2 = 22
Autre mthode pour effectuer la conversion, il suffit de dresser le tableau ci-dessous.
Dans la premire ligne, on crit les puissances de 2 : 1, 2, 4, 8, 16. . . Dans la seconde
ligne, on crit les bits :
16
1

8
0

4
1

2
1

1
0

Il y va ainsi une fois 2, plus une fois 4 plus une fois 16 (et 0 fois pour le reste), do 22 !
Conversion dcimal vers binaire
Imaginons, cette fois que nous souhaitions envoyer en message le nombre 13. Pour cela,
nous allons dresser le mme tableau que prcdemment :
8
-

4
-

2
-

1
-

Et posons-nous la question : dans 13 combien de fois 8 ? Une fois bien-sr ! Et il reste


5. Do le tableau suivant :
8
1

4
-

2
-

1
-

Maintenant, recommenons ! Dans 5 combien de fois 4 ? Une fois et il reste 1 !


8
1
386

4
1

2
-

1
-

REPRSENTATION DES NOMBRES ENTIERS


8
1

4
1

2
0

1
1

Dans 1 combien de fois 2. Zro fois, logique. Et dans 1 combien de fois 1 : 1 fois.
Donc le nombre 13 scrit 1101 en binaire. Le tour est jou.

Retour sur le langage Ada


Reprsentation des Integer en Ada
Vous avez compris le principe ? Si la rponse est encore non, je vous conseille de vous
entraner faire diverses conversions avant dattaquer la suite, car nous nen sommes
quau B-A-BA. En effet, les ordinateurs nenregistre pas les informations bits par bits,
mais plutt par paquets. Ces paquets sont appels octets, cest--dire des paquets de
8 bits (mme sil est possible de faire des paquets de 2, 4 ou 9 bits).
Pour enregistrer un Integer, le langage Ada utilise non pas un seul mais quatre octets,
cest--dire 4 8 = 32 bits ! Avec un seul bit, on peut coder deux nombres (0 et
1), avec deux bits on peut coder quatre nombres (0, 1, 2, 3), avec trois bits on peut
coder huit nombres. . . avec 32 bits, on peut ainsi coder 232 nombres, soit 4 294 967 296
nombres.
Mais la difficult ne sarrte pas l. En effet, le plus grand Integer nest pas 4 294 967
296, mais 2 147 483 647 (cest--dire presque la moiti). Pourquoi ? Tout simplement
parce quil faut partager ces 4 294 967 296 nombres en deux : les positifs et les ngatifs.
Comment les distinguer ?
La rponse est simple : tous les Integer commenant par un 0 seront positifs (exemple :
00101001 10011101 10100011 11111001) ; tous les integer commenant par un 1 seront
ngatifs (exemple : 10101001 10011101 10100011 11111001). Ce premier bit est appel
bit de poids fort. Facile hein ? Eh bien non ! Car il y a encore un problme : on se
retrouve avec deux zros !
00000000 00000000 00000000 00000000 : +0 10000000 00000000 00000000 00000000 :
0
Cest dommage et cela complique les oprations dun point de vue lectronique. Du
coup, il faut dcaler les nombres ngatifs dun cran : le nombre 10000000 00000000
00000000 00000000 correspondra non pas 0 mais 1. Le premier 1 signifie que
le nombre est ngatif, les 31 zros, si on les convertit en base 10, devraient donner 0,
mais avec ce dcalage, on doit ajouter 1, ce qui nous donne - 1 !
Consquence immdiate : lOverflow et les types long

OUF ! Cest un peu compliqu tout a. . . Et a sert quoi ?

387

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


Patience, je vous entrane peu peu dans les mandres de linformatique afin de mieux
revenir sur notre sujet : le langage Ada et la programmation. Cette faon de coder les
nombres entiers, qui nest pas spcifique au seul langage Ada, a deux consquences.
Tout dabord le plus grand Integer positif est 2 147 483 647 (231 1), quand le plus
petit Integer ngatif est - 2 147 483 648 (231 ) : il y a un dcalage de 1 puisque 0 est
cod comme un positif et pas comme un ngatif par la machine.
Deuxime consquence, nous navons que 232 nombres notre disposition, dont le plus
grand est seulement 231 1. Cest trs largement suffisant en temps normal, mais pour
des calculs plus importants, cela peut savrer un peu juste souvenez-vous seulement
des calculs de factorielles (chapitre 19). En effet, si vous dpassez ce maximum, que se
passera-t-il ? Eh bien plusieurs possibilits. Soient vous y allez faon dmnageur
en rdigeant un code faisant clairement appel des nombres trop importants :
1
2

n : = 2 ** 525 ;
p := Integer ' last + 1 ;

Et alors, le compilateur vous arrtera tout de suite par un doux message : value not
in range of type "Standard.Integer". Ou bien vous y allez de faon masque (
), le compilateur ne dtectant rien (une boucle itrative ou rcursive, un affichage du
type Put(Integerlast+1);). Vos nombres devenant trop grands, ils ne peuvent plus
tre cods sur seulement quatre octets, il y a ce que lon appelle un dpassement de
capacit (aussi appel overflow [jadore ce mot ]). Vous vous exposez alors deux
types derreurs : soit votre programme plante en affichant une jolie erreur du type
suivant.
raised CONSTRAINT_ERROR : ###. adb :# overflow check failed

Soit il ne dtecte pas lerreur (cest ce qui est arriv avec notre programme de calcul
de factorielles au chapitre 19) : le nombre positif devient trop grand, il dborde sur
le bit de poids fort normalement rserv au signe, ce qui explique que nos factorielles
devenaient trangement ngatives.
Comment remdier ce problme si lon souhaite par exemple connatre la
factorielle de 25 ?
Eh bien le langage Ada a tout prvu. Si les Integer ne suffisent plus, il est possible dutiliser dautres types : Long_Integer (avec le package Ada.Long_Integer_Text_IO) et
Long_Long_Integer (avec le package Ada.Long_Long_Integer_Text_IO). Les Long_Integer
sont cods sur 4 octets soit 32 bits comme les Integer. Pourquoi ? Eh bien cela dpend
de vos distributions en fait. Sur certaines plateformes, les Integer ne sont cods que sur
16 bits soit 2 octets do la ncessit de faire appel aux Long_Integer. Pour augmenter
vos capacits de calcul, utilisez donc plutt les Long_Long_Integer qui sont cods sur
8 octets soit 64 bits. Ils stendent donc de +9 223 372 036 854 775 807 -9 223 372
036 854 775 808, ce qui nous fait 232 fois plus de nombres que les Long_Integer.
388

REPRSENTATION DES NOMBRES ENTIERS


Il existe galement des types Short_Integer cods sur 2 octets
soit 16 bits (avec le package Ada.Short_Integer_Text_IO) et
Short_Short_Integer cods sur 1 octet soit 8 bits (avec le package
Ada.Short_Short_Integer_Text_IO). Le plus grand Short_Integer est donc
32 767 ; le plus grand des Short_Short_Integer est quant lui 127. Pratique
pour de petits calculs ne ncessitant pas dencombrer la mmoire.
Ce problme de portabilit du type Integer incitera les programmeurs expriments prfrer lutilisation de types personnels plutt que du type Integer
afin dviter tout problme.

Utiliser les bases


En Ada, il est possible de travailler non plus avec des nombres entiers en base 10 mais
avec des nombres binaires ou des nombres en base 3, 5, 8, 16. . . On parlera dOctal
pour la base 8 et dhexadcimal pour la base 16 (bases trs utilises en Informatique).
Par exemple, nous savons que le nombre binaire 101 correspond au nombre 5. Pour
cela, nous crirons :
N : Integer ;
BEGIN
N := 2 # 101 # ;
Put ( N ) ;

1
2
3
4

Le premier nombre (2) correspond la base. Le second (101) correspond au nombre en


lui-mme exprim dans la base indique : Attention ! En base 2, les chiffres ne doivent
pas dpasser 1. Chacun de ces nombres doit tre suivi dun dise (#). Le souci, cest
que ce programme affichera ceci :
5

Il faudra donc modifier notre code pour spcifier la base voulue : Put(N,Base => 2) ;
ou Put(N,5,2) ; (le 5 correspondant au nombre de chiffres que lon souhaite afficher).
Votre programme affichera alors votre nombre en base 2.
Il sera possible dcrire galement :
1
2

N := 8 # 27 # -- correspond au nombre 7 + 8 * 2 = 23
N := 16 # B # -- en hexad cimal , A correspond 10 et B 11

Il est mme possible dcrire un exposant la suite de notre nombre :


1

N := 2 # 101 # E3 ;

Notre variable N est donc donne en base 2, elle vaut donc 101 en binaire, soit 5. Mais
lexposant E3 signifie que lon doit multiplier 5 par 23 = 8. La variable N vaut donc
en ralit 40. Ou, autre faon de voir la chose : notre variable N ne vaut pas 101 (en
binaire bien sr) mais 101000 (il faut en ralit ajouter 3 zros).
389

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


Imposer la taille
Imaginez que vous souhaitiez raliser un programme manipulant des octets. Vous crez
un type modulaire T_Octet de la manire suivante :
1

Type T_Octet is mod 2 ** 8 ;

Vous aurez ainsi des nombres allant de 0000 0000 jusqu 1111 1111. Sauf que pour les
enregistrer, votre programme va tout de mme rquisitionner 4 octets. Du coup, loctet
0100 1000 sera enregsitr ainsi en mmoire : 00000000 00000000 00000000 01001000.
Ce qui nous fait une perte de trois octets. Minime me direz-vous. Sauf que si lon vient
crer des tableaux dun million de cases de types T_Octets, pour crer un nouveau
format de fichier image par exemple, nous perdrons 3 1000000 = 3 M o pour rien.
Il est donc judicieux dimposer certaines contraintes en utilisant lattribut Size de la
sorte :
1
2

Type T_Octet is mod 2 ** 8 ;


For T_Octet ' size use 8 ;

Ainsi, nos variables de type T_Octet, ne prendront rellement que 1 octet en mmoire.
Linstruction use est utilise pour tout autre chose que les packages ! Ada est
finalement trs souple, derrire son apparente rigueur.

Si vous crivez For T_Octetsize use 5 ; , le compilateur vous expliquera que 5 bits sont insuffisants : size for "T_Octet" too small, minimum
allowed is 8. Donc en cas de doute, vous pouvez toujours comptez sur GNAT.

Reprsentation du texte
Avant de nous attaquer la reprsentation des dcimaux et donc des Float, qui est
particulirement complexe, voyons rapidement celle du texte. Vous savez quun String
nest rien de plus quun tableau de characters et quun Unbounded_String est en fait
une liste de Characters. Mais comment sont reprsents ces characters en mmoire ?
Eh bien cest trs simple, lordinateur nenregistre pas des lettres mais des nombres.
Hein ? Et comment il retrouve les lettres alors ?

Grce une table de conversion. Lexemple le plus connu est ce que lon appelle la
table ASCII (American Standard Code for Information Interchange). Il sagit dune
table de correspondance entre des characters et le numro qui leur est attribu. Cette
390

REPRSENTATION DU TEXTE
vieille table (elle date de 1961) est certainement la plus connue et la plus utilise. La
voici en dtail sur la figure 23.4.

Figure 23.4 Table ASCII Standard


Comme vous pouvez le voir, cette table contient 128 caractres allant de A z en
passant par 7, # ou des caractres non imprimables comme la touche de suppression ou le retour la ligne. Vous pouvez dors et dj constater que seuls les caractres
anglo-saxons sont prsents dans cette table (pas de ou de ) ce qui est contraignant pour nous Franais. De plus, cette table ne comporte que 128 caractres, soit 27 .
Chaque caractre ne ncessite donc que 7 bits pour tre cod en mmoire, mme pas
un octet ! Heureusement, cette clbre table sest bien dveloppe depuis 1961, donnant
naissance de nombreuses autres tables. Le langage Ada nutilise donc pas la table
ASCII classique mais une sorte de table ASCII modifie et tendue. En utilisant le
8me bit, cette table permet dajouter 128 nouveaux caractres et donc de bnficier
de 28 = 256 caractres en tout.
Donc tu nous mens depuis le dbut ! Il est possible dutiliser le !

Non, je ne vous ai pas menti et vous ne pouvez pas tout fait utiliser les caractres
accentus comme les autres. Ainsi si vous crivez ceci :
1
2

...

C : Character

391

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


BEGIN
C := ' ' ; Put ( C ) ;
...

3
4
5

Vous aurez droit ce genre dhorreur :

Vous devrez donc crire les lignes ci-dessous si vous souhaitez obtenir un e minuscule
avec un accent circonflexe :
1
2
3
4
5

...

C : Character
BEGIN
C := Character ' val ( 136 ) ; Put ( C ) ;
...

Enfin, dernire remarque, les caractres imprims dans la console ne correspondent pas
toujours ceux imprims dans un fichier texte. Ainsi du caractre N230 : il vaut
dans un fichier texte mais dans la console. Je vous fournis en figure 23.5 la liste
des caractres tels quils apparaissent dans un fichier texte.

Figure 23.5 Caractres aprs criture dans un fichier


Et en figure 23.6 tel quils apparaissent dans la console.
Vous remarquerez les ressemblances avec la table ASCII, mais aussi les diffrences
quant aux caractres spciaux. Le character n12 correspond au saut de page et le
n9 la tabulation.
392

REPRSENTATION DES NOMBRES DCIMAUX EN VIRGULE FLOTTANTE

Figure 23.6 Caractres aprs criture dans la console

Reprsentation des nombres dcimaux en virgule flottante


Nous arrivons enfin la partie la plus complexe. Sil vous parat encore compliqu de
passer du binaire au dcimal avec les nombres entiers, je vous conseille de relire la souspartie concerne avant dattaquer celle-ci car la reprsentation des float est un peu tire
par les cheveux. Et vous devrez avoir une notion des puissances de 10 pour comprendre
ce qui suit (je ne ferai pas de rappels, il ne sagit pas dun cours de Mathmatiques).

Reprsentation des float


Reprsentation en base 10
Avant toute chose, nous allons voir comment crire diffremment le nombre 9876, 0123
dans notre base 10. Une convention, appele criture scientifique, consiste ncrire
quun seul chiffre, diffrent de 0, avant la virgule : 9, 8760123. Mais pour compenser
cette modification, il faut multiplier le rsultat obtenu par 1000 pour indiquer quen
ralit il faut dcaler la virgule de trois chiffres vers la droite. Ainsi, 9876, 0123
scrira 9, 8760123 103 ou mme 9, 8760123 E3.
Eh ! a me rappelle les critures bizarres quon obtient quand on utilise Put(
) avec des float !
Eh oui, par dfaut, cest ainsi que sont reprsents les nombres dcimaux par lordinateur, le E3 signifiant 103 . Il suffit donc simplement de modifier lexposant (le 3) pour
393

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


dcaler la virgule vers la gauche ou la droite : on parle ainsi de virgule flottante.
Reprsentation en binaire
Cette criture en virgule flottante prend tout son sens en binaire : pour enregistrer le
nombre 1001, 1 il faudra enregistrer en ralit le nombre 1, 0011E3. Mais comme
le chiffre avant la virgule ne peut pas tre gale 0, ce ne peut tre quun 1 en binaire !
Autrement dit, il faudra enregistrer trois nombres :
le signe : 1 (le nombre est ngatif)
lexposant : 11 (pour E3, rappelons que 3 scrit 11 en binaire)
la mantisse : 0011 (on ne conserve pas le 1 avant la virgule, cest inutile)
Do une criture en mmoire qui ressemblerait cela :
Signe
1

Exposant
11

Mantisse
0011

Pourquoi ne pas avoir enregistr seulement deux nombres ? La partie entire


et la partie dcimale ?

Cette reprsentation existe, cest la reprsentation dite en virgule fixe. Mais


cette reprsentation rigide ne permet pas de reprsenter autant de nombres
que la reprsentation en virgule flottante, bien plus souple. En effet, avec
4 octets, le nombre binaire 00000000 00000000 , 00000000 00000000 1 ne
pourrait pas tre reprsent en virgule fixe, le 1 tant situ trop loin . En
revanche, cela ne pose aucun soucis en virgule flottante, puisquil suffira de
lcrire ainsi : 1,0E(-17).
En simple prcision, les nombres virgule flottante sont reprsents sur 4 octets. Comment se rpartissent les diffrents bits ? Cest simple.
Dabord 1 bit pour le signe,
puis 8 bits pour lexposant,
enfin le reste (soit 23 bits) pour la mantisse.
Sauf que lexposant, nous venons de le voir peut tre ngatif et quil ne va pas tre
simple de grer des exposants ngatifs comme nous le faisions avec les Integer. Pas
question davoir un bit pour le signe de lexposant et 7 pour sa valeur absolue ! Le
codage est un peu plus compliqu : on va dcaler. Lexposant pourra aller de -127
+128. Si lexposant vaut 0000 0000, cela correspondra au plus petit des exposants :
127. Il y a un dcalage de 127 par rapport lencodage normal des Integer.
394

REPRSENTATION DES NOMBRES DCIMAUX EN VIRGULE FLOTTANTE


Un exemple
Nous voulons convertir le nombre 6,75 en binaire et en virgule flottante. Tout dabord,
convertissons-le en binaire :
6, 75 = (1 22 ) + (1 21 ) + (0 20 ) + (1 21 ) + (1 22 )
Donc notre nombre scrira 110,11. Passons en criture avec virgule flottante : 1, 1011 22
ou 1, 1011E2. Rappelons quil faut dcaler notre exposant de 127 : 2 + 127 = 129. Et
comme 129 scrit 10000001 en binaire, cela nous donnera :
Signe
0

Exposant
1000 0001

Mantisse
1011000 00000000 00000000

Cas particuliers

Ton raisonnement est bien gentil, mais le nombre 0 na aucun chiffre diffrent
de 0 avant la virgule !
En effet, ce raisonnement nest valable que pour des nombres dont lexposant nest
ni 0000 0000 ni 1111 1111, cest dire si les nombres sont normaliss. Dans le cas
contraire, on associe des valeurs particulires :
Exposant 0 Mantisse 0 : le rsultat est 0.
Exposant 0 : on tombe dans le cas des nombres dnormaliss, cest dire dont le
chiffre avant la virgule est 0 (nous nentrerons pas davantage dans le dtail pour
ce cours)
Exposant 1111 1111 Mantisse 0 : le rsultat est linfini.
Exposant 1111 1111 : le rsultat nest pas un nombre (Not A Number : NaN).
Remarquez que du coup, pour les flottants, il est possible davoir +0 et 0 ! Bien. Vous
connaissez maintenant les bases du codage des nombres flottants (pour plus de prcisions, voir le tutoriel de Mewtow http://www.fr.openclassrooms.com/informatique/
cours/nombres-flottants-et-processeurs), il est grand temps de voir les implications que cela peut avoir en Ada.

Implications sur le langage Ada


De nouveaux types pour gagner en prcision
Bien entendu, vous devez vous en doutez, il existe un type Short_Float (mais pas
short_short_float), et il est possible de travailler avec des nombres virgule flottante
de plus grande prcision laide des types Long_Float (cod sur 8 octets au lieu de
4, cest le format double prcision) et Long_Long_Float (cod sur 16 octets). Vous
395

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


serez alors amens utiliser les packages Ada.Short_Float_IO, Ada.Long_Float_IO
et Ada.Long_Long_Float_IO (mais ai-je encore besoin de le spcifier ?).
Problmes lis aux arrondis
Les nombres flottants posent toutefois quelques soucis techniques. En effet, prenons
un nombre de type float. Il dispose de 23 bits pour coder sa mantisse, plus un bit
fantme puisque le chiffre avant la virgule na pas besoin dtre cod. Donc il est
possible de coder un nombre de 24 chiffres, mais pas de 25 chiffres. Par exemple 225 + 1,
en binaire, correspond un nombre de 26 chiffres : le premier et le dernier vaudront 1,
les autres 0. Essayons le code suivant :
1

...

x : Float ;
Begin
x := 2 . 0 ** 25 ;
put ( integer ( x ) , base = > 2 ) ; new_line ;
criture en binaire (c ' est un entier )
put (x , exp = > 0 ) ; new_line ;
le nombre flottant

2
3
4
5
6

-- on affiche son
-- et on affiche

7
8
9
10
11

...

x := x + 1 . 0 ;
put ( integer ( x ) , base = > 2 ) ; new_line ;
pour l ' affichage
put (x , exp = > 0 ) ;

-- on incr mente
-- et rebelote

Que nous renvoie le programme ? Deux fois le mme rsultat ! trange. Pourtant nous
avons bien incrment notre variable x ! Je vous ai dj donn la rponse dans le
prcdent paragraphe : le nombre de bits ncessaire pour enregistrer les chiffres (on
parle de chiffres significatifs) est limit 23 (24 en comptant le bit avant la virgule). Si
nous dpassons ces capacits, il ne se produit pas de plantage comme avec les Integer,
mais le processeur renvoie un rsultat arrondi do une perte de prcision.
De mme, si vous testez le code ci-dessous, vous devriez avoir une drle de surprise.
Petite explication sur ce code : on effectue une boucle avec deux compteurs. Lun est
un flottant que lon augmente de 0.1 chaque itration, lautre est un integer que lon
incrmente. Nous devrions donc avoir 1001 itrations et la fin, n vaudra 1001 et x
vaudra 100.1. Logique, non ? Eh bien testez donc ce code.
1

...

2
3
4
5
6
7

x := 0 . 0 ;
n := 0 ;
while x < 100 . 0 loop
x := x + 0 . 1 ;
n := n + 1 ;
end loop ;

8
9
10

396

Put ( " x vaut " ) ; put (x , exp = > 0 ) ; new_line ;


Put_line ( " n vaut " & Integer ' image ( n ) ) ;

REPRSENTATION DES NOMBRES DCIMAUX EN VIRGULE FLOTTANTE


Alors, que vaut n ? 1001, tout va bien. Mais que vaut x ? 100.09904 ! ! ! Qua-t-il bien
pu se passer encore ? Pour comprendre, il faut savoir que le nombre dcimal 0.1 scrit
approximativement 0.000 1100 1100 1100 1100 1100 en binaire. . . Cette criture est
infinie, donc lordinateur doit effectuer un arrondi. Si la conversion dun nombre entier
en binaire tombe toujours juste, la conversion dun nombre dcimal quant elle peut
poser problme car lcriture peut tre potentiellement infinie. Et mme chose lorsque
lon transforme le nombre binaire en nombre dcimal, on ne retrouve pas 0.1 mais une
valeur trs proche ! Cela ne pose pas de vritable souci lorsque lon se contente deffectuer une addition. Mais lorsque lon effectue de nombreuses additions, ce problme
darrondi commence se voir. La puissance des ordinateurs et la taille du type float
(4 octets) est largement suffisante pour pallier dans la plupart des cas ce problme
darrondi en fournissant une prcision bien suprieure nos besoins, mais cela peut
toutefois tre problmatique dans des cas o la prcision est de mise.
Car si cette perte de prcision peut paratre minime, elle peut parfois avoir de lourdes
consquences : imaginez que votre programme calcule la vitesse quune fuse doive
adopter pour se poser sans encombre sur Mars en effectuant pour cela des milliers
de boucles ou quil serve la comptabilit dune grande banque amricaine grant des
millions doprations la seconde. Une lgre erreur darrondi qui viendrait se rpter
des milliers ou des millions de fois pourrait terme engendrer la destruction de votre
fuse ou une crise financire mondiale (comment a ? Vous trouvez que jexagre ? ).
Plus simplement, ces problmes darrondis doivent vous amener une conclusion : les
types flottants ne doivent pas tre utiliss la lgre ! Partout o les types entiers sont
utilisables, prfrez-les aux flottants (vous comprenez maintenant pourquoi depuis le
dbut, je rechigne utiliser le type Float). Nutilisez pas de flottant comme compteur
dans une boucle car les problmes darrondis pourraient bien vous jouer des tours.
Delta et Digits
Toujours pour dcouvrir les limites du type float, testez le code ci-dessous :
1
2
3
4
5
6

...

x : float ;
begin
x := 12 . 3456789 ;
put (x , exp = > 0 ) ;
...

Votre programme devrait afficher la valeur de x, or il naffiche que :


12.34568

Eh oui, votre type float ne prend pas tous les chiffres ! Il peut manipuler de trs grands
ou de trs petits nombres, mais ceux-ci ne doivent pas, en base 10, excder 7 chiffres
significatifs, sinon . . . il arrondit ! Ce nest pas trs grave, mais cela constitue encore
une fois une perte de prcision. Alors certes vous pourriez utiliser le type Long_Float
qui gre 16 chiffres significatifs, ou long_long_Float qui en gre 20 ! Mais il vous est
397

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES


galement possible en Ada de dfinir votre propre type flottant ainsi que sa prcision.
Exemple :
1
2

type MyFloat is digits 9 ;


x : MyFloat := 12 . 3456789 ;

Le mot digits indiquera que ce type MyFloat grera au moins 9 chiffres significatifs !
Pourquoi au moins ? Eh bien parce quen crant ainsi un type Myfloat, nous crons
un type de nombre flottant particulier utilisant un nombre doctet particulier. Ici, notre
type Myfloat sera cod dans votre ordinateur de sorte que sa mantisse utilise 53 bits.
Ce type Myfloat pourra en fait grer des nombres ayant jusqu 15 chiffres significatifs,
mais pas au-del.
Pour savoir le nombre de bits utiliss pour la mantisse de votre type, vous
naurez qu crire Put(MyfloatMachine_Mantissa) ;
Enfin, nous avons beaucoup parl des nombres en virgule flottante, car cest le format
le plus utilis pour reprsenter les nombres dcimaux. Mais comme voqu plus haut,
il existe galement des nombres en virgule fixe. Lide est simple : le nombre de chiffres
aprs la virgule est fix ds le dpart et ne bouge plus. Inconvnient : si vous avez fix
2 chiffres aprs la virgule, vous pourrez oubliez les calculs avec 0.00003 ! Mais ces
nombres ont lavantage de permettre des calculs plus rapides pour lordinateur. Vous
en avez dj rencontr : les types Duration ou Time sont des types virgule fixe (donc
non flottante). Pour crer un type de nombre en virgule fixe vous procderez ainsi :
1
2

type T_Prix is delta 0 . 01 ;


Prix_CD : T_Prix := 15 . 99 ;

Le mot cl delta indique lcart minimal pouvant sparer deux nombres en virgule fixe
et donc le nombre de chiffres aprs la virgule (2 pas plus) : ici, notre prix de 1599,
sil devait augmenter, passerait 15, 99 + 0, 01 = 16 . Une prcision toutefois : il est
ncessaire de combiner delta avec digits ou range ! Exemple :
1

type T_Livret_A is delta 0 . 01 digits 6 ;

Toute variable de type T_Livret_A aura deux chiffres aprs la virgule et 6 chiffres en
tout. Sa valeur maximale sera donc 9999.99. Une autre rdaction serait :
1

type T_Livret_A is delta 0 . 01 range 0 . 0 .. 9999 . 99 ;

Toutefois, gardez lesprit que malgr leurs limites (arrondis, overflow . . .) les nombres
en virgule flottante demeurent bien plus souples que les nombres en virgule fixe. Ces
derniers ne seront utiliss que dans des cas prcis (temps, argent . . .) o le nombre de
chiffres aprs la virgule est connu lavance.
398

REPRSENTATION DES NOMBRES DCIMAUX EN VIRGULE FLOTTANTE

En rsum
Lencodage du type Integer peut dpendre de la machine utilise. Cest pourquoi
il est parfois utile de dfinir son propre type entier ou de faire appel au type
Long_Long_Integer afin de vous mettre labri des problmes doverflow.
Le type Character gre 256 symboles numrots de 0 255, dont certains ne
sont pas imprimables. Les caractres latins sont accessibles via leur position, en
utilisant les attributs characterpos() et characterval().
Le type Float et autres types virgule flottante, offrent une grande souplesse
mais peuvent tre soumis des problmes darrondis.
Les nombres virgule fixe sont plus faciles comprendre mais impliquent beaucoup de restrictions. Rservez-les quelques types spcifiques dont vous connaissez par avance le nombre maximal de chiffres avant et aprs la virgule.

399

CHAPITRE 23. VARIABLES III : GESTION BAS NIVEAU DES DONNES

400

Chapitre

24

La programmation modulaire II :
Encapsulation
Difficult :
Avec ce chapitre, nous allons entrer de plein pied dans la Programmation Oriente Objet
(souvent appele par son doux acronyme de POO). Lobjectif de ce chapitre est double :
expliquer ce que sont les notions de POO, de classes. . . ;
fournir une premire application de ce concept de POO en abordant lencapsulation.
Cette notion de Programmation Oriente Objet est une notion complique que nous allons
prendre le temps daborder au travers de ce chapitre et des quatre suivants (Eh oui, cinq
chapitres, ce nest pas de trop). Alors, prenez un grand bol dair car avec la POO nous
allons faire une longue plonge dans une nouvelle faon de programmer de dutiliser nos
packages, en commenant par lencapsulation.

401

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION

Quest-ce quun objet ?


Une premire approche
Au cours de la partie III, nous avons manipul des types plus complexes : tableaux,
pointeurs, types structurs. . . nous nous sommes ainsi approch de cette notion dobjet
en manipulant ce qui ntait dj plus de simples variables. Je parlais ainsi de variables
composites, mais il ne sagissait pas encore de vritables objets.
Mais alors, cest quoi un objet ? Si cest encore plus compliqu que les pointeurs et les types structurs jabandonne !
Vous vous souvenez de lpoque o nous avions ralis notre TP sur le craps ? la suite
de cela, je vous avais dit quil aurait t plus judicieux que nos nombreuses variables
soient toutes rassembles en un seul et mme contenant, comme la figure 24.1.

Figure 24.1 Rassemblement de plusieurs variables en une seule


Avec nos connaissances actuelles, nous savons que ces diffrentes variables pourraient
tre rassembles en un seul type structur de la manire suivante :
1
2
3
4
5

type T_MainJoueur is record


de1 , de2 : natural range 1 .. 6 ;
hasard
: generator ;
somme
: natural range 2 .. 12 ;
end record ;

Voire mieux encore, nous pourrions tout rassembler (le type T_MainJoueur, les variables, les fonctions et les procdures associes) dans un seul package appel P_MainJoueur !
la manire de la figure 24.2.
Ainsi, nous aurions au sein dun mme paquetage la fois les variables, les types et les
programmes ncessaires son utilisation (gnration des ds, lecture de leur somme. . .).
402

QUEST-CE QUUN OBJET ?

Figure 24.2 Rassemblement en un seul package

Bref, aujourdhui, vous ne procderiez plus comme lpoque o tout tait en vrac dans
le programme principal.
En effet, jadmets avoir fait dnormes progrs en trs peu de temps, en
primaire dj mes instituteurs. . . Eh ! Mais tu drives l ! Tu ne rponds pas
ma question : CEST QUOI UN OBJET ? ! ?
Un objet ? Cest justement cela : un type de donne muni dune structure comprenant
lensemble des outils ncessaires la reprsentation dun objet rel ou dune notion.
Vous ne devez plus voir vos packages simplement comme une bibliothque permettant de stocker tout-et-nimporte-quoi-du-moment-que-a-ne-me-gne-plus-dans-monprogramme-principal mais plutt comme une entit part entire, porteuse dun
sens et dune logique propre. Ainsi, pour constituer un objet correspondant la main
dun joueur, notre package P_MainJoueur devrait contenir un type T_Main_Joueur
(englobant les ds et tous les paramtres ncessaires) ainsi que les outils pour lire ou
modifier cette main. . . mais rien qui ne concerne son score par exemple !
Ainsi, concevoir un objet cest non seulement concevoir un type particulier mais galement tous les outils ncessaires sa manipulation par un tiers utilisateur et une
structure pour englober tout cela.
Jinsiste sur le mot ncessaire ! Prenez par exemple un programmeur qui a conu un
type T_Fenetre avec tout plein de procdures et de fonctions pour faire une jolie fe403

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION


ntre sous Windows, Linux ou Mac. Il souhaite faire partager son travail au plus grand
nombre : il cre donc un package P_Fenetre quil va diffuser. Ce package P_Fenetre na
pas besoin de contenir des types T_Image_3D, ce serait hors sujet. Mais lutilisateur at-il besoin daccder aux procdures Creer_Barre_De_Titre, Creer_Bouton_Fermer,
Creer_Bouton_Agrandir. . . ? Non, il na besoin que dune procedure Creer_Fenetre !
La plupart des petits programmes crs par le programmeur nintresseront pas lutilisateur final : il faudra donc permettre laccs pour lutilisateur certaines fonctionnalits,
mais pas toutes !
Mathieu Nebra en parle dans son cours sur le C++. Lillustration quil donne est trs parlante et peut vous fournir une seconde explication si vous avez encore du mal comprendre de
quoi il retourne. Cliquez ici http://www.fr.openclassrooms.
com/informatique/cours/programmez-avec-le-langage-c/
introduction-la-verite-sur-les-strings-enfin-devoilee\#ss\
_part\_1 pour suivre son explication (partie 1 seulement).

Posons le vocabulaire
Pour faire de la programmation oriente objet, il faut que nous tablissions un vocabulaire prcis. Le premier point claircir est la diffrence entre les termes classe
et objet . La classe correspond notre type (nous nuancerons cette assertion plus
tard) : elle doit toutefois tre munie dun package dans lequel sont dcrits le type et les
fonctions et procdures pour le manipuler. partir de ce schma , il est possible de
crer plusieurs variables que lon nommera objets : on dit alors que lon instancie
des objets partir dune classe (de la mme manire que lon dclare des variables dun
certain type).
Cette notion de classe est encore incomplte. Elle sera approfondie lors des
chapitres suivants.
Autre point de vocabulaire, les procdures et fonctions lies une classe et sappliquant
notre objet sont appeles mthodes. Pour mettre les choses au clair, reprenons
lexemple de notre jeu de craps en image la figure 24.3.
Enfin, certaines catgories de mthodes portent un nom spcifique selon le rle quelles
jouent : constructeur pour initialiser les objets, destructeur pour les dtruire proprement (en librant la mmoire dynamique par exemple, nous reverrons cela dans le
chapitre sur la finalisation), accesseur, modifieur pour lire ou modifier un attribut,
itrateur pour appliquer une modification tous les lments dune pile par exemple. . .
404

QUEST-CE QUUN OBJET ?

Figure 24.3 Classe et objets

405

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION

De nouvelles contraintes
On attache la Programmation Oriente Objet, non seulement la notion de classe
englobant la fois le type et les programmes associs, mais galement divers principes
comme :
Lencapsulation : cest lune des proprits fondamentales de la POO.
Les informations contenues dans lobjet ne doivent pas tre bidouillables par
lutilisateur. Elles doivent tre protges et manipules uniquement laide des
mthodes fournies par le package. Si lon prend lexemple dune voiture : le
constructeur de la voiture fait en sorte que le conducteur nait pas besoin dtre
mcanicien pour lutiliser. Il cre un volant, des siges et des pdales pour viter
que le conducteur ne vienne mettre son nez dans le moteur (ce qui serait un
risque). Nous avons commenc voir cette notion avec la sparation des spcifications et du corps des packages et nous allons y ajouter la touche finale au
cours de ce chapitre avec la privatisation.
La gnricit : nous lavons rgulirement voque au cours de ce tutoriel. Elle
consiste raliser des packages applicables divers types de donnes. Il en tait
ainsi pour les Doubly_Linked_Lists ou les Vectors qui pouvaient contenir des
float, des integer ou des types personnaliss. Nous verrons cette notion plus en
profondeur lors du prochain chapitre sur la programmation modulaire.
Lhritage et la drivation : deuxime notion fondamentale de la conception
oriente objet. Plus complexe, elle sera traite aprs la gnricit.

Un package. . . priv
Partie publique / partie prive
" Ici cest un club priv, on nentre pas ! "
Vous avez peut-tre dj entendu cette tirade ? Eh bien nous allons pouvoir nous venger aujourdhui en faisant la mme chose avec nos packages. Bon certes, personne ne
comptait vraiment venir danser avec nous devant le tuto. . . Disons quil sagit de rendre
une partie de notre code inaccessible . Petite explication : lorsque nous avons rdig
nos packages P_Pile et P_File, je vous avais bien prcis que nous voulions proposer
lutilisateur ou au programmeur final toute une gamme doutils qui lui permettraient
de ne pas avoir se soucier de la structure des types T_File et T_Pile. Ainsi, pour
accder au deuxime lment dune pile, il lui suffit dcrire :
1

...

2
3
4
5
6
7
8

406

n : integer ;
P : T_Pile
BEGIN
...
-- cr ation de la pile
pop (P , n ) ;
put ( first ( P ) ) ;

UN PACKAGE. . . PRIV
La logique est simple : il dpile le premier lment puis affiche le premier lment de
la sous-pile restante. Simple et efficace ! Cest dailleurs pour cela que nous avions cr
toutes ces primitives. Mais en ralit, rien nempche notre programmeur dcrire le
code suivant.
1
2
3
4
5
6

...

P : T_Pile
BEGIN
...
-- cr ation de la pile
put ( P . suivant . valeur ) ;

Et l, cest plutt embtant non ? Car nous avons rdig des procdures et des fonctions
justement dans le but quil ne puisse pas utiliser lcriture avec pointeurs. Cette criture
prsente des risques, nous le savons et avons pu le constater en testant nos packages
P_Pile et P_File ! Alors il est hors de question que nous ayons fait tous ces efforts pour
fournir un code sr et quun olibrius samuse finalement dtourner notre travail.
Si cela na pas de grande incidence notre chelle, cela peut poser des problmes de
stabilit et de scurit du code dans de plus grands projets ncessitant la coopration
de nombreuses personnes.
Bah oui mais on ne peut pas empcher les gens de faire nimporte quoi ? :-

Eh bien si, justement ! La solution consiste rendre la structure de nos types T_Pile,
T_File et T_Cellule illisible en dehors de leurs propres packages. Actuellement, nimporte qui peut la manipuler sa guise simplement en faisant appel au bon package.
On dit que nos types sont publics. Par dfaut, tout type, variable, sous-programme. . .
dclar dans un package est public.
Pour restreindre laccs un type, une variable. . ., il faut donc le dclarer comme priv
(PRIVATE en Ada). Pour raliser cela, vous allez devoir changer votre faon de voir vos
packages ou tout du moins les spcifications. Vous les voyiez jusqualors comme une
sorte de grande bote o lon stockait tout et nimporte quoi, et nimporte comment.
Vous allez devoir apprendre que cette grande bote est en fait compartimente (voir
figure 24.4).
Comme lindique le schma ci-dessus, le premier compartiment, celui que vous utilisiez,
correspond la partie publique du package. Il est introduit par le mot IS. Le second
compartiment correspond la partie prive et est introduit par le mot PRIVATE, mais
comme cette partie tait jusqualors vide, nous navions pas la renseigner. Reprenons
le code du fichier P_Pile.ads pour lui ajouter une partie prive :
1

PACKAGE P_Pile IS

2
3

--

PARTIE PUBLIQUE : NOS PRIMITIVES

4
5
6

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;


PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;

407

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION

Figure 24.4 Organisation des packages

FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;


FUNCTION Length ( P : T_Pile ) RETURN Integer ;
FUNCTION First ( P : T_Pile ) RETURN Integer ;

7
8
9
10

PRIVATE

11
12

--

13

PARTE PRIV E : NOS TYPES

14
15
16
17
18
19
20
21
22
23

TYPE T_Cellule ;
TYPE T_Pile IS ACCESS ALL T_Cellule ;
TYPE T_Cellule IS
RECORD
Valeur : Integer ;
Index
: Integer ;
Suivant : T_Pile ;
END RECORD ;
END P_Pile ;

Les utilisateurs doivent garder laccs aux primitives, elles ont t faites pour eux tout
de mme. En revanche, ils nont pas avoir accs la structure de nos types, donc
nous les rendons privs. Et voil ! Le tour est jou ! Pas besoin de modifier le corps de
notre package, tout se fait dans les spcifications.
noter quil est possible de dclarer des fonctions ou procdures dans la
partie PRIVATE. Ces programmes ne pourront ds lors tre utiliss que par les
programmes du corps du package. Plus dexplications seront fournies ci-aprs.

408

UN PACKAGE. . . PRIV

Visibilit
Mais a marche pas tes affaires ! la compilation, GNAT mindique
"T_Pile" is undefined (more references follow).
Ah oui. . . cest embtant. Cela mamne vous parler de la visibilit du code. Lorsque
vous crez un programme faisant appel P_Pile (avec linstruction WITH P_Pile ; ),
ce programme na pas accs lintgralit du package ! Il aura simplement accs aux
spcifications et, plus prcisment encore, aux spcifications publiques ! Toute la partie
prive et le corps du package demeurent invisibles, comme lindique le schma 26.5.

Figure 24.5 Visibilit dun package


Une flche indique quune partie a accs ce qui est crit dans une autre ; On voit
ainsi que seul le corps du package sait comment est constitue la partie prive puisquil
a pleine visibilit sur lensemble du fichier ads. Donc seul le BODY peut manipuler
librement ce qui est dans la partie PRIVATE.
Comment peut-on alors crer un type T_Pile si ce type est invisible ?

409

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION


Lutilisateur doit pouvoir voir ce type sans pour autant pouvoir le lire. Voir notre
type signifie que lutilisateur peut dclarer des objets de type T_Pile et utiliser des
programmes utilisant eux-mme ce type. En revanche, il ne doit pas en connatre la
structure, cest ce que jappelle ne pas pouvoir lire notre type . Comment faire cela ?
Eh bien, nous devons indiquer dans la partie publique que nous disposons de types
privs. Voici donc le code du package P_Pile tel quil doit tre crit (concentrez-vous
sur la ligne 5) :
PACKAGE P_Pile IS

1
2

--

PARTIE PUBLIQUE

TYPE T_Pile IS PRIVATE ;


type T_Pile

5
6

-- On indique qu ' il y a un
-- mais que celui - ci est priv

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;


PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;
FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;
FUNCTION Length ( P : T_Pile ) RETURN Integer ;
FUNCTION First ( P : T_Pile ) RETURN Integer ;

8
9
10
11
12
13

PRIVATE

14
15

--

16

PARTE PRIV E

17
18
19
20
21
22
23
24
25
26

TYPE T_Cellule ;
TYPE T_Pile IS ACCESS ALL T_Cellule ;
est le type T_Pile
TYPE T_Cellule IS
RECORD
Valeur : Integer ;
Index
: Integer ;
Suivant : T_Pile ;
END RECORD ;
END P_Pile ;

-- on d crit ce qu '

Et cette fois, cest fini : votre type T_Pile est visible mais plus lisible, vous avez cr
un vritable type priv en encapsulant le type T_Cellule.

Un package priv et limit


Que faire avec un type PRIVATE ?
Nous avons dit que le type T_Pile tait priv et donc quil tait visible mais non lisible.
Par consquent, il est impossible de connatre la structure dune pile P et donc dcrire :
1

P . valeur := 15 ;

410

UN PACKAGE PRIV ET LIMIT


En revanche, il est possible de dclarer des variables de type T_Pile, daffecter une pile
une autre ou encore de tester lgalit ou lingalit de deux piles :
1
2
3
4
5
6
7

-- TOUTES LES OP RATIONS CI - DESSOUS SONT TH ORIQUEMENT


POSSIBLES
P1 , P2 , P3 : T_Pile
--d claration
BEGIN
...
P2 := P1 ;
-- affectation
if P2 = P1 and P3 /= P1
-- comparaison ( galit et in galit
seulement )
then ...

Mais moins que vous ne les surchargiez, les oprateurs +, *, / ou - ne sont pas
dfinis pour nos types T_Pile et ne sont donc pas utilisables, de mme pour les test
dinfriorit (< et <=) ou de supriorit (> et >=). Il nest donc pas possible non plus
dcrire :
1
2
3

P3 := P1 + P2 ;
if P3 < P1
then ...

Restreindre encore notre type


Type limit et priv

Eh ! Mais si P1, P2 et P3 sont des piles, ce sont donc des pointeurs ! Cest
pas un peu dangereux dcrire P2 := P1 dans ce cas ?
Si, en effet. Vous avez bien retenu la leon sur les pointeurs. Il est effectivement risqu de laisser lutilisateur la possibilit deffectuer des comparaisons et surtout des
affectations. Heureusement, le langage Ada a tout prvu ! Pour restreindre encore les
possibilits, il est possible de crer un type non plus PRIVATE, mais LIMITED PRIVATE
(priv et limit) ! Et cest extrmement facile raliser puisquil suffit juste de modifier
la ligne n5 du fichier ads :
1

PACKAGE P_Pile IS

2
3

--

PARTIE PUBLIQUE

4
5

TYPE T_Pile IS LIMITED PRIVATE ;


d sormais priv ET limit ! ! !

-- Le type T_Pile est

6
7
8
9
10

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;


PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;
FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;
FUNCTION Length ( P : T_Pile ) RETURN Integer ;

411

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION


FUNCTION First ( P : T_Pile ) RETURN Integer ;

11
12

PRIVATE

13
14

--

15

PARTE PRIV E

16
17
18
19
20
21
22
23
24
25

TYPE T_Cellule ;
TYPE T_Pile IS ACCESS ALL T_Cellule ;
est le type T_Pile
TYPE T_Cellule IS
RECORD
Valeur : Integer ;
Index
: Integer ;
Suivant : T_Pile ;
END RECORD ;
END P_Pile ;

-- on d crit ce qu '

Dsormais, lutilisateur ne pourra QUE dclarer ses piles ! Plus daffectation ni de


comparaison !
Mais ? ! ? quoi a sert davoir un objet si on ne peut pas lui affecter de
valeur ?
Pensez aux fichiers et au type File_Type. Aviez-vous besoin deffectuer des affectations
ou des comparaisons ? Non bien sr. Cela naurait eu aucun sens ! Mais cela ne vous
a pas empch demployer des objets de type File_Type et de leur appliquer diverses
mthodes comme Put(), Get(), End_Of_Page() et tant dautres. Le principe est exactement le mme dans le cas prsent : est-ce cohrent daffecter UNE valeur une pile ?
Je ne crois pas. Dailleurs, nous avons la primitive Push() pour ajouter une valeur
celles dj existantes. En revanche, il pourrait tre utile dajouter quelques mthodes
notre package P_Pile pour copier une pile dans une autre ou pour tester si les piles
contiennent un ou des lments identiques. Le but tant davoir un contrle total sur
les affectations et les tests : avec un type LIMITED PRIVATE, rien part la dclaration
ne peut se faire sans les outils du package associ.
Type seulement limit
Pour information, il est possible de dclarer un type qui soit seulement LIMITED. Par
exemple :
1
2
3
4

TYPE T_Vainqueur IS LIMITED RECORD


Nom : String ( 1 .. 3 ) ;
Score : Natural := 0 ;
END RECORD ;

Ainsi, il sera impossible deffectuer une affectation directe ou un test dgalit (ou
ingalit) :
412

EXERCICES
1
2
3
4
5
6
7

...

Moi , Toi : T_Vainqueur ;


BEGIN
Toi := ( " BOB " , 5300 ) ;
possible !
Moi := Toi ;
if Moi = Toi
comparaison possible !
then ...

-- Erreurs ! ! ! Pas d ' affectation


-- Deuxi me erreur ! ! ! Pas de

En revanche, puisque le type T_Vainqueur nest pas priv, sa structure est accessible
et il demeure possible deffectuer des tests ou des affectations sur ses attributs. Le code
suivant est donc correct.
1
2
3
4

Toi . nom := " JIM " ;


If Moi . score > Toi . score
then put ( " Je t ' ai mis la misere ! ! ! " ;
end if ;

Lintrt dun type uniquement LIMITED est plus discutable et vous ne le verrez que
trs rarement. En revanche, les types LIMITED PRIVATE vont devenir de plus en plus
courants, croyez-moi.

Exercices
Exercice 1
nonc
Sur le mme principe, modifier votre package P_File afin que votre type T_File soit
priv et limit.
Solution
1

PACKAGE P_Pile IS

2
3

TYPE T_Pile IS LIMITED PRIVATE ;

4
5
6
7
8
9

PROCEDURE Push ( P : IN OUT T_Pile ; N : IN Integer ) ;


PROCEDURE Pop ( P : IN OUT T_Pile ; N : OUT Integer ) ;
FUNCTION Empty ( P : IN T_Pile ) RETURN Boolean ;
FUNCTION Length ( P : T_Pile ) RETURN Integer ;
FUNCTION First ( P : T_Pile ) RETURN Integer ;

10
11

PRIVATE

12
13

TYPE T_Cellule ;

413

CHAPITRE 24. LA PROGRAMMATION MODULAIRE II : ENCAPSULATION


TYPE T_Pile IS ACCESS ALL T_Cellule ;
TYPE T_Cellule IS
RECORD
Valeur : Integer ;
Index
: Integer ;
Suivant : T_Pile ;
END RECORD ;

14
15
16
17
18
19
20
21
22

END P_Pile ;

Exercice 2
nonc
Crer une classe T_Perso contenant le nom dun personnage de RPG (Jeu de rle),
ses points de vie et sa force. La classe sera accompagne dune mthode pour saisir un
objet de type T_Perso et dune seconde pour afficher ses caractristiques.
Solution
1
2
3
4
5
6
7
8
9
10
11
1
2

package P_Perso is
type T_Perso is private ;
-- ventuellement limited
private
procedure get ( P : out T_Perso ) ;
procedure put ( P : in T_Perso ) ;
private
type T_Perso is record
Nom
: string ( 1 .. 20 ) ;
PV
: Natural := 0 ;
Force : Natural := 0 ;
end record ;
end P_Perso ;
with ada . text_io ;
with ada . integer_text_io ;

use ada . text_io ;


use ada . integer_text_io ;

3
4

package body P_Perso is

5
6
7
8
9
10
11

procedure get ( P : out T_Perso ) is


begin
put ( " Entrez un nom ( moins de 20 lettres ) : " ) ; get ( P . Nom
) ; skip_line ;
put ( " Entrez ses points de vie
: " ) ; get ( P . PV )
; skip_line ;
put ( " Entrez sa force
: " ) ; get ( P .
Force ) ; skip_line ;
end get ;

12
13

414

procedure put ( P : in T_Perso ) is

EXERCICES
14
15
16
17
18
19

begin
put_line ( P . nom ) ;
put_line ( "
VIE : " & integer ' image ( P . PV ) ) ;
put_line ( "
FORCE : " & integer ' image ( P . Force ) ) ;
end put ;
end P_Perso ;

Voil qui est fait pour notre entre en matire avec la POO. Rassurez-vous, ce chapitre
ntait quune entre en matire. Si lintrt de la POO vous semble encore obscur ou
que certains points mriteraient des claircissement, je vous conseille de continuer la
lecture des prochains chapitres durant lesquels nous allons prciser la notion dobjet et
de classe. La prochaine tape est la gnricit, notion dj prsente en Ada83 et dont
je vous ai dj vaguement parl au cours des prcdents chapitres.

En rsum
En Programmation oriente objet, les types dobjets sont appels classes. Chaque
classe dispose de son propre package.
Le terme mthode regroupe les fonctions et les procdures associes une classe.
Toutes les mthodes doivent tre fournies avec le package.
Lorsque lon programme orient objet , il est im