cours ada de a à z

451
1 C H A P I T R E 1 NOTIONS DE BASE

Upload: starfreind

Post on 14-Jun-2015

1.567 views

Category:

Documents


5 download

DESCRIPTION

cours complet de langage Ada by Abdelhedi Meftah

TRANSCRIPT

Page 1: Cours Ada de A à Z

1

C H A P I T R E 1

N O T I O N S D E B A S E

Page 2: Cours Ada de A à Z

DÉFINITIONS GÉNÉRALES 2

1.1 DÉFINITIONS GÉNÉRALES

Le terme informatique (computer science) est un mot français désignant lascience qui traite des données pour obtenir des résultats. Ce traitement esttraditionnellement effectué à l’aide d’algorithmes. Un algorithme (algorithm) estune suite d’opérations à effectuer pour résoudre un problème (exemples 1.1 et 1.2).

Exemple 1.1 Algorithme de résolution de l’équation ax+b = 0.

1 si a et b sont nuls, chaque nombre réel est solution et l’algorithme est terminé;1 si a est nul et b non nul, l’équation est insoluble et l’algorithme est terminé;2 si a est non nul, soustraire b à gauche et à droite du signe = et l’on obtient ax

= –b;3 diviser chaque membre par a et l’on obtient le résultat cherché qui est x = –b/a.

L’algorithmique est une discipline fondamentale de l’informatique qui traite dela conception, de la réalisation et de la vérification des algorithmes. Sa pratique estrendue difficile par la rigueur qu’il est nécessaire d’appliquer, rigueur souventnégligée par les néophytes, voire parfois par les concepteurs plus expérimentés.

Exemple 1.2 Algorithme de mise en marche d’une voiture.

1 mettre la clé dans le démarreur;2 serrer le frein à main;3 mettre le levier des vitesses au point mort;4 répéter les opérations suivantes tant que le moteur ne tourne pas:

• mettre la clé dans la position marche;• tourner la clé dans le sens des aiguilles d’une montre;• attendre quelques secondes;• si le moteur ne démarre pas, remettre la clé dans la position initiale;

5 enclencher la première vitesse;6 desserrer le frein à main.

Lorsqu’un ordinateur doit exécuter un algorithme, celui-ci doit être exprimédans un langage compréhensible par la machine. Ce langage appelé langagemachine (machine code) est composé de suites de chiffres 0 et 1 appelés bits quicorrespondent à des états distincts des circuits qui composent la machine. Or leprogrammeur ne peut pas exprimer des algorithmes complexes avec des 0 et des 1!Il va utiliser un langage plus proche d’une langue naturelle appelé langage deprogrammation (programming language). Une fois cet algorithme codé dans unlangage de programmation, le programme source (source program) ainsi créé sera:

Page 3: Cours Ada de A à Z

DÉFINITIONS GÉNÉRALES 3

• soit traduit complètement en langage machine par le compilateur(compiler) pour permettre ensuite l’édition de liens;

• soit directement interprété (interpreted), c’est-à-dire que chaque ligne decode source est exécutée directement sans traduction préalable de tout leprogramme; cette ligne peut être traduite ou non en un langage proche dulangage machine avant exécution.

La compilation (compilation) est donc une étape supplémentaire mais al’avantage de produire un programme en langage machine. Ce programme enlangage machine peut exister aussi longtemps que nécessaire. Chaque fois que leprogramme doit être utilisé, il le sera directement, ce qui implique que la compi-lation n’est nécessaire qu’après chaque modification du programme source.

L’interprétation (interpretation) est plus directe que la compilation, mais latraduction de chaque ligne a lieu lors de toutes les utilisations du programme.

L’édition de liens (link) est la phase pendant laquelle les différentes partiescomposant un programme sont réunies de manière à former un programmeexécutable. C’est au cours de cette opération que des composants préexistants, parexemple des éléments graphiques, sont placés dans cet exécutable. Ces composantssont souvent regroupés dans une (ou plusieurs) bibliothèque(s) (library).

L’exécution (execution) d’un programme par un ordinateur consiste à exécuterles instructions machine les unes à la suite des autres. Elle sera nettement plusrapide dans le cas d’un programme compilé que pour un programme interprété.

Le déverminage (debugging) est l’activité de récupération, diagnostic etcorrection des erreurs de conception (logique) du programme. En d’autres termes,durant cette activité on procède à la mise au point du programme.

De manière générale, la programmation (programming) est l’art d’écrire desprogrammes (programs) en langage de programmation et constitue une autrediscipline fondamentale de l’informatique. Un environnement de programmation(programming environment) ou de développement représente l’ensemble des outilslogiciels (et parfois matériels) nécessaires à la conception, à la réalisation et à lamaintenance d’applications complexes.

Il faut enfin définir le terme implémentation (implementation) qui regroupetoutes les caractéristiques (d’un langage de programmation) propres à un en-vironnement de programmation particulier. Parfois ce terme représente l’environ-nement lui-même.

Page 4: Cours Ada de A à Z

LANGAGES DE PROGRAMMATION 4

1.2 LANGAGES DE PROGRAMMATION

1.2.1 Historique

Plusieurs centaines de langages de programmation ont été proposés au cours del’évolution de l’informatique. La majorité d’entre eux s’appliquaient à desdomaines très particuliers alors que d’autres se voulaient plus généraux. Leursuccès ou leur échec tient autant à leurs qualités ou leurs défauts qu’à la période oùils apparurent sur le marché ou dans les universités. Le tableau 1.1 décrit par ordrechronologique d’apparition plusieurs langages de programmation parmi lesprincipaux utilisés jusqu’à aujourd’hui ainsi que leur principal domained’application. Même s’ils ne sont pas cités explicitement, il faut égalementmentionner les langages d’assemblage appelés assembleurs (assemblers), utiliséssurtout pour programmer au niveau de la machine. Finalement, il faut préciser qu’ilexiste encore d’autres catégories de langages informatiques destinés par exemple àla simulation de systèmes ou à la conception de circuits électroniques.

Tableau 1.1 Principaux langages de programmation.

Année Langage Domaine d’application Remarques

1955 Fortran Calcul scientifi-que

Langage ancien, dont les versions plus récentescomportent encore des bizarreries héritées desannées 50; normalisé actuellement sous l’appellationFortran 98.

1960 Algol-60 Algorithmique Premier langage dit structuré grâce à la notion deblocs.

1959 Lisp Intelligence artifi-cielle

Premier langage non impératif.

1961 Cobol Applicationscommerciales

Langage verbeux, peu maniable, encore très utiliséen informatique de gestion.

1964 PL/1 Langage général Complexe, ayant la prétention d’être universel etregroupant les spécificités de Cobol et Fortran, crééet utilisé chez IBM.

1965 Basic «Travail à la mai-son»

Basique, simple d’utilisation mais peu adapté à laprogrammation structurée.

1970 Prolog Intelligence artifi-cielle

Langage non impératif.

1971 Pascal Enseignement Créé à l’Ecole polytechnique de Zurich, diffusé par-tout mais souffrant des différences présentes dans lesnombreuses versions existantes.

Page 5: Cours Ada de A à Z

LANGAGES DE PROGRAMMATION 5

1.2.2 Langage de programmation Ada

L’expérience montre que le premier langage de programmation appris estfondamental pour l’avenir d’un programmeur. En effet, les habitudes prises sontancrées si profondément qu’il est très difficile de les modifier voire de s’en défaire!L’apprentissage de la programmation doit donc s’effectuer avec un langage forçantle programmeur à adopter de bonnes habitudes (sect. 1.3). Il est toujours plus faciled’évoluer vers de nouveaux langages lorsque ces bonnes habitudes sont acquises.Le langage Ada aide à l’apprentissage d’une bonne programmation en obligeant leprogrammeur à se soumettre à certaines contraintes et en lui fournissant unepanoplie assez riche d’outils agréables à utiliser. Ces outils vont lui permettre decoder relativement simplement un algorithme même complexe et de refléterfidèlement sa structure. La version d’Ada présentée dans cet ouvrage est celledéfinie en 1995 [ARM] et normalisée ISO.

1972 C Programmationsystème

Accès facile au matériel, largement diffusé; norma-lisé sous l’appellation ANSI-C.

1976 Smalltalk Langage général Programmation orientée objets, prototypage rapide.

1980 Modula-2 Programmationsystème

Descendant direct de Pascal, mêmes problèmes deversions différentes.

1983 Ada Langage général Riche, utilisé pour développer des systèmes comple-xes et fiables, ainsi que pour des applications tempsréel critiques; normalisé sous l’appellation Ada 83.

1984 C++ Langage général Successeur de C; permet la programmation orientéeobjets; normalisé par l’ISO en 1998.

1995 Ada 95 Langage général Successeur d’Ada 83; ajoute la programmationorientée objets, par extension, etc; normalisé sousl’appellation Ada 95.

1996 Java Internet Semblable à C++, mais plus sûr; permet la program-mation d’applications classiques mais aussid’applets.

Tableau 1.1 (suite) Principaux langages de programmation.

Année Langage Domaine d’application Remarques

Page 6: Cours Ada de A à Z

BONNES HABITUDES DE PROGRAMMATION 6

1.3 BONNES HABITUDES DE PROGRAMMATION

Le but de tout programmeur est d’écrire des programmes justes, simples,lisibles, fiables et efficaces. Pour la justesse, la simplicité et la lisibilité, lesquelques points suivants sont fondamentaux (la liste est non exhaustive!):

• Réfléchir et imaginer de bons algorithmes de résolution avant d’écrire lapremière ligne du programme.

• Une fois l’algorithme général trouvé, en écrire l’esquisse dans unformalisme pseudo-formel, puis préciser cette esquisse pour finalementcoder l’algorithme dans le langage de programmation choisi.

• Lors du codage, choisir des noms parlants (utiliser des mnémoniques) pourreprésenter les objets manipulés dans le programme, commenter chaquemorceau du programme de manière explicative plutôt que descriptive ettester chaque module, procédure, fonction soigneusement.

Pendant toute la démarche, adopter et appliquer systématiquement des conventions simples et cohérentes.

Page 7: Cours Ada de A à Z

DE L’ANALYSE DU PROBLÈME À L’EXÉCUTION DU PROGRAMME 7

1.4 DE L’ANALYSE DU PROBLÈME À L’EXÉCUTION DU PROGRAMME

Voici une ébauche de marche à suivre pour la création de programmes à partird’un problème donné:

1 bien lire l’énoncé du problème, être certain de bien le comprendre;2 réfléchir au problème, déterminer les points principaux à traiter;3 trouver un bon algorithme de résolution (sect. 1.7), l’écrire dans le formalisme

choisi;4 coder l’algorithme en un programme écrit sur papier (au moins pour son

architecture principale);5 introduire le programme dans l’ordinateur au moyen d’un éditeur de texte;6 compiler le programme;7 effectuer l’édition de liens du programme;8 exécuter le programme, vérifier son bon fonctionnement par des tests

significatifs.

En cas d’erreurs de compilation, il faut les corriger avec l’éditeur de texte puisrecommencer le point 6. Si le programme fonctionne mais donne des résultats faux,ou si l’exécution du programme se termine par un message d’erreur, cela signifiequ’il y a des fautes de logique. Il faut réfléchir, parfois longuement, trouver l’ori-gine des fautes en particulier en s’aidant des outils de déverminage, modifier lepro-gramme en conséquence puis reprendre au point 6.Lorsque les programmes source dépassent une page, il serait judicieux de s’habi-tuer à les concevoir et les tester par étapes. Cette façon de faire devient en effet indispensable lorsque la taille du code écrit dépasse, par exemple, deux cents li-gnes, ce qui représente encore un tout petit programme!

Page 8: Cours Ada de A à Z

PROPRIÉTÉS D’UN PROGRAMME 8

1.5 PROPRIÉTÉS D’UN PROGRAMME

Lors de l’écriture d’une application, les propriétés énumérées ci-aprèsdevraient toujours guider le programmeur lors des choix qu’il devra inévitablementeffectuer, que le programme soit simple ou compliqué. En effet, un code bien écritsera toujours plus fiable, lisible, simple, juste et même efficace.

Ces propriétés sont les suivantes:

• La fiabilité (reliability) consiste à ce que les erreurs de programmationsoient détectées à la compilation ou à l’exécution afin que le programmen’ait jamais de comportement imprévu. Il est naturellement préférable quele plus d’erreurs possible soient signalées à la compilation de manière àdiminuer la phase de test du programme, toujours très coûteuse en temps eten argent, ainsi que le code supplémentaire généré pour la vérification decontraintes liées à l’exécution de certaines instructions. Un langage forte-ment typé répond à ce critère de fiabilité.

• La lisibilité (readability) permet de réduire la documentation associée auprogramme, de simplifier la correction des erreurs et de faciliter lesmodifications futures. Un langage permettant la construction de structuresde données et disposant de structures de contrôle répond à ce critère.Rappelons cependant que les habitudes du programmeur sont au moinsaussi importantes que le langage!

• La simplicité (simplicity) est un critère évident: ne jamais compliquerlorsqu’il est possible de rester simple.

• L’efficacité (efficiency) doit naturellement être suffisante afin que leprogramme s’exécute rapidement, mais ne doit pas constituer l’idole àlaquelle tout le reste est sacrifié. Une conception bien pensée, une bonnestructuration du code sont beaucoup plus importantes que l’accumulationd’astuces subtiles permettant un gain de temps minime lors de l’exécutiondu programme.

• Enfin, on appelle portabilité (portability) la propriété représentantl’indépendance d’une application ou d’un langage de programmation parrapport à la machine utilisée. Cette propriété joue un rôle fondamental lorsde l’évolution des programmes au cours du temps. Plus un logiciel estportable, moins il sera sensible aux changements de matériels utilisés.

Page 9: Cours Ada de A à Z

EXEMPLE INTRODUCTIF 9

1.6 EXEMPLE INTRODUCTIF

Une bonne introduction à l’algorithmique consiste à étudier un problèmesimple (exemple 1.3) dont la résolution sera effectuée en respectant la marche àsuivre (sect. 1.4), restreinte aux points 1 à 4. Pour trouver un algorithme de résolu-tion, il faut appliquer une méthode et l’utiliser chaque fois qu’un problème deprogrammation doit être résolu.

La méthode proposée ici est connue sous le nom de méthode de décompositionpar raffinements successifs. Cet exemple va également permettre d’insister sur lesbonnes habitudes de programmation et d’introduire les premières notions deprogrammation en langage Ada.

Exemple 1.3 Problème simple.

Dessiner dans une fenêtre graphique une figure composée de deux formesgéométriques, soit un carré et un triangle isocèle. De plus, le programme doitannoncer le début et la fin du dessin dans une fenêtre de texte.

Du fait de sa simplicité, la compréhension de ce problème est immédiate, aprèsavoir précisé que la disposition des formes est libre. Le point 1 de la marche àsuivre est fait.

Page 10: Cours Ada de A à Z

MÉTHODE DE DÉCOMPOSITION PAR RAFFINEMENTS SUCCESSIFS 10

1.7 MÉTHODE DE DÉCOMPOSITION PAR RAFFINEMENTS SUCCESSIFS

Cette méthode est basée sur l’idée que, étant donné un problème à résoudre, ilfaut le décomposer en sous-problèmes de telle manière que:

• chaque sous-problème constitue une partie du problème donné;• chaque sous-problème soit plus simple (à résoudre) que le problème donné;• la réunion de tous les sous-problèmes soit équivalente au problème donné.

Il faut ensuite reprendre chaque sous-problème et le décomposer comme ci-dessus et recommencer jusqu’à ce que chaque sous-problème soit facile à résoudre.Une étape de cette suite de décompositions est appelée raffinement.

Une telle méthode est efficace après avoir été utilisée plusieurs fois. Lorsqueles problèmes deviennent suffisamment complexes pour que la découverte d’unesolu-tion ne soit plus un processus trivial, il est indispensable de l’appliquersystéma-tiquement. Elle donne en général de bons algorithmes résolvant lesproblèmes posés. Mais, comme toute méthode, elle a cependant ses limites. Celle-ci devient inutilisable lorsque la complexité des problèmes est tout simplement tropgrande pour que la suite de raffinements soit facilement exploitable.

Page 11: Cours Ada de A à Z

APPLICATION À L’EXEMPLE INTRODUCTIF 11

1.8 APPLICATION À L’EXEMPLE INTRODUCTIF

1.8.1 Considérations techniques

Selon le point 2 de la marche à suivre (sect. 1.4), il faut déterminer les pointsprincipaux à traiter. Le problème étant en partie géométrique, il faut tout d’abordsavoir comment dessiner dans une fenêtre de l’écran d’un ordinateur. Ladocumentation technique nous apprend que:

• le système de coordonnées cartésiennes a son origine au point (0, 0) situéen haut à gauche de la fenêtre graphique;

• les axes sont disposés comme le montre la figure 1.1.

Figure 1.1 Axes et coordonnées cartésiennes dans une fenêtre graphique.

Ceci étant établi, il faut connaître comment faire apparaître la fenêtre, dessinerun carré et un triangle, ou au moins des segments de droite. De même, il seranécessaire de savoir comment écrire dans la fenêtre de texte. Ces renseignementsconstituent le point 2 de la marche à suivre.

1.8.2 Algorithme de résolution

L’algorithme de résolution (point 3, sect. 1.4) va être déterminé en utilisant laméthode décrite dans la section 1.7. Etant donné le problème initial, on en extraitles sous-problèmes:

1 annoncer le début du dessin;2 ouvrir, faire apparaître la fenêtre de dessin;3 dessiner le carré;4 dessiner le triangle isocèle;5 annoncer la fin du dessin.

Ceci constitue le premier raffinement. Comme les points 1, 2 et 5 sontimmédiatement traduisibles en Ada, ils seront laissés tels quels. Le raffinement

(0,0) X

Y (X,Y)

Page 12: Cours Ada de A à Z

APPLICATION À L’EXEMPLE INTRODUCTIF 12

suivant est alors:1 annoncer le début du dessin;2 ouvrir, faire apparaître la fenêtre de dessin;3 choisir la position du carré, i.e. celle du sommet en haut à gauche;4 dessiner le carré avec les côtés parallèles aux axes de coordonnées;5 choisir la position du triangle isocèle, i.e. celle du sommet gauche;6 dessiner le triangle isocèle sur la pointe, avec la base parallèle à l’axe des x.7 annoncer la fin du dessin.

Afin de présenter un troisième raffinement, l’environnement Ada à dispositionest supposé ne pas permettre le dessin d’un carré ou d’un triangle. La décom-position devient:

1 annoncer le début du dessin;2 ouvrir, faire apparaître la fenêtre de dessin;3 choisir la position du carré, i.e. celle du sommet en haut à gauche;4 dessiner le côté supérieur depuis cette position;5 dessiner le côté droit depuis l’extrémité droite du côté supérieur;6 dessiner le côté inférieur depuis l’extrémité inférieure du côté droit;7 dessiner le côté gauche depuis l’extrémité gauche du côté inférieur;8 choisir la position du triangle isocèle, i.e. celle du sommet gauche;9 dessiner la base depuis cette position;10 dessiner le côté droit depuis l’extrémité droite de la base;11 dessiner le côté gauche depuis l’extrémité inférieure du côté droit;12 annoncer la fin du dessin.

Cette suite de raffinements s’arrête ici. En effet, même si la norme ne définitaucune opération graphique, des outils supplémentaires présents dans (presque)tous les environnements Ada permettent de dessiner un segment depuis un pointcourant et d’afficher un mot. La suite d’opérations 1, 2, 3.1, 3.2.1, etc., constitueun algorithme de résolution du problème donné.

1.8.3 Codage de l’algorithme en Ada

Un premier essai de codage de l’algorithme (point 4, sect. 1.4) donne leprogramme Exemple_Essai_1 (exemple 1.4). Il est correct, simple, fonctionneparfaitement mais est assez mal écrit. En effet, l’art de la programmation doit obéirà des conventions de style telles que celles décrites dans [AUS 95].

En particulier, il faut savoir que:

• un programme doit être lisible, ce qu’il n’est pas;• un programme doit être commenté, ce qu’il n’est pas;• le code doit refléter les décisions prises par le programmeur, ici la grandeur

des dessins et les points initiaux des dessins;• les nombres entiers peuvent signifier n’importe quoi! Il faut préciser leur

signification en leur substituant des noms parlants.

Page 13: Cours Ada de A à Z

APPLICATION À L’EXEMPLE INTRODUCTIF 13

Exemple 1.4 Premier codage (maladroit) de l’algorithme obtenu.

with Ada.Text_IO; use Ada.Text_IO;

with Spider; use Spider;

procedure Exemple_Essai_1 isbegin

Put_Line ( "Debut du dessin");Init_Window ("Fenetre de dessin");Move_To (30, 120);Line (50, 0);Line (0, 50);Line (– 50, 0);Line (0, – 50);Move_To (110, 120);Line (80, 0);Line (– 40, 40);Line (– 40, – 40);Put_Line ( "Fin du dessin");

end;

Malgré son apparente complexité, l’exemple 1.5 ci-après présente unprogramme source bien écrit et illustrant les points énumérés précédemment. Lesqualités de style de ce programme résident en particulier dans:

• les indications sur l’auteur, le but du programme (ici réduit au minimum),la date de création, les modifications éventuelles; d’autres informationsgénérales pourraient bien entendu compléter cette introduction;

• la mention systématique de noms représentant des nombres;• les explications décrivant chaque partie du programme;• la cohérence dans le choix des identificateurs;• la mise en page et l’alignement des lignes.

Il est vrai qu’en pratique, la production de logiciels dans des délais souventextrêmement réduits conduit parfois à négliger l’effort de présentation du code.D’un autre côté, certaines entreprises imposent des normes strictes à respecter à lalettre. C’est le cas des industries pour lesquelles la qualité et la fiabilité du codeproduit est impérative comme dans le domaine spatial ou l’avionique par exemple.

Exemple 1.5 Codage corrigé de l’algorithme obtenu.

-- Auteur: Dupont Jean-- But du programme: illustrer un codage soigne-- Date de creation: 1 octobre 1999-- Date de modification:-- Raison de la modification:

Page 14: Cours Ada de A à Z

APPLICATION À L’EXEMPLE INTRODUCTIF 14

-- Pour afficher du texte dans la fenetre de textewith Ada.Text_IO; use Ada.Text_IO;

-- Pour lire les nombres entiers donnes par l'utilisateurwith Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

-- Pour travailler avec la fenetre de dessinwith Spider; use Spider;

-- Ce programme illustre un codage soigne en Ada; il dessine un-- carre et un triangleprocedure Exemple_Bien_Fait is

Droite : constant := 1; -- Pour se deplacer d'une uniteGauche : constant := –1; -- dans les quatre directionsHaut : constant := –1;Bas : constant := 1;

-- Pour un trait vertical, le deplacement horizontal est nulA_L_Horizontale : constant := 0;

-- Pour un trait horizontal, le deplacement vertical est nulA_La_Verticale : constant := 0;

Cote_Carre : constant := 50; -- Longueur d'un cote du carre

Abscisse_Carre : Integer; -- Abscisse et ordonnee du pointOrdonnee_Carre : Integer; -- initial de dessin du carre

Demi_Base : constant := 40; -- Longueur de la demi-base du-- triangle

Abscisse_Triangle : integer; -- Abscisse et ordonnee du pointOrdonnee_Triangle : integer; -- initial de dessin du triangle

begin -- Exemple_Bien_Fait

-- Presentation du programme a l'utilisateurPut ("Bonjour. Je vais dessiner un carre et un triangle ");Put_Line ("dans une fenetre de dessin.");Put_Line ("Debut du dessin...");

-- Pour pouvoir dessiner dans la fenetre de dessinInit_Window ("Fenetre de dessin");

-- L'utilisateur donne le point initial de dessin du carrePut ("Donnez l'abscisse du point initial du carre: ");Get (Abscisse_Carre);Put ("Donnez l'ordonnee du point initial du carre: ");Get (Ordonnee_Carre);

-- Dessin du carreMove_To (Abscisse_carre, Ordonnee_Carre);Line (Cote_Carre * Droite, A_L_Horizontale);Line (A_La_Verticale, Cote_Carre * Bas);Line (Cote_Carre * Gauche, A_L_Horizontale);Line (A_La_Verticale, Cote_Carre * Haut);

-- L'utilisateur donne le point initial de dessin du trianglePut ("Donnez l'abscisse du point initial du triangle:");Get (Abscisse_Triangle);Put ("Donnez l'ordonnee du point initial du triangle:");

Page 15: Cours Ada de A à Z

APPLICATION À L’EXEMPLE INTRODUCTIF 15

Get (Ordonnee_Triangle);

-- Dessin du triangle.Move_To (Abscisse_Triangle, Ordonnee_Triangle);Line (2 * Demi_Base * Droite, A_L_Horizontale);Line (Demi_Base * Gauche, Demi_Base * Bas);Line (Demi_Base * Gauche, Demi_Base * Haut);

-- Message de fin du dessin et du programmePut_Line ("Fin du dessin.");Put_Line ("Fin du programme.");

end Exemple_Bien_Fait;

Page 16: Cours Ada de A à Z

STRUCTURE D’UN PROGRAMME ADA 16

1.9 STRUCTURE D’UN PROGRAMME ADA

Un programme Ada est composé de quatre parties principales:

• La clause de contexte (context clause) qui sera développée par la suite (§10.5.1). Cette clause doit contenir les outils Ada que le programmeur peututiliser sans avoir à les construire lui-même, et indispensables au fonction-nement du programme. Ces outils sont regroupés en paquetages, notionprésentée ultérieurement (chap. 11). Il est nécessaire de les indiquer pourque le compilateur puisse en tenir compte lors de la phase de vérification etde traduction en code machine. La mention de ces paquetages se fera demanière intuitive en suivant les exemples fournis. Pour le programmeExemple_Bien_Fait la clause de contexte est la suivante:

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;with Spider; use Spider;

• L’en-tête du programme, où est spécifié le nom du programme. Pour leprogramme Exemple_Bien_Fait l’en-tête est la suivante:

procedure Exemple_Bien_Fait is

• La partie déclarative (declarative part), comprise entre l’en-tête et le motreservé begin, contenant les déclarations (declarations) des objets(constantes, variables, etc.) utilisés dans le programme. Ces objetsreprésenteront des données traitées par le programme. La partie déclarativepeut encore contenir d’autres déclarations comme des sous-programmes(chap. 4) ou encore des types (chap. 5).

• Pour le programme Exemple_Bien_Fait la partie déclarative est lasuivante:

Droite : constant := 1;Gauche : constant := –1;Haut : constant := –1;Bas : constant := 1;

A_L_Horizontale : constant := 0;A_La_Verticale : constant := 0;

Cote_Carre : constant := 50;Abscisse_Carre : Integer;Ordonnee_Carre : Integer;

Demi_Base : constant := 40;Abscisse_Triangle : Integer;Ordonnee_Triangle : Integer;

Il faut noter que les déclarations sont groupées logiquement: d’abord lesobjets généraux pour le dessin, puis ceux concernant le carré, finalementceux concernant le triangle.

Page 17: Cours Ada de A à Z

STRUCTURE D’UN PROGRAMME ADA 17

• La partie instructions du programme, comprise entre les mots begin etend, appelée souvent corps (body). Elle contient les instructions(statements) du programme, c’est-à-dire les actions à entreprendre sur lesdonnées. Parmi celles-ci et à titre d’exemple:

Put_Line ("Debut du dessin...");Line (Cote_Carre * Droite, A_L_Horizontale);

C’est dans cette partie que se trouve, sous forme codée, l’algorithme choisipour résoudre le problème. Il faut encore relever le rappel du nom duprogramme après le end final.

Finalement, il faut insister sur le fait qu’un programme doit être documenté, enparticulier par des commentaires initiaux précisant le nom de l’auteur, la date decréation, etc., comme suggéré dans l’exemple 1.5. Un guide méthodologiqueintitulé Style et qualité des programmes Ada 95 [AUS 95], destiné auxprofessionnels, fournit de nombreuses indications relatives aux différents aspectsde l’écriture du code source (sect. 1.11).

Page 18: Cours Ada de A à Z

CONSTITUANTS D’UN PROGRAMME 18

1.10 CONSTITUANTS D’UN PROGRAMME

1.10.1 Généralités

Un programme est écrit dans un langage de programmation. Ce langage estcomposé de mots, symboles, commentaires, etc. Ceux-ci sont groupés en phrasesdont l’ensemble compose le programme. Les phrases obéissent à des règles et cesrègles déterminent de manière absolument stricte si une phrase est correcte ou non,c’est-à-dire si cette phrase respecte la syntaxe (syntax) du langage.

L’analogie avec les langues naturelles (français, allemand, etc.) est donc forte(fig. 1.2), la principale différence étant qu’une phrase française peut être formée demanière beaucoup moins rigoureuse et signifier néanmoins quelque chose. Or unephrase en Ada (ou tout autre langage de programmation) doit être absolument justepour être comprise par le compilateur, sans quoi elle est obligatoirement rejetée!

Un programme Ada est composé de phrases appelées unités syntaxiques(syntactic units). Elles sont elles-mêmes constituées de mots, symboles, etc.appelés unités lexicales (lexical units). Chaque unité lexicale est une suite decaractères appartenant au jeu de caractères Ada.

Figure 1.2 Analogies avec la langue française.

1.10.2 Jeu de caractères en Ada

A d a

mots et symbole de ponctuation

nom verbe article nomsujet verbe complément

L i n e

identificateurs et symboles

id. de procédure constante constante constante

appel de procédure paramètre paramètre

e s t u n l a n g a g e .

( A _ L a _ V e r t i c a l e, C o t e _ C a r r e * B a s ) ;

Page 19: Cours Ada de A à Z

CONSTITUANTS D’UN PROGRAMME 19

Le jeu de caractères en Ada comporte les caractères décrits dans [ARM 2.1].Cet ensemble est normalisé ISO sous le nom LATIN-1 et comporte les lettres ma-juscules et minuscules, les lettres accentuées, les chiffres, les symboles de ponc-tuation, etc. Il comprend comme sous-ensemble tous les caractères du code ASCII(§ 3.8.1). On distingue les caractères imprimables (printable characters), lisibles,comme ceux cités ci-dessus, des caractères non imprimables qui incluent lescaractères de contrôle (control characters) interprétés de manière spéciale parl’implémentation (par exemple des caractères de contrôle comme le tabulateurhorizontal (tab), le retour de chariot (return), le saut de ligne (line feed), etc.).

1.10.3 Unités lexicales en Ada

Diverses unités lexicales existent en Ada:

• Les identificateurs (identifiers), comme par exemple A, Limite_100,Cote_Carre, Put, etc. Un identificateur est un mot composé de lettres, dechiffres et du caractère _ et commençant obligatoirement par une lettre. S’iln’y a aucune limite théorique au nombre de caractères composant unidentificateur, la norme Ada laisse à l’implémentation le soin de définir lenombre maximum de caractères significatifs, tout en précisant cependantque ce maximum doit être d’au moins deux cents caractères. Pour respecterles bonnes habitudes de programmation, les identificateurs doivent être par-lants pour les lecteurs du programme! Les caractères significatifs servent àdifférencier deux identificateurs. Il faut relever que la casse (majuscule/minuscule) n’est pas significative!

Le langage contient des identificateurs prédéfinis (predefined identifiers)comme Integer, Float, Text_IO, Constraint_Error, etc., que leprogrammeur peut éventuellement redéfinir dans son programme, maisdont le but principal reste néanmoins d’être utilisés tels quels, dans lessituations pour lesquelles ils ont été créés. L’annexe [ARM A.1] en donnela liste.

• Les mots réservés (reserved words) qui sont des identificateurs restreintsà un usage bien défini (toujours le même!), par exemple procedure, is,begin, etc. La liste est fournie dans [ARM 2.9].

• Les symboles (symbols) formés d’un unique caractère ou de deux caractèresaccolés, comme

< > = <= := + – , ;

la liste exhaustive se trouve dans [ARM 2.2].

• Les commentaires (comments), c’est-à-dire n’importe quels caractères im-primables typographiés après le symbole -- et ce jusqu’à la fin de la ligne(fin de commentaire). Ils servent à expliquer le pourquoi et le comment desconstructions auxquelles ils s’appliquent.

Page 20: Cours Ada de A à Z

CONSTITUANTS D’UN PROGRAMME 20

• Les constantes numériques entières ou réelles ou plus simplement lesnombres comme

123 –36 24E3 12.3 –234.0E3 –0.3E–2

les sections 2.2 et 2.3 définiront précisément ces constantes.

• Les constantes caractères ou plus simplement les caractères, c’est-à-direun seul caractère imprimable entre apostrophes comme

'a' '?' 'A' '='

la section 3.8 définira précisément ces constantes.

• Les constantes chaînes de caractères ou plus simplement les chaînes decaractères, c’est-à-dire une suite d’un ou de plusieurs caractères(éventuellement aucun) entre guillemets comme

"abcd" "CARRE" "triangle de Pascal" "="

la section 9.2 définira précisément ces constantes.

Il est parfois possible, voire pratique, d’accoler deux unités lexicales. Mais enrègle générale, pour augmenter la lisibilité, deux unités lexicales seront séparéespar au moins un espace, une tabulation ou une fin de ligne.

1.10.4 Unités syntaxiques et diagrammes syntaxiques

Les unités syntaxiques, comme les unités lexicales d’ailleurs, peuvent êtredécrites par des diagrammes appelés diagrammes syntaxiques. Un diagrammesyntaxique (syntactic diagram) permet donc de décrire l’orthographe d’une unitélexicale ou, comme son nom l’indique, la syntaxe (grammaire) d’une phrase duprogramme. Toute phrase est donc composée d’une séquence d’unités lexicales. Ilest ainsi possible de vérifier si une instruction d’un programme en cours d’écritureest correcte en vérifiant sa syntaxe à l’aide des diagrammes syntaxiques dulangage, Ada en l’occurrence.

Page 21: Cours Ada de A à Z

CONSTITUANTS D’UN PROGRAMME 21

Figure 1.3 Diagrammes syntaxiques définissant un identificateur et un chiffre.

Cette vérification est simple à réaliser; il suffit de parcourir le diagrammesyntaxique dans le sens des flèches. Par exemple, la figure 1.3 montre un teldiagramme, permettant de vérifier qu’un «mot» est bien un identificateur. Ensuivant le diagramme syntaxique on rencontre une première bulle indiquant que lepremier caractère d’un identificateur doit être une lettre. Si c’est le cas l’analysepeut continuer avec un choix: soit l’identificateur ne comporte qu’une lettre et lediagramme est quitté, soit il comporte d’autres caractères (lettres, chiffres ou _ ) etil faut alors suivre les autres chemins proposés par le diagramme et vérifier au furet à mesure s’ils peuvent correspondre aux caractères constitutifs de la suite del’identificateur. Si le diagramme est quitté, l’unité lexicale ou syntaxique analyséeest correcte. Mais si l’on reste bloqué dans le diagramme sans possibilité d’ensortir, cela correspond à une erreur d’orthographe ou de syntaxe.

Il existe trois sortes de bulles dans les diagrammes syntaxiques: les cercles, lesrectangles à «coins» arrondis et ceux à angle droit. Si les coins sont à angle droit,cela signifie un renvoi à une entité syntaxique différente. Il faut ainsi se reporter àun autre digramme, pour continuer l’analyse et en revenir en cas de succès, et ainside suite. Si les coins sont arrondis ou si c’est un cercle, alors le contenu représenteun symbole ou un mot réservé qui devra être présent tel quel dans le texte analysé.Les bulles de la figure 1.3 contenant les mots lettre et chiffre renvoient doncà deux autres diagrammes syntaxiques dont l’un des deux est donné dans la mêmefigure. Par contre, celles contenant les chiffres de 0 à 9, ou le caractère _ montrentque ces caractères doivent apparaître tels quels dans un nombre, ou un identificateur.

lettre

_lettre

chiffre

identificateur

chiffre

90 1 2 3 4 5 6 7 8

Page 22: Cours Ada de A à Z

MISE EN PAGE D’UN PROGRAMME 22

1.11 MISE EN PAGE D’UN PROGRAMME

La mise en page d’un programme Ada est assez libre. Cependant, et pour desraisons évidentes de clarté et de lisibilité d’un programme, il existe des conventionsde style qu’il faut respecter, décrites dans [AUS 95]. En particulier il est demandé:

• de commenter chaque déclaration ou instruction importante en indiquant laraison de sa présence; ce commentaire se place avant ou à côté de ladéclaration ou instruction;

• de choisir des identificateurs faciles à comprendre;• d’assurer un degré élevé de systématique et de cohérence;• d’effectuer une seule déclaration ou instruction par ligne;• d’indenter, c’est-à-dire de décaler vers la droite les déclarations ou instruc-

tions contenues dans une autre déclaration ou instruction.

Page 23: Cours Ada de A à Z

23

1.12 EXERCICES

1.12.1 Conception d’un algorithmeEcrire un algorithme d’appel téléphonique à partir d’une cabine publique.

1.12.2 Déclarations et instructions

En Ada, peut-on mélanger des déclarations et des instructions dans une partiedéclarative? Dans un corps?

1.12.3 Utilisation d’un diagramme syntaxique

Utiliser le diagramme syntaxique de la figure 1.3 pour déterminer si les termessuivants sont des identificateurs en Ada:

Ada pRemiEr Découpe Bon_Appetit Ah__Ah Fin_ A+Tard Quoi?

Page 24: Cours Ada de A à Z

POINTS À RELEVER 24

1.13 POINTS À RELEVER

1.13.1 En général

• Un algorithme est une suite d’opérations à effectuer pour résoudre unproblème.

• Saisie du programme, compilation, édition de liens, exécution,déverminage constituent les phases de création de tout programme.

• Algol-60, Pascal, Modula-2, Ada, Ada 95 forment une famille de langagesde programmation.

• C, C++, Java forment une autre famille de langages de programmation.

• Le terme Ada est utilisé dans cet ouvrage pour nommer la version 1995 dulangage de programmation Ada normalisé la première fois en 1983.

• Le respect des bonnes habitudes de programmation aboutit à desprogrammes plus simples et plus lisibles.

• Fiabilité, lisibilité, simplicité, justesse et efficacité des programmes sontdes propriétés parmi les plus importantes.

• La méthode de décomposition par raffinements successifs aboutit à unalgorithme de résolution du problème initial.

• Les unités lexicales constituent les mots du texte du programme alors queles unités syntaxiques en forment les phrases.

• Les diagrammes syntaxiques permettent de s’assurer que la syntaxe d’uneconstruction est correcte.

• Les identificateurs nomment les entités d’un programme et doiventtoujours être parlants.

• Les commentaires expliquent les raisons de la présence des constructionsauxquelles ils s’appliquent.

1.13.2 En Ada

• Un programme principal Ada est constitué dans l’ordre de la clause decontexte, l’en-tête, la partie déclarative et la partie instructions.

• Les identificateurs sont composés d’une suite de lettres ou de chiffres,éventuellement séparés par le caractère _ et doivent obligatoirementcommencer par une lettre.

Page 25: Cours Ada de A à Z

25

C H A P I T R E 2

N O M B R E S E T E N T R É E S- S O R T I E S

Page 26: Cours Ada de A à Z

26 NOMBRES ET ENTRÉES-SORTIES DE BASE

Page 27: Cours Ada de A à Z

RAPPELS 27

2.1 RAPPELS

Un programme Ada se compose de quatre parties:

• la clause de contexte;• l’en-tête;• la partie déclarative;• la partie instructions.

La partie déclarative contient les déclarations des objets (constantes, variables,etc.) représentant les données que l’on veut traiter. Le corps contient les ins-tructions du programme, c’est-à-dire les traitements à effectuer sur ces données. Leprésent chapitre porte sur la représentation de ces données, en se limitant pourl’instant à deux catégories de nombres: les nombres entiers et réels.

Page 28: Cours Ada de A à Z

TYPES ENTIERS 28

2.2 TYPES ENTIERS

2.2.1 Motivation

Les nombres entiers sont traditionnellement utilisés les premiers dans un coursd’apprentissage de la programmation. En effet, ils ne posent pas (ou peu) deproblèmes. Le travail avec eux est naturel puisqu’il correspond à de l’arithmétique.Un réflexe doit cependant être acquis le plus vite possible, il est mis en évidencedans la note 2.1.

NOTE 2.1 L’ensemble des nombres entiers n’est pas infini sur un ordinateur.

Tous les nombres entiers ne sont évidemment pas utilisables sur un ordinateur; en effet, s’il existeen théorie une infinité de tels nombres, chacun d’entre eux occupe un ou quelques mots en mémoireet, de ce fait, l’ensemble des entiers représentables sur un ordinateur est fini. Généralement, lesentiers sont représentés par 16 ou 32 bits, éventuel-lement 8 bits (§ 2.5.2). Les machines surlesquelles ils occupent 64 bits ne sont encore pas très courantes.

2.2.2 Généralités

L’identificateur Integer est un identificateur prédéfini. Il signifie: «un objet(constante, variable, etc.) de type Integer contiendra un nombre entier et riend’autre». Les variables Abscisse_Carre, Ordonnee_Carre, etc., du programmeExemple_Bien_Fait (§ 1.8.3) sont de type Integer. Le type Integer faitpartie des types discrets (discrete types, [ARM 3.2]). Il faut cependant déjàmentionner qu’il existe d’autres types qu’Integer pour les entiers (§ 2.2.7). Maistous les nombres entiers (non explicitement basés) doivent respecter la syntaxedécrite par la figure 2.1.

Les constantes entières sont les nombres entiers eux-mêmes. Ces nombrespeuvent être décimaux ou basés. Un nombre basé est un nombre écrit dans unebase explicitement mentionnée, comprise entre 2 et 16. Le caractère _ est utile pourla lisibilité d’un nombre mais n’a aucune signification particulière.

Page 29: Cours Ada de A à Z

TYPES ENTIERS 29

Figure 2.1 Diagrammes syntaxiques définissant un nombre entier décimal.

Exemple 2.1 Nombres entiers décimaux ou basés.

Nombres entiers décimaux:10 12E6 12e6 12E+6 1_0 1_000 1_0E2 1E1_0

Le nombre entier 12 dans différentes bases (binaire, octale, décimale, hexadécimale):2#1100# 2#0000_1100# 8#14# 10#12# 16#c# 16#C#

Les opérations arithmétiques possibles sur les valeurs entières se répartissenten deux catégories:

les opérations unaires + – abs

chiffre

Numéral

chiffre

_

Nombre entier décimal

Exposant positif

E

Numéral

+e

Numéral

Exposant positif

Page 30: Cours Ada de A à Z

TYPES ENTIERS 30

• le symbole + représente l’identité et le symbole – l’opposé;• le mot réservé abs représente l’opération «valeur absolue»;

les opérations binaires + – * / ** rem mod

où• le symbole + représente l’addition et le symbole – la soustraction;• le symbole * représente la multiplication et le symbole / la division

(entière);• le symbole ** représente l’exponentiation (exposant entier positif ou nul);• le mot réservé rem représente le reste de la division entière (euclidienne);

notons les relations suivantes: A rem (–B) = A rem B et (–A) rem B= –(A rem B);

• le mot réservé mod représente l’opération mathématique modulo qui ne serapas détaillée ici, ni d’ailleurs les différences entre mod et rem; il suffit depréciser que mod est équivalent à rem si les deux opérandes sont positifs.

Les opérations + – * / ** abs rem mod sont appelées opérateurs(operators), alors que les valeurs sur lesquelles ils opèrent sont les opérandes(operands).

Les expressions (expressions) entières sont des combinaisons de cesopérations. L’évaluation (le calcul) d’une expression (expression evaluation)consiste à trouver la valeur (le résultat) de l’expression. Il faut alors prendre gardeau fait qu’une telle expression n’est pas toujours calculable (note 2.2)! En effet, unedivision par zéro est impossible ou encore l’élévation d’un nombre à une tropgrande puissance provoque un débordement de capacité (overflow), c’est-à-direl’obtention d’une valeur non représentable sur la machine utilisée (note 2.1).Lorsque le calcul de l’expression n’est pas possible, une erreur sera générée àl’exécution du programme.

NOTE 2.2 Attention aux dépassements de capacité.

En Ada, le calcul d’une expression provoquera une erreur à l’exécution (en généralConstraint_Error, § 6.3.2) si la valeur obtenue n’est pas représentable sur la machine ou si elle esthors de l’intervalle des valeurs du type utilisé. Une expression doit donc être conçue de manière àne jamais provoquer de telles erreurs. Si cette garantie ne peut pas être obtenue (utilisation devaleurs données à l’exécution, par exemple par l’utilisateur), Ada offre un mécanisme de traitementdes erreurs (sect. 6.3).

Exemple 2.2 Expressions entières.

2est une expression réduite à une seule constante de valeur 2;3 + 4est une expression de valeur 7, l’opérateur est + et les opérandes sont les constantes

Page 31: Cours Ada de A à Z

TYPES ENTIERS 31

3 et 4;–2est une expression de valeur –2;abs (–2)est une expression de valeur 2;2 ** 8est une expression de valeur 256;4 * 5est une expression de valeur 20;4 / 2est une expression de valeur 2;5 / 2est aussi une expression de valeur 2;4 rem 2 et 4 mod 2sont deux expressions de valeur 0;5 rem 2 et 5 mod 2sont deux expressions de valeur 1;5 rem (–2)est une expression de valeur 1;(–5) rem 2est une expression de valeur –1;2 + 3 * 4est une expression qui vaut 14 (sect. 2.4);(2 + 3) * 4est une expression qui vaut 20;Nombre + 1est une expression additionnant 1 à la valeur actuelle de la variable Nombre(dépassement de capacité possible);(Nombre + 10) / Nombreest une expression correcte ((dépassement de capacitépossible lors de l’adition ou si Nombre vaut 0).

Les parenthèses permettent de définir des sous-expressions et un ordred’évaluation de celles-ci. Mais les opérateurs sont également classés en niveaux depriorité (sect. 2.4).

2.2.3 Affectation

Le mot affectation (assignment) signifie «donner une valeur à». Parlerd’affectation de 3 à la variable Nombre signifie que l’on veut que la variableNombre prenne dès lors la valeur 3. Une variable est donc un objet dont la valeurpeut être modifiée au cours de l’exécution d’un programme, alors qu’une constantevoit sa valeur fixée lors de sa déclaration (§ 3.9.1). Toute affectation entre valeuret variable entières s’écrit (exemple 2.3):

nom_de_variable_entière := expression_de_type_Integer;

La sémantique de n’importe quelle affectation s’exprime par un algorithme:1 obtenir la valeur de tous les opérandes de l’expression;2 calculer la valeur de l’expression;3 remplacer le contenu de la variable par cette valeur.

ATTENTION: lors de l’affectation, une erreur se produira si la variable ne peut pasaccueillir la valeur calculée.

Exemple 2.3 Affectations d’expressions entières à une variable entière.

-- ...procedure Exemple_2_3 is

Page 32: Cours Ada de A à Z

TYPES ENTIERS 32

Max : constant := 5; -- Une constante entiereNombre : Integer; -- Une variable entiere

begin -- Exemple_2_3

Nombre := 5; -- Affecte la valeur 5 a NombreNombre := Nombre + 4; -- Affecte la valeur 9 a NombreNombre := (36 / 10) * 2; -- Affecte la valeur 6 a NombreNombre := Nombre / 2; -- Affecte la valeur 3 a NombreNombre := Max; -- Affecte la valeur de Max a... -- Nombre

2.2.4 Dangers liés aux variables non initialisées

Soit le morceau de programme donné dans l’exemple 2.4.

Exemple 2.4 Attention aux variables non initialisées.

-- ...procedure Exemple_2_4 is

Nombre : Integer; -- Deux variables entieres sans valeurs

Resultat : Integer; -- initiales definies

begin -- Exemple_2_4

Resultat := Nombre + 1; ...

Que vaut Resultat après l’affectation? La réponse est que Resultat a unevaleur indéfinie (undefined value) car la valeur de Nombre n’était pas définie aumoment de l’affectation. La variable non initialisée Nombre possédait en fait unevaleur entière calculée à partir de la suite de bits du mot mémoire utilisé pour cettevariable. L’état de ces bits dépend de l’état (électrique) de la mémoire!

L’utilisation de variables déclarées mais non initialisées, c’est-à-dire nepossédant pas de valeur définie au moment de leur utilisation, est une erreur trèscourante. De plus, la détection de ces erreurs est difficile puisqu’il est possible quele programme s’exécute tout de même, en produisant évidemment n’importe quelsrésultats à commencer par des résultats corrects! Lors de l’utilisation de la valeurd’une variable, il faut donc toujours s’assurer que cette variable possède une valeurbien définie (note 2.3).

NOTE 2.3 Toute variable doit être initialisée.

Le programmeur qui utilise la valeur d’une variable, de quelque type que ce soit, doit être certain que

Page 33: Cours Ada de A à Z

TYPES ENTIERS 33

cette valeur est toujours bien définie.

2.2.5 Attributs First, Last, Succ et Pred

Un attribut (attribute) en Ada est une construction offerte par le langage etpermettant d’obtenir une certaine caractéristique d’une entité.

Les attributs First et Last sont prédéfinis et donnent la première,respectivement la dernière valeur d’un intervalle ou d’une suite; les attributs Succet Pred sont aussi prédéfinis et fournissent la valeur suivante, respectivementprécédente d’une valeur donnée entre parenthèses (exemple 2.5).

Exemple 2.5 Utilisation des attributs First, Last, Succ et Pred.

Integer'First donne le nombre le plus petit des entiers du type Integer;Integer'Lastdonne le nombre le plus grand des entiers du type Integer;Integer'Succ(0)donne 1;Integer'Pred(0)donne –1;Integer'Succ(Nombre)donne la valeur Nombre+1;Integer'Pred(Nombre + 1)donne la valeur Nombre;Integer'Pred(Integer'Last)donne la valeur précédent le plus grand desentiers.

2.2.6 Généralités sur les attributs

Comme mentionné auparavant, un attribut en Ada est un outil offert par lelangage qui permet d’obtenir une caractéristique d’une entité. Il faut noter lasyntaxe un peu surprenante qui utilise l’apostrophe pour indiquer que l’on a affaireà un attribut placé après celle-ci, ainsi que la présence obligatoire d’unidentificateur avant l’apostrophe.

Attention à la note 2.2! Par exemple Integer'Succ(Integer'Last)n’existe pas et le calcul de cette expression provoquera une erreur à la compilationdu programme.

Finalement, le calcul d’un attribut est l’une des opérations les plus prioritairesdans une expression. En particulier, un attribut est calculé avant n’importe quelopérateur.

2.2.7 Types Short_Integer et Long_Integer

Le langage Ada a la particularité de fournir différents types pour, dans notrecas, traiter les nombres entiers. La norme autorise une implémentation à offrir les

Page 34: Cours Ada de A à Z

TYPES ENTIERS 34

types Short_Integer et Long_Integer avec les particularités suivantes:

• le type Short_Integer a les mêmes caractéristiques qu’Integer maisl’intervalle [Short_Integer'First ; Short_Integer'Last] (ex-primé en notation mathématique) est plus petit que celui représenté par[Integer'First ; Integer'Last];

• le type Long_Integer a les mêmes caractéristiques qu’Integer maisl’intervalle [Long_Integer'First ; Long_Integer'Last] (expriméen notation mathématique) est plus grand que celui représenté par[Integer'First ; Integer'Last].

Exemple 2.6 Valeurs possibles pour les nombres les plus petits et les plus grands des types entiers.

Avec Integer sur 16 bits:Integer'First vaut –2**15;Integer'Last vaut 2**15–1;Avec Short_Integer sur 8 bits:Short_Integer'First vaut –2**7;Short_Integer'Last vaut 2**7–1;Avec Long_Integer sur 32 bits:Long_Integer'First vaut –2**31;Long_Integer'Last vaut 2**31–1;

L’existence de ces différents types entiers permet donc, en choisissant l’unplutôt que l’autre, de limiter ou d’augmenter l’intervalle des nombres entiersutilisables pour une ou plusieurs valeurs. Mais il faut toujours se rappeler que lenombre de bits, donc l’intervalle des nombres représentés, attribués à ces différentstypes, dépend de l’implémentation. Enfin, le langage permet de définir d’autrestypes entiers (sect. 6.2).

Page 35: Cours Ada de A à Z

TYPES RÉELS 35

2.3 TYPES RÉELS

2.3.1 Motivation

Les nombres réels sont indispensables dans les applications dites numériquesde la programmation (programmes d’analyse numérique, de statistiques, de calculpar éléments finis, de régulation numérique, etc.). Dans la majorité des autres cas,les nombres réels sont peu fréquents. Leur utilisation, sans un minimum deprécautions, peut s’avérer dangereuse du fait des erreurs de calcul provoquées parleur représentation en mémoire (§ 2.5.2). Comme pour les entiers, l’ensemble desnombres réels représentables sur un ordinateur est fini.

Ces nombres réels vont être présentés sans trop de détails en commençant parles réels en virgule flottante et en laissant de côté (pour l’instant) les réels envirgule fixe.

2.3.2 Généralités

Float est un identificateur prédéfini. Il signifie «un objet (constante, variable,etc.) de type Float contiendra un nombre réel en virgule flottante et rien d’autre».Les nombres réels (non explicitement basés) doivent respecter la syntaxe donnée àla figure 2.2. Le type Float fait partie des types numériques (numeric types, [ARM3.2]).

Les constantes réelles sont les nombres réels eux-mêmes. Ils peuvent êtredécimaux ou basés mais l’on ne décrira que les nombres décimaux (exemple 2.7).Le caractère _ s’utilise comme dans les nombres entiers, pour en faciliter la lecture.

Figure 2.2 Diagrammes syntaxiques définissant un nombre réel décimal.

Nombre réel décimal

Numéral . Numéral

Exposant

Exposant

E

Numéral

+e

-

Page 36: Cours Ada de A à Z

TYPES RÉELS 36

Exemple 2.7 Nombres réels décimaux (comparer avec l’exemple 2.1).

10.0 12.0E6 12.0e6 12.0E+6 12.0E–6

1_0.0 1_000.0 1_0.0E2 1.0E1_0

Les opérateurs arithmétiques possibles sur les valeurs réelles se répartissent endeux catégories:

les opérateurs unaires + – abs

où• le symbole + représente l’identité et le symbole – l’opposé;• le mot réservé abs représente l’opération «valeur absolue»;

et les opérateurs binaires + – * / **

où• le symbole + représente l’addition et le symbole – la soustraction;• le symbole * représente la multiplication et le symbole / la division;• le symbole ** représente l’exponentiation (exposant entier positif, négatif

ou nul).

Les expressions réelles sont des combinaisons de ces opérations, commeillustré dans l’exemple 2.8.

Exemple 2.8 Expressions réelles.

1.0est une expression réduite à une seule constante de valeur 1.0 qui peutégalement s’écrire 1.0e0 ou 0.1e1 ou 0.1E+1 ou encore 10.0e–1;2.0 ** (–8)est une expression de valeur 256–1;–3.0 + 4.0est une expression de valeur 1.0;4.3 * 5.0e0est une expression de valeur 21.5;4.0 / 2.0est une expression de valeur 2.0;5.0 / 2.0est une expression de valeur 2.5 (comparer avec l’exemple 2.2);2.0 + 3.0 * 4.0est une expression qui vaut 14.0 (sect. 2.4);2.0 + (3.0 * 4.0)est une expression qui vaut 14.0;(2.0 + 3.0) * 4.0est une expression qui vaut 20.0.

Page 37: Cours Ada de A à Z

TYPES RÉELS 37

2.3.3 Affectation

L’affectation s’effectue comme pour les entiers (§ 2.2.3):nom_de_variable_réelle := expression_de_type_Float;

2.3.4 Attributs First, Last et Digits

Comme pour les entiers, Ada offre des attributs pour les nombres réels. Lesattributs First et Last ont la même signification que pour les entiers. L’attributprédéfini Digits donne le nombre maximum de chiffres significatifs. Parexemple, et selon l’implémentation, Float'Digits peut donner la valeur 6.

2.3.5 Types Short_Float et Long_Float

De manière analogue aux entiers, la norme autorise une implémentation à offrirles types Short_Float et Long_Float avec les particularités suivantes:

• le type Short_Float a les mêmes caractéristiques que Float mais lenombre de chiffres significatifs est plus petit que celui de Float;

• le type Long_Float a les mêmes caractéristiques que Float mais lenombre de chiffres significatifs est plus grand que celui de Float, avec unminimum de 11.

L’intervalle des valeurs de ces deux types sera probablement également diffé-rent de celui de Float.

Page 38: Cours Ada de A à Z

PRIORITÉ DES OPÉRATEURS ARITHMÉTIQUES 38

2.4 PRIORITÉ DES OPÉRATEURS ARITHMÉTIQUES

En mathématiques une question se pose avec une expression telle que 2+3*4:quel est le résultat de ce calcul? Est-ce 20 (addition de 2 et 3 puis multiplicationpar 4) ou 14 (ajouter 2 à 3*4)?

Le même problème se pose en programmation. Pour le résoudre il faut tenircompte des priorités définies dans le langage (exemple 2.9). Celles propres à Adasont, pour les opérateurs vus jusqu’ici et dans l’ordre de priorité décroissante:

• les opérateurs ** et abs;• les opérateurs binaires * / rem et mod ;• les opérateurs unaires – et +;• les opérateurs binaires – et +.

Dans chacun de ces groupes les opérateurs sont de même priorité. Lors del’évaluation d’une expression comprenant des opérateurs de même priorité, ceux-ci s’appliquent de gauche à droite.

Exemple 2.9 Applications de la priorité des opérateurs.

Integer'First + 10calcul de Integer'First puis addition de 10;2 + 3 + 4donne 9 avec calcul de gauche à droite;2 + 3 * 4donne 14 car la sous-expression 3*4 est d’abord évaluée (prioritéde * par rapport à + );2.0 * 3.0 / 4.0donne 1.5 car l’expression est calculée de gauche à droite(opérateurs de même priorité);2 * 3 + 4 / 3donne 7 car la sous-expression 2*3 est d’abord évaluée puisla sous-expression 4/3 est calculée enfin l’addition des résultats partiels 6 et1 est effectuée.

Il est naturellement possible de préciser l’ordre d’évaluation (c’est-à-direl’ordre de calcul des constituants) d’une expression en utilisant les parenthèses ( et) comme en mathématiques. Les sous-expressions entre parenthèses sont alorscalculées en priorité (exemple 2.10).

Exemple 2.10 Parenthèses, expressions et priorités des opérateurs.

(2 + 3) * 4 2+3 donne 5, multiplié par 4 donne 20;2 + (3 * 4)3*4 donne 12, ajouté à 2 donne 14 (parenthèses inutiles);3 * (1 + 2) + 41+2 donne 3, multiplié par 3 donne 9, ajouté à 4 donne 13;abs (–2.0)donne 2.0, avec les parenthèses indispensables;3.0 * (–2.0)donne – 6.0, avec les parenthèses indispensables;

Page 39: Cours Ada de A à Z

PRIORITÉ DES OPÉRATEURS ARITHMÉTIQUES 39

5.0 ** (–2)donne 25.0–1, avec les parenthèses indispensables;(2 ** 3) ** 4donne 4096, avec les parenthèses indispensables.

Les parenthèses des quatre derniers exemples sont indispensables pourrespecter la syntaxe Ada car en écrivant par exemple 3.0*–2.0, l’ordre de prioritéimposerait de calculer d’abord 3.0*–, ce qui n’a aucun sens!

Page 40: Cours Ada de A à Z

REMARQUES RELATIVES AUX TYPES ENTIERS ET RÉELS 40

2.5 REMARQUES RELATIVES AUX TYPES ENTIERS ET RÉELS

2.5.1 Conversions de types

Il est interdit par la norme d’écrire des expressions composées d’un mélanged’opérandes entiers et réels. S’il est nécessaire de former de telles expressions,alors il faut décider si leur valeur sera entière ou réelle et utiliser des conversionsexplicites de type (exemple 2.11) entre Integer et Float pour s’assurer quechaque sous-expression (opérande opérateur opérande) est formée d’opérandes demême type. Une conversion explicite de type a la forme:

nom_de_type ( expression )

où• le nom_de_type sert à convertir la valeur de l’expression en une valeur de

ce type.

Exemple 2.11 Exemples de conversions explicites entre Integer et Float.

Float ( 5 )5 est converti en 5.0;Integer ( 2.03 )2.03 est converti en 2;Float ( –800 ) / 2.5E2l’expression vaut –3.2;Integer ( 3.5 ) 3.5 est converti en 4;Integer ( –2.5 ) –2.5 est converti en –3.

La conversion d’une valeur réelle en une valeur entière est faite par arrondi versl’entier le plus proche.

2.5.2 Valeurs entières et réelles utilisables en Ada

Les valeurs entières utilisables sont celles comprises dans l’intervalle[System.Min_Int;System.Max_Int] (en notation mathématique) dont lesdeux bornes ont bien entendu une valeur dépendant de l’ordinateur et ducompilateur Ada utilisé. Sans entrer maintenant dans les détails, il faut soulignerque les constantes Min_Int et Max_Int sont mises à disposition par le paquetageprédéfini System (sect. 19.3). Tous les types entiers ont leur domaine de définition(§ 2.2.7) inclus dans l’intervalle ci-dessus.

Les valeurs réelles en virgule flottante utilisables dépendent de lareprésentation en mémoire des nombres réels. Un tel nombre est enregistré sousforme d’une mantisse et d’un exposant, le tout implémenté généralement sur 32 ou64 bits. Par exemple, le nombre 0.10012e13 est pour le programmeur constituéde 0.10012 pour la mantisse et de 13 pour l’exposant. Mais en réalité, le nombrede chiffres significatifs est en relation avec le nombre de bits dédiés à lareprésentation de la mantisse; les bits restants sont eux utilisés pour l’exposant.

Page 41: Cours Ada de A à Z

REMARQUES RELATIVES AUX TYPES ENTIERS ET RÉELS 41

Dans tous les cas il faut se méfier lors de calculs avec les nombres réels,particulièrement si ces nombres sont grands ou petits. En effet, une addition telleque 1.0e45 + 1.0e–40 donne 1.0e45 !

RAPPEL: les décimales dont la position excède le nombre de chiffres signi-ficatifs ne signifient plus rien!

Page 42: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 42

2.6 DIALOGUE PROGRAMME-UTILISATEUR

2.6.1 Motivation

La qualité du dialogue entre l’utilisateur et un logiciel est une propriétéfondamentale d’une partie dudit logiciel appelée interface homme-machine (userinterface). Ce dialogue est rendu possible par l’existence de périphériquesspécialisés comme le clavier, la souris, le microphone, etc., qui permettent l’entréed’informations dans la machine, et de périphériques comme l’écran, l’imprimante,le traceur ou les hauts-parleurs qui permettent la sortie de résultats ou de messagespour l’utilisateur. L’importance du dialogue réside dans le fait que plus celui-ci estcompréhensible, cohérent et agréable, plus l’utilisateur acceptera ou aura envied’utiliser le logiciel. Sans vouloir approfondir ici la notion de dialogue (cela ne faitpas partie d’une introduction à la programmation), il faut simplement mentionnerqu’il consiste entre autres pour l’utilisateur en:

• l’introduction des données;• la commande du logiciel;• la compréhension du déroulement des opérations;• la compréhension des résultats obtenus;

et pour le programme, en:

• la demande des données nécessaires à son exécution;• la production de résultats lisibles et clairement présentés;• la quittance des opérations importantes effectuées;• la mise en garde de l’utilisateur en cas de donnée erronée;• la mise en garde de l’utilisateur en cas de commande erronée ou dan-

gereuse.

Même s’il existe des logiciels spécialisés de conception et de réalisationd’interfaces homme-machine, la programmation d’un dialogue textuel en Ada sebasera sur les outils de base que sont d’une part les deux opérations Get etGet_Line (§ 9.2.3) pour la lecture de données (nombres, caractères...), d’autrepart Put et Put_Line pour l’affichage de messages ou de résultats. Ces quatreopérations sont mises à disposition dans des paquetages (sect. 10.2) prédéfinis,spécialisés dans les entrées-sorties de texte tels que Ada.Text_IO, les entrées-sorties d’entiers Ada.Integer_Text_IO ou de réels Ada.Float_Text_IO.

Dans nos programmes et pour des raisons de simplicité, l’utilisation d’autresmécanismes indispensables à tout interface homme-machine actuel ou futur com-me la souris, la reconnaissance vocale, les écrans tactiles, etc., sera complètementignorée.

2.6.2 Lecture de nombres entiers ou réels

Pour effectuer la lecture d’une valeur entière ou réelle en respectant les

Page 43: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 43

quelques principes cités au paragraphe 2.6.1, le programme doit, dans l’ordre:

• afficher un message clair à l’utilisateur pour lui indiquer ce qu’il doit faire;• attendre que l’utilisateur ait introduit la valeur;• lire la valeur;• vérifier la validité de la valeur obtenue (§ 6.3.5);• reprendre son exécution.

L’exemple 2.12 illustre l’utilisation des opérations d’entrées-sorties et réaliseune ébauche de dialogue entre le programme et l’utilisateur. Il faut biencomprendre que ce dialogue obéit à un protocole fixé dans le programme et quel’utilisateur doit suivre.

Exemple 2.12 Ebauche de dialogue entre un programme et son utilisateur.

with Ada.Text_IO;with Ada.Integer_Text_IO;with Ada.Float_Text_IO;

-- Calcul du volume d'un mur de briquesprocedure Ebauche_Dialogue is

-- Nombre de briques en longueurLongueur_Mur : Integer;

-- Nombre de briques en hauteurHauteur_Mur : Integer;

-- Dimensions d'une briqueLongueur_Brique : Float;Largeur_Brique : Float;Hauteur_Brique : Float;

-- Volume du mur de briquesVolume : Float;

-- Autres declarations......

begin -- Ebauche_Dialogue

-- Presentation du programme......

-- Obtenir le nombre de briques formant le mur en longueurAda.Text_IO.Put ( "Donnez le nombre de briques " );Ada.Text_IO.Put ( "(longueur du mur): " );Ada.Integer_Text_IO.Get ( Longueur_Mur );

-- Obtenir le nombre de briques formant le mur en hauteurAda.Text_IO.Put ( "Donnez le nombre de briques " );Ada.Text_IO.Put ( "(hauteur du mur): " );Ada.Integer_Text_IO.Get ( Hauteur_Mur );

-- Obtenir les dimensions d'une briqueAda.Text_IO.Put ( "Donnez les dimensions d'une brique. " );Ada.Text_IO.Put ( "La longueur: " );

Page 44: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 44

Ada.Float_Text_IO.Get ( Longueur_Brique );Ada.Text_IO.Put ( "La largeur: " );Ada.Float_Text_IO.Get ( Largeur_Brique );Ada.Text_IO.Put ( "La hauteur: " );Ada.Float_Text_IO.Get ( Hauteur_Brique );

-- Calcul du volume du murVolume := Longueur_Brique * Largeur_Brique * Hauteur_Brique *

Float ( Longueur_Mur * Hauteur_Mur );

-- Montrer a l'utilisateur la valeur du volume du murAda.Text_IO.Put ( " Le volume du mur de briques vaut : " );Ada.Float_Text_IO.Put ( Volume );Ada.Text_IO.New_Line;...

end Ebauche_Dialogue;

Comment la lecture des dimensions d’une brique et du mur est-elle effectuée?L’exemple de la figure 2.3 va illustrer cette opération.

Figure 2.3 Lecture de valeurs données par l’utilisateur.

Après l’exécution de Ada.Float_Text_IO.Get(Longueur_Brique); lavariable Longueur_Brique vaut 30.0, Largeur_Brique et Hauteur_Briquesont indéfinies.

Après l’exécution de Ada.Float_Text_IO.Get(Largeur_Brique); lavariable Largeur_Brique vaut 20.0, Longueur_Brique garde sa valeur 30.0et Hauteur_Brique est encore indéfinie.

Après l’exécution de Ada.Float_Text_IO.Get(Hauteur_Brique); lavariable Hauteur_Brique vaut 15.5, Longueur_Brique et Largeur_Briqueconservent leur valeur.

Les valeurs ont été lues par le programme en respectant l’ordre dans lequel elles

programme ce que l’utilisateur a donné

Ada.Float_Text_IO.Get(Longueur_Brique);

Ada.Float_Text_IO.Get(Largeur_Brique);

Ada.Float_Text_IO.Get(Hauteur_Brique);

30.0 20.0 15.5

Page 45: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 45

ont été données par l’utilisateur. Par ailleurs, les nombres doivent être séparés parun (ou plusieurs) espace(s) ou par une (ou plusieurs) fin(s) de ligne. Finalement lesespaces et les fins de lignes sont sautés (ignorés) lorsque le prochain nombre à lireest précédé de tels séparateurs.

Concernant l’écriture en Ada des instructions d’entrées-sorties, il fautremarquer la correspondance obligatoire entre le type de la variable et le nom dupaquetage utilisé comme préfixe aux opérations Get et Put. Il serait en effetincorrect d’écrire l’une des instructions suivantes:

Ada.Float_Text_IO.Get (Longueur_Mur);Ada.Integer_Text_IO.Get (Longueur_Brique);Ada.Float_Text_IO.Put (Longueur_Mur);Ada.Integer_Text_IO.Put (Longueur_Brique);

2.6.3 Passage à la ligne lors de la lecture

Il existe des situations où il est nécessaire de sauter tout ce que l’utilisateur atapé au clavier (§ 6.3.5 par exemple) jusqu’à la fin de la ligne. L’opérationSkip_Line effectue cette tâche en lisant tous les caractères restants de la ligne eten les éliminant, y compris la fin de ligne (fig. 2.4).

Dans la pratique, l’utilisateur marque une fin de ligne en introduisant au clavierle caractère de contrôle de retour de chariot (§ 1.10.2).

Figure 2.4 Elimination de valeurs données par l’utilisateur.

Après l’exécution de Ada.Float_Text_IO.Get(Longueur_Brique); lavariable Longueur_Brique vaut 30.0, Largeur_Brique et Hauteur_Briquesont indéfinies. Après l’exécution de l’instruction Ada.Text_IO.Skip_Line; lereste de la ligne a été sauté.

Après l’exécution de Ada.Float_Text_IO.Get(Largeur_Brique); la va-riable Largeur_Brique vaut 10.0, Longueur_Brique garde sa valeur 30.0 et

programme ce que l’utilisateur a donné

Ada.Float_Text_IO.Get(Longueur_Brique);

Ada.Text_IO.Skip_Line;

Ada.Float_Text_IO.Get(Largeur_Brique);

30.0 20.0 15.5

Ada.Text_IO.Skip_Line;

Ada.Float_Text_IO.Get(Hauteur_Brique);

10.05.0 1.0

Page 46: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 46

Hauteur_Brique est encore indéfinie. Après l’exécution de l’instructionAda.Text_IO.Skip_Line; le reste de la ligne a été sauté.

Après l’exécution de Ada.Float_Text_IO.Get(Hauteur_Brique); la va-riable Hauteur_Brique vaut 5.0, Longueur_Brique et Largeur_Briqueconservent leur valeur. Le nombre 1.0 subsiste pour une éventuelle future lecture.

2.6.4 Affichage de messages sur l’écran

Un programme bien conçu affiche toujours des messages à l’écran indiquant àl’utilisateur qu’il faut introduire une donnée, qu’une opération est terminée, etc. EnAda, ces messages sont en fait des constantes du type String (sect. 9.2) appeléeschaînes de caractères et toujours écrites entre guillemets (exemple 2.13).

Exemple 2.13 Exemples de chaînes de caractères.

"ceci est une chaine de caracteres""Attention: si un guillemet doit etre place dans unechaine"" de caracteres, il faut le dedoubler ainsi "" CQFD."

L’affichage d’un message sur l’écran, pour l’utilisateur, est maintenant facile:Ada.Text_IO.Put ( "Donnez les dimensions d’une brique " );

2.6.5 Complément sur le dialogue homme-machine

Tout bon dialogue avec l’utilisateur doit respecter les deux règles données dansla note 2.4.

NOTE 2.4 Indication à l’utilisateur que son programme s’exécute.

Tout programme doit annoncer le début de son exécution par un message à l’utilisateur. Celui-ciobtient donc la confirmation du début d’exécution du programme.Tout programme doit faire patienter l’utilisateur par un message d’avertissement lorsque leprogramme effectue des opérations nécessitant un temps relativement long, plusieurs secondes parexemple.

2.6.6 Affichage de valeurs entières ou réelles

L’opération Put s’utilise aussi pour afficher une valeur entière ou réelle àl’écran. Par exemple:

Ada.Float_Text_IO.Put ( Volume ); -- Volume de type Float

Page 47: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 47

Ada.Integer_Text_IO.Put ( Longueur_Mur ); -- Longueur_Mur de-- type Integer

S’il faut afficher plusieurs valeurs, il est nécessaire d’utiliser une opération Put parvaleur. La notation scientifique est utilisée pour une valeur réelle (sauf en cas demention explicite du contraire).

2.6.7 Mise en page du texte affiché par un programme

Toute valeur entière est affichée sur un certain nombre de positions. Ce nombrede positions dépend du type des valeurs et correspond au nombre maximum dechiffres possibles plus un. Par exemple, si le type Integer est sur 32 bits, la plusgrande valeur de ce type, 2_147_483_647, est composée de 10 chiffres. N’importequelle valeur de ce type Integer sera donc affichée sur 11 positions.

De manière analogue, toute valeur réelle est écrite en notation scientifique avec2 positions avant le point décimal, N positions après le point décimal où N est lenombre de chiffres significatifs du type et 3 positions pour l’exposant.

Il est cependant possible de modifier cette mise en page. Pour les valeursentières, il suffit de compléter l’opération Put avec le nombre de positionssouhaitées. Par exemple:

Ada.Integer_Text_IO.Put ( Longueur_Mur, 8 );

Pour les valeurs réelles, le procédé est le même, mais en donnant une suite detrois nombres pour, dans l’ordre, le nombre de positions avant le point décimal,après le point décimal et pour l’exposant. Par exemple:

Ada.Float_Text_IO.Put ( Volume, 3, 9, 2 );

2.6.8 Passage à la ligne lors de l’affichage

Il est naturellement possible d’afficher une suite formée de messages et devaleurs entières ou réelles en utilisant plusieurs fois l’opération Put commementionné auparavant (§ 2.6.6). Mais tous ces textes et nombres vont être disposésles uns à la suite des autres! Le programmeur peut alors décider à quel moment ilfaut terminer la ligne affichée et passer à la suivante en utilisant l’opérationNew_Line. Par exemple:

Ada.Text_IO.Put ( "Avec un mur d’une longueur de " );Ada.Integer_Text_IO.Put ( Longueur_Mur );Ada.Text_IO.New_Line;Ada.Text_IO.Put ( "Le volume du mur de briques vaut : " );Ada.Float_Text_IO.Put ( Volume );Ada.Text_IO.New_Line;

En supposant que l’utilisateur a donné 50 pour la longueur du mur et que levolume calculé vaut 1980.5, l’utilisateur verra donc s’afficher:

Avec un mur d’une longueur de 50

Page 48: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 48

Le volume du mur de briques vaut : 1.98050E+03

Il existe finalement la possibilité de contracter les deux opérations Put etNew_Line par Put_Line lors de l’affichage d’un message (et uniquement dans cecas). Par exemple, les deux instructions:

Ada.Text_IO.Put ( "Le programme est maintenant termine!" );Ada.Text_IO.New_Line;

seront souvent écrites comme suit:Ada.Text_IO.Put_Line ( "Le programme est maintenant termine!" );

2.6.9 Abréviations d’écriture dans un programme

Dans un programme Ada, l’utilisation intensive des préfixes commeAda.Text_IO ou Ada.Integer_Text_IO diminue la lisibilité du programme.En effet, tout programmeur Ada sait que les opérations comme Get, Put ouNew_Line viennent des paquetages d’entrées-sorties prédéfinis.

Il est possible d’omettre ces préfixes en ajoutant, dans la clause de contexte, desdirectives permettant au compilateur de traduire le code Ada comme si ces préfixesétaient mentionnés là où ils sont nécessaires. Ces notions seront développées dansle paragraphe mentionnant la notion de clause use (§ 10.5.2). L’exemple 2.14présente le style d’écriture usuel pour les entrées-sorties, sans les préfixes.

Exemple 2.14 Abréviations d’écriture pour les opérations d’entrées-sorties.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;with Ada.Float_Text_IO; use Ada.Float_Text_IO;-- ...

procedure Dialogue is...

begin -- Dialogue...-- Obtenir le nombre de briques formant le mur en longueur

-- Ada.Text_IO est sous-entendu comme prefixe des PutPut ( "Donnez le nombre de briques " );Put ( "(longueur du mur): " );

-- Ada.Integer_Text_IO est sous-entendu comme prefixe de GetGet ( Longueur_Mur );

-- Obtenir le nombre de briques formant le mur en largeur

-- Ada.Text_IO est sous-entendu comme prefixe des PutPut ( "Donnez le nombre de briques " );Put ( "(hauteur du mur): " );

-- Ada.Integer_Text_IO est sous-entendu comme prefixe de GetGet ( Hauteur_Mur );

Page 49: Cours Ada de A à Z

DIALOGUE PROGRAMME-UTILISATEUR 49

-- Obtenir les dimensions d'une brique

-- Ada.Text_IO est sous-entendu comme prefixe des PutPut ( "Donnez les dimensions d'une brique " );Put ( "(longueur largeur hauteur): " );

-- Ada.Float_Text_IO est sous-entendu comme prefixe des GetGet ( Longueur_Brique );Get ( Largeur_Brique );Get ( Hauteur_Brique );

-- Calcul du volume du murVolume := Longueur_Brique * Largeur_Brique * Hauteur_Brique *

Float ( Longueur_Mur * Hauteur_Mur );

-- Montrer a l'utilisateur la valeur du volume du mur

-- Ada.Text_IO est sous-entendu comme prefixe de PutPut ( " Le volume du mur de briques vaut : " );

-- Ada.Integer_Text_IO est sous-entendu comme prefixe de PutPut ( Volume );

-- Ada.Text_IO est sous-entendu comme prefixe de New_LineNew_Line;...

end Dialogue;

Page 50: Cours Ada de A à Z

50

2.7 EXERCICES

2.7.1 Utilisation des opérations arithmétiquesEcrire un programme qui calcule la somme, la différence, le produit, la division

et le reste de la division entre deux nombres entiers donnés par l’utilisateur duprogramme.

2.7.2 Utilisation des opérations arithmétiquesEcrire un programme qui calcule le nombre minimum de billets et de pièces de

monnaie nécessaires pour décomposer une somme d’argent donnée par l’utilisateurdu programme.

2.7.3 Utilisation d’attributs

Ecrire un programme qui affiche les bornes des types Integer et Float, ainsique le nombre de chiffres significatifs de Float. En faire de même avec d’autrestypes comme Short_Integer, Long_Float, etc. (§ 2.2.7 et 2.3.5) selonl’implémentation à disposition.

2.7.4 Correction d’erreursTrouver les erreurs de compilation puis, après correction, celles de logique

conduisant à des résultats erronés dans le programme suivant:procedure Erreurs is

Entier_1 : Integer;Reel_1 : Float := 5;

begin -- Erreurs

-- Valeur mediane des entiersEntier_1 := Integer'Last + Integer'First/2;Put("Valeur mediane des valeurs du type Integer", Entier_1);

-- Quelques puissancesReel_1 := Reel_1**5 + (Entier_1+5)**3;Put("Un calcul de puissances:", Reel_1);New_Line;

end Erreur;

Page 51: Cours Ada de A à Z

POINTS À RELEVER 51

2.8 POINTS À RELEVER

2.8.1 En général

• L’intervalle des valeurs numériques entières ou réelles est toujours fini.

• L’affectation consiste à donner, changer la valeur d’une variable.

• Attention aux variables non initialisées qui sont toujours des sourcesd’erreurs.

• Les opérateurs possèdent tous une priorité définissant précisément l’ordred’évaluation d’une expression.

• Les parenthèses peuvent servir à modifier l’ordre d’évaluation d’uneexpression.

• Lors de l’introduction de données par un utilisateur, un dialogue doits’instaurer entre le programme et lui-même.

2.8.2 En Ada

• Les attributs doivent être utilisés chaque fois que c’est possible pourfaciliter la maintenance des programmes Ada.

• Dans une expression, le mélange de valeurs entières et réelles est interdit.

• Une conversion explicite de type peut transformer une valeur entière en sonéquivalent réel et inversement (arrondi).

Page 52: Cours Ada de A à Z

52

C H A P I T R E 3

S É L E C T I O N , I T É R A T I O N , A U T R E S T Y P E S D E B A S E

Page 53: Cours Ada de A à Z

53 SÉLECTION, ITÉRATION, AUTRES TYPES DE BASE

Page 54: Cours Ada de A à Z

INSTRUCTION IF SANS ALTERNATIVE 54

3.1 INSTRUCTION IF SANS ALTERNATIVE

3.1.1 Motivation

Les premiers programmes présentés s’exécutaient en séquence, c’est-à-dire uneinstruction après l’autre en suivant leur ordre d’écriture dans le corps duprogramme. Pour modifier cette situation, il est possible de programmer unesélection (un choix), c’est-à-dire la manière d’exécuter ou de ne pas exécuter unesuite d’instructions. Cette possibilité est donnée par l’instruction if.

3.1.2 Instruction if sans alternative

Comment écrire un programme qui affiche tous les nombres impairs entre deuxbornes ? Soit Nb_Courant le nombre examiné; déterminer s’il est pair ou impairpeut être réalisé en considérant le reste de la division (entière) par 2. En effet, sil’expression abs Nb_Courant rem 2 vaut 0 alors Nb_Courant est pair, mais siabs Nb_Courant rem 2 vaut 1 alors Nb_Courant est impair (rem est l’opérateurdonnant le reste de la division entière et abs la valeur absolue). Il est donc possiblede n’afficher que les nombres impairs en effectuant une sélection en fonction de lavaleur d’une condition. Cela s’écrit:

if abs Nb_Courant rem 2 = 1 then -- Nb_Courant est-il impair?Put ( "Le nombre " );Put ( Nb_Courant );Put ( "est impair" );New_Line;

end if;

Le message Le nombre ... est impair n’est affiché que si la conditionabs Nb_Courant rem 2 = 1 est vraie, i.e. lorsque Nb_Courant est impair.

L’instruction if, appelée aussi énoncé conditionnel, s’utilise donc chaque foisqu’un choix entre différentes options doit être programmé. Sa forme générale (sansalternative) est:

if condition thensuite_d_instructions;end if;

où• if et then sont de nouveaux mots réservés;• la condition est en fait une expression dont le résultat d’évaluation doit

être soit vrai soit faux; une telle expression est appelée expression boolé-enne (§ 3.4.2).

• la suite_d_instructions est exécutée si la condition est vraie; rienne se passe si elle est fausse.

Soient expression_1 et expression_2 deux expressions entières. Alors ilest possible de construire des expressions booléennes selon l’exemple 3.1.

Page 55: Cours Ada de A à Z

INSTRUCTION IF SANS ALTERNATIVE 55

Exemple 3.1 Exemples d’expressions booléennes.

expression_1 = expression_234+5 = 56–17 est vraie;expression_1 /= expression_234+5 /= 56–17 est fausse;expression_1 <= expression_21 <= 5 est vraie, de même que 1 <= 1;expression_1 < expression_25 < 3*3 est vraie;expression_1 >= expression_25 >= 1 est vraie, de même que 1 >= 1;expression_1 > expression_21 > 5 est fausse.

Les six symboles = /= <= < >= > sont appelés opérateurs de comparaison.Il faut encore relever que plusieurs conditions peuvent être combinées en une seule,par exemple:

if abs Nb_Courant rem 2 = 1 and Nb_Courant > 20 then ...

Le mot réservé and est un opérateur booléen réalisant la fonction logique et.Tout ceci sera présenté avec la notion du type Boolean (sect. 3.4).

Page 56: Cours Ada de A à Z

INSTRUCTION IF AVEC ALTERNATIVE 56

3.2 INSTRUCTION IF AVEC ALTERNATIVE

Comment modifier cet exemple de manière à afficher un message siNb_Courant est impair et un autre message s’il est pair? Cela s’écrit:

Put ( "Le nombre " );Put ( Nb_Courant );

if abs Nb_Courant rem 2 = 1 then -- Nb_Courant est-il impair?Put ( "est impair" ); -- Nb_Courant est impair

elsePut ( "est pair" ); -- Nb_Courant est pair

end if;

New_Line;

La suite d’instructions comprise entre les mots réservés else et end if estexécutée si la condition est fausse, ici si Nb_Courant est pair. La forme généralede l’instruction if avec alternative est:

if condition thensuite_d_instructions_1;elsesuite_d_instructions_2;end if;

où• else est un nouveau mot réservé;• si la condition est vraie, alors la suite_d_instructions_1 est

exécutée, sinon c’est suite_d_instructions_2 qui l’est.

Page 57: Cours Ada de A à Z

INSTRUCTION IF GÉNÉRALISÉE 57

3.3 INSTRUCTION IF GÉNÉRALISÉE

Finalement, comment modifier une dernière fois notre exemple de manière àafficher un message variant selon la valeur de Nb_Courant? Ce message afficherasimplement l’ordre de grandeur (unité, dizaine, centaine, millier ou plus) deNb_Courant. Cela s’écrit:

Nb_Courant := abs Nb_Courant; -- Le nombre en valeur absolue

Put ( "Le nombre " );Put ( Nb_Courant );

if Nb_Courant < 10 then -- Nb_Courant est-il une unite?Put ( "est une unite" );

elsif Nb_Courant < 100 then -- Nb_Courant est-il une dizaine?Put ( "est une dizaine" );

elsif Nb_Courant < 1000 then -- Nb_Courant est-il une centaine?

Put ( "est une centaine" );

elsif Nb_Courant < 10_000 then -- Nb_Courant est-il un millier?Put ( "est un millier" );

else -- Nb_Courant est alors plus grand que 9999

Put ( "est plus qu’un millier" );

end if;

New_Line;

L’exécution de cette instruction if généralisée commence par l’évaluation (lecalcul) de l’expression Nb_Courant < 10. Si elle est vraie le message "est uneunite" s’affiche et le reste de l’instruction est sauté. Si Nb_Courant < 10 estfausse, alors l’expression suivante (dans l’ordre d’écriture) Nb_Courant < 100est évaluée. Si elle est vraie le message "est une dizaine" s’affiche et le restede l’instruction est sauté. Sinon l’expression suivante est évaluée et ainsi de suite.Finalement, si toutes les expressions sont fausses, le message "est plus qu’unmillier" placé après le mot réservé else est effectué.

La forme générale de l’instruction if généralisée est:if condition_1 thensuite_d_instructions_1;elsif condition_2 thensuite_d_instructions_2;elsif condition_3 thensuite_d_instructions_3;...elseautre_suite_d_instructions;end if;

Page 58: Cours Ada de A à Z

INSTRUCTION IF GÉNÉRALISÉE 58

où• elsif est un nouveau mot réservé;• la suite_d_instructions_i est exécutée pour la première

condition_i vraie dans l’ordre d’écriture et le reste de l’instruction ifest sauté; si toutes ces conditions sont fausses, c’estautre_suite_d_instructions qui l’est.

On appelle branche chacune des suites d’instructions précédée de la lignecontenant la condition. La branche commençant par else doit être la dernière etest optionnelle. Par conséquent, si toutes les conditions sont fausses et que cettebranche n’est pas présente, aucune instruction de l’instruction if généralisée n’estexécutée.

Page 59: Cours Ada de A à Z

LE TYPE BOOLEAN 59

3.4 LE TYPE BOOLEAN

3.4.1 Généralités

Boolean est un identificateur prédéfini. Il signifie «un objet (constante,variable, expression...) de type Boolean aura la valeur vrai ou faux et riend’autre». Le type Boolean fait partie des types discrets (discrete types, [ARM3.2]).

Les constantes booléennes sont False et True, qui sont aussi desidentificateurs prédéfinis et qui représentent respectivement les valeurs faux etvrai.

Les opérations possibles sur les valeurs de type Boolean sont:and or xor not = /= <= < >= >

où• and représente la fonction logique et;• or représente la fonction logique ou (non exclusif);• xor représente la fonction logique ou exclusif;• not représente la fonction logique non;• comme déjà mentionné (§ 3.1.2) = /= <= < >= et > représentent les

opérateurs de comparaison.

Ces quatre nouveaux opérateurs sont appelés opérateurs logiques (ouopérateurs booléens). Les opérateurs de comparaison possèdent un sens bien définicar, par définition, le type Boolean est ordonné, avec False < True.

3.4.2 Expressions booléennes

Les expressions booléennes ou logiques s’écrivent comme présenté dans lesexemples 3.1 et 3.2. Les parenthèses servent à préciser l’ordre d’évaluation. Deplus, la priorité des opérateurs logiques est (dans l’ordre décroissant):

not(le plus prioritaire)and or xor(même priorité entre eux)

Relevons qu’il semble alors possible de construire des expressions ambiguëscomme Trouve and Present or Existe. Mais en fait une telle expression estinterdite car Ada exige l’utilisation des parenthèses lorsque des opérateurs logiquesdifférents (parmi and, or et xor) coexistent dans une expression. L’expressionprécédente doit donc s’écrire (Trouve and Present) or Existe ou alorsTrouve and (Present or Existe).

Exemple 3.2 Exemples d’expressions booléennes.

Trouve : Boolean;déclaration de deux variables booléennes;

Page 60: Cours Ada de A à Z

LE TYPE BOOLEAN 60

Present : Boolean;

Nombre : Integer;déclaration d’une variable entière;Trouve and Presentexpression vraie si Trouve et Present sont vrais;not Trouveexpression vraie si Trouve est faux;Trouve or Presentexpression vraie si Trouve ou Present est vrai;not (Trouve and Present)expression vraie si Trouve ou Present estfaux;not Trouve or not Presentexpression équivalente à la précédente;Nombre = 0 or Nombre = 1expression vraie si Nombre vaut 0 ou 1;Trouve and Nombre = 0expression vraie si Trouve est vraie et si Nombrevaut 0.

A titre de synthèse, tous les opérateurs existant en Ada sont donnés dans letableau 3.1, par ordre décroissant de priorité.

3.4.3 Formes de contrôle en raccourci and then et or else

Sans pouvoir en donner maintenant l’utilité, il faut relever qu’il existe lesformes de contrôle en raccourci and then et or else, qui ne sont pas desopérateurs, mais qui s’utilisent comme les opérateurs and et or, avec le mêmeniveau de priorité.

Soit l’expression booléenne expression_1 and then expression_2 (oùexpression_1 et expression_2 sont aussi booléennes). Expression_1 estd’abord calculée. Si elle est fausse, alors expression_2 n’est pas calculée etl’expression complète est aussi fausse. Si expression_1 est vraie alorsl’expression complète aura la valeur de expression_2.

Soit l’expression booléenne expression_1 or else expression_2 (où

Tableau 3.1 Opérateurs dans l’ordre décroissant de priorité.

Opérateurs Classes d’opérateurs

** abs not opérateurs prioritaires

* / rem mod opérateurs multiplicatifs

+ – opérateurs additifs unaires

+ – & opérateurs additifs binaires

= /= <= < >= > opérateurs de comparaison

and or xor opérateurs logiques

Page 61: Cours Ada de A à Z

LE TYPE BOOLEAN 61

expression_1 et expression_2 sont aussi booléennes). Expression_1 estd’abord calculée. Si elle est vraie, alors expression_2 n’est pas calculée etl’expression complète est aussi vraie. Si expression_1 est fausse alorsl’expression complète aura la valeur de expression_2.

L’exemple 3.3 illustre l’utilisation des deux formes de contrôle en raccourci.

Exemple 3.3 Exemples d’utilisation de formes de contrôle en raccourci.

Trouve : Boolean := True;déclaration de deux variables booléennes;Present : Boolean := False;

Present and then Trouveexpression fausse (car Present est fausse) etTrouve n’est pas évaluée;Trouve and then Presentexpression fausse (car Present est fausse);Trouve or else Presentexpression vraie (car Trouve est vraie) etPresent n’est pas évaluée;Present or else Trouveexpression vraie (car Trouve est vraie).

Il faut encore mentionner les tests d’appartenance in et not in (§ 6.1.5) quidonnent également un résultat booléen et qui possèdent le même niveau de prioritéque les opérateurs de comparaison. Ces tests d’appartenance, comme les formes decontrôle en raccourci, ne sont pas des opérateurs car, contrairement aux opérateurs,il n’est pas possible de les surcharger (sect. 4.9).

3.4.4 Affectation

L’affectation s’effectue comme dans l’exemple 3.4.

Exemple 3.4 Affectations d’expressions booléennes à une variable booléenne.

-- ...procedure Exemple_3_4 is

Trouve : Boolean; -- Deux variables booleennesPresent : Boolean := False;Nb_Courant : Integer := ...; -- Une variable entiere

begin -- Exemple_3_4

Trouve := True; -- Affecte True a TrouveTrouve := Trouve and Present; -- Affecte False a TrouveTrouve := Present; -- Affecte False a Trouve

Trouve := Nb_Courant = 25; -- Trouve obtient la valeur True-- si Nb_Courant vaut 25, False-- sinon

...

Page 62: Cours Ada de A à Z

LE TYPE BOOLEAN 62

De manière générale, l’affectation s’écrit:nom_de_variable_booléenne := expression_de_type_Boolean;

3.4.5 Entrées-sorties

Comme pour tous les types simples, Ada offre la possibilité d’effectuer desentrées-sorties sur des valeurs de type Boolean. Sans en décrire ni expliquer lemécanisme utilisé (sect. 17.4), ces entrées-sorties sont disponibles sous les nomsPut et Get après avoir effectué la déclaration suivante:

package ES_Boolean is new Ada.Text_IO.Enumeration_IO ( Boolean );

Exemple 3.5 Entrées-sorties de valeurs booléennes.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_3_5 is

Trouve : Boolean; -- Deux variables booleennesPresent : Boolean := False;Nb_Courant : Integer := ...; -- Une variable entiere

package ES_Boolean is new Ada.Text_IO.Enumeration_IO(Boolean);use ES_Boolean; -- § 2.6.9

begin -- Exemple_3_5

Put_Line (" Cet exemple..." );

Get (Trouve); -- Lit une valeur booleennePut (Trouve); -- Affiche la valeur lue

Put (Trouve and Present); -- Affiche la valeur de-- l'expression booleenne-- Trouve and Present

Put (Nb_Courant = 25); -- Affiche TRUE si Nb_Courant-- vaut 25, FALSE sinon

...

Comme suggéré dans la dernière instruction de l’exemple 3.5, les valeursbooléennes TRUE et FALSE sont toujours affichées en majuscules sur le nombreminimum de positions. A noter qu’il est possible de modifier ces aspectsd’affichage [ARM A.10.10].

Page 63: Cours Ada de A à Z

ITÉRATION: LA BOUCLE FOR 63

3.5 ITÉRATION: LA BOUCLE FOR

3.5.1 Motivation

Dans tout programme d’une certaine taille, des actions sont répétées plusieursfois. Ici (fig. 3.1) le programmeur veut créer un dessin formé de dix triangles.

Figure 3.1 La figure à dessiner.

Le lecteur remarque immédiatement que cela revient à dessiner dix fois lemême triangle. On appelle itération (iteration) la répétition d’un grouped’instructions. Cet exemple comporte donc dix itérations.

3.5.2 Généralités

La réalisation d’itérations se fait en utilisant des instructions appeléescommunément boucles (il y a trois boucles différentes en Ada). Le fait que lenombre d’itérations à effectuer soit calculable avant l’arrivée dans la boucleconduit à choisir l’instruction for. Cette instruction s’écrit:

for I in 1..10 loop-- dessiner le triangle

end loop;

où• I est une variable entière déclarée automatiquement.

Cette instruction for va s’exécuter de la manière suivante: la variable de boucleI va prendre successivement les valeurs 1, 2, 3, ..., 9 et 10 et, pour chacune de cesvaleurs, l’action dessiner le triangle qui suit le mot réservé loop va s’effec-tuer. Un premier essai de programme complet est donné dans l’exemple 3.6.

Exemple 3.6 Premier essai de programme pour dessiner les dix triangles.

with Spider; use Spider; -- Pour pouvoir dessiner

-- ...procedure Triangle_10_Premier_Essai is

-- Nombre de triangles a dessinerNombre_De_Triangles : constant := 10;

X_Init : constant := 10; -- Abscisse du point initialY_Init : constant := 30; -- Ordonnée du point initial

Page 64: Cours Ada de A à Z

ITÉRATION: LA BOUCLE FOR 64

begin -- Triangle_10_Premier_Essai

-- Presentation du programme...

-- Pour pouvoir dessiner dans la fenetre de dessinInit_Window ("Fenetre de dessin");

-- Deplacement au point initial de dessinMove_To (X_Init, Y_Init);

-- Dessiner les dix trianglesfor I in 1..Nombre_De_Triangles loop

Line (40, 0); -- Dessin de la baseLine (–20, –20); -- Dessin du cote a droiteLine (–20, 20); -- Dessin du dernier cote

end loop;

end Triangle_10_Premier_Essai;

Une lecture attentive de ce programme fait apparaître un problème: les trianglesseront tous dessinés au même endroit. Il faut donc changer de point initial avantchaque dessin. On remarque que pour le nième triangle, ce point a les coordonnées(N_Init+(n–1)*40, Y_Init).

D’autre part, les nombres 20 et 40 sont à remplacer par une ou des constantesen vertu des bonnes habitudes de programmation. Le code source modifié enconséquence devient alors correct et l’on obtient l’exemple 3.7.

Exemple 3.7 Programme de dessin des dix triangles.

with Spider; use Spider; -- pour pouvoir dessiner

-- ...procedure Triangle_10_Correct is

-- Nombre de triangles à dessinerNombre_De_Triangles : constant := 10;X_Init : constant := 10; -- Abscisse du point initialY_Init : constant := 30; -- Ordonnée du point initialBase : constant := 40; -- Longueur de la base d'un triangle

begin -- Triangle_10_Correct

-- Presentation du programme...

-- Pour pouvoir dessiner dans la fenetre de dessinInit_Window ("Fenetre de dessin");

-- Dessiner les 10 trianglesfor I in 1..Nombre_De_Triangles loop

-- Deplacement au point suivantMove_To (X_Init + (I – 1) * Base, Y_Init);

Page 65: Cours Ada de A à Z

ITÉRATION: LA BOUCLE FOR 65

-- Dessin d'un triangleLine ( Base, 0 );Line (–Base / 2, –Base / 2);Line (–Base / 2, Base / 2);

end loop;

end Triangle_10_Correct;

L’erreur commise dans le programme Triangle_10_Premier_Essai estclassique. Il faut en effet toujours prendre garde à tenir compte de la variation dela variable de boucle. En général cette variable doit être utilisée au moins une foisdans les instructions répétées, sinon il y a de fortes chances qu’une erreur delogique se soit glissée dans le code source (note 3.1). Dans le programmeTriangle_10_Correct, la variable de boucle I apparaît dans le calcul du pointinitial du dessin d’un triangle, point initial qui change à chaque itération!

NOTE 3.1 Variable de boucle et itérations.

La variable de boucle doit généralement être utilisée dans le corps de la boucle. Dans le cas contraireune erreur de logique est très probable.

La forme générale de l’instruction for est:for variable_de_boucle in intervalle loopsuite_d_instructions;end loop;

avec• les trois nouveaux mots réservés for, in et loop;• variable_de_boucle déclarée automatiquement;• intervalle dont la forme la plus simple et la plus courante est

expression_1 .. expression_2;• variable_de_boucle, expression_1 et expression_2 de type

discret (discrete types, [ARM 3.2]).

D’autres formes d’intervalle seront décrites par la suite (§ 6.1.1).

3.5.3 Précisions sur l’utilisation de la boucle for

La boucle for doit être choisie chaque fois que le nombre d’itérations àeffectuer est calculable avant l’arrivée dans la boucle (note 3.2). Ce nombred’itérations sera égal à expression_2 – expression_1 + 1.

La variable de boucle (appelée aussi variable de contrôle) est déclaréeimplicitement, du type des bornes de l’intervalle et n’existe que dans le corps de la

Page 66: Cours Ada de A à Z

ITÉRATION: LA BOUCLE FOR 66

boucle for. Elle prendra successivement (au début de chaque itération) la valeursuivante dans l’intervalle.

Il n’est pas possible de changer la valeur de la variable de boucle, par uneaffectation par exemple. Le compilateur détecterait une telle tentative et afficheraitun message d’erreur.

Si l’intervalle est nul, c’est-à-dire que l’expression_1 a une valeur supérieureà expression_2, la boucle n’est simplement pas effectuée.

Si la valeur d’expression_1 ou d’expression_2 est modifiée par uneitération, le nombre d’itérations ne change pas!

Il est possible d’écrire:for variable_de_boucle in reverse intervalle loopsuite_d_instructions;end loop;

Dans ce cas variable_de_boucle décroît de expression_2 àexpression_1; reverse est un mot réservé de plus.

Page 67: Cours Ada de A à Z

ITÉRATION: LA BOUCLE WHILE 67

3.6 ITÉRATION: LA BOUCLE WHILE

3.6.1 Généralités

La première construction permettant de répéter l’exécution d’une suited’instructions est l’instruction for. L’emploi de cette construction est possible (etrecommandé) lorsque le nombre d’itérations à effectuer est calculable avant l’arrivéedans la boucle. L’instruction while permet de répéter l’exécution d’une suited’instructions un nombre de fois non calculable avant l’arrivée dans la boucle. Cetterépétition s’effectue en fait tant qu’une condition (expression booléenne) est vraie.L’exemple 3.8 illustre l’utilisation d’une instruction while.

Exemple 3.8 Traiter des nombres tant qu’un nombre particulier, ici 0, n’apparaît pas.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

-- ...procedure Exemple_3_8 is

Nombre_Final : constant := 0; -- Permet de terminer le-- traitement des nombres

Nombre_Lu : Integer := 1; -- Nombre donne par l'utilisateur

begin -- Exemple_3_8

... -- Presentation du programme

-- Traiter les nombres de l'utilisateur. Attention, etre sur-- que Nombre_Lu a une valeur bien definie!

while Nombre_Lu /= Nombre_Final loop

Put ( "Donnez le nombre suivant: " );Get ( Nombre_Lu );

-- Traitement du nombre si ce n'est pas le dernierif Nombre_Lu /= Nombre_Final then

...

end if;

end loop;...

La suite d’instructions comprise entre loop et end loop est répétée tant que lacondition Nombre_Lu /= Nombre_Final est vraie, i.e. tant que Nombre_Lu estdifférent de 0. Le style de codage de cet exemple peut cependant être améliorécomme dans l’exemple 3.10.

La forme générale de l’instruction while est:while expression_de_type_Boolean loopsuite_d_instructions;

Page 68: Cours Ada de A à Z

ITÉRATION: LA BOUCLE WHILE 68

end loop;

où• la suite_d_instructions contenue dans la boucle est exécutée tant que

l’expression_de_type_Boolean est vraie.

3.6.2 Précisions sur l’utilisation de la boucle while

Toutes les variables appartenant à l’expression booléenne doivent avoir unevaleur bien définie (note 2.3).

Il faut s’assurer que l’expression booléenne devient fausse après un nombre finid’itérations, faute de quoi le programme exécuterait l’instruction whileindéfiniment.

Si l’expression booléenne est initialement fausse, l’instruction while n’est paseffectuée.

Page 69: Cours Ada de A à Z

ITÉRATION: LA BOUCLE GÉNÉRALE LOOP 69

3.7 ITÉRATION: LA BOUCLE GÉNÉRALE LOOP

3.7.1 Motivation

Les deux boucles vues jusqu’à présent permettent d’implanter tous les casd’itérations. Mais le codage de certains d’entre eux sera plus facile et plus lisibleavec une boucle où, par exemple, la condition de répétition de la boucle peut êtreplacée au milieu (exemples 3.9 et 3.10) ou à la fin de la boucle, ou encore sil’algorithme prévoit des sorties multiples de la boucle.

Exemple 3.9 Implantation d’un petit menu avec contrôle de la réponse de l’utilisateur.

...loop

Put_Line ( "Votre choix parmi les quatre options:" );Put_Line ( "1. Calcul de la moyenne" );Put_Line ( "2. Calcul de la variance" );Put_Line ( "3. Calcul de l'ecart-type" );Put_Line ( "4. Quitter ce menu" );

Get ( Reponse ); -- On suppose que Reponse est une-- variable entiere

exit when Reponse = 4; -- Sortie de la boucle

-- Traiter ci-dessous les options 1, 2 et 3...

end loop;...

Exemple 3.10 Exemple 3.8 récrit avec une boucle loop.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

-- ...procedure Exemple_3_10 is

Nombre_Final : constant := 0; -- Permet de terminer le-- traitement des nombres

Nombre_Lu : Integer; -- Nombre donne par l'utilisateur

begin -- Exemple_3_10

... -- Presentation du programme

-- Traiter les nombres de l'utilisateur. Dans cet exemple-- Nombre_Lu peut avoir une valeur quelconque lors de l’entree-- dans la boucle!loop

Page 70: Cours Ada de A à Z

ITÉRATION: LA BOUCLE GÉNÉRALE LOOP 70

Put ( "Donnez le nombre suivant: " );Get ( Nombre_Lu );

exit when Nombre_Lu = Nombre_Final;

-- Traitement du nombre puisque ce n'est pas le dernier...

end loop;...

3.7.2 Généralités

La forme générale de la boucle loop est très simple:loopsuite_d_instructionsend loop;

Sans rajouter de condition de sortie, une telle boucle loop s’exécuterait indé-finiment! C’est pourquoi l’instruction exit, permettant la sortie de la boucle,existe et est fortement liée à cette boucle. Sa forme générale est:

exit when expression_booleenne;

Si l’expression_booleenne est vraie, l’instruction exit provoque la sortiede la boucle dans laquelle elle se trouve; sinon l’exécution de la boucle continue.Une instruction exit doit être placée parmi la suite d’instructions d’une boucleloop et il est autorisé d’en mettre plusieurs. Enfin il est possible, mais déconseillé,de placer une ou plusieurs instructions exit dans une boucle for ou while.

3.7.3 Précisions sur l’utilisation des boucles et de l’instruction exit

La boucle loop (comme for et while d’ailleurs) peut comporter une étiquette(label) utile dans des cas tels que celui de deux boucles imbriquées:

Externe: looploop

...exit when B; -- Sortie de la boucle interne

-- si B est vraie...exit Externe when C; -- Sortie d’Externe si C est vraie...

end loop;...

end loop Externe;

L’instruction exit; (sans condition) existe et provoque la sortie incondi-tionnelle de la boucle la plus interne qui la contient.

Page 71: Cours Ada de A à Z

ITÉRATION: LA BOUCLE GÉNÉRALE LOOP 71

Enfin, la note 3.2 résume les critères de choix d’une boucle.

NOTE 3.2 Choix d’une boucle parmi for, while et loop.

Le choix d’une boucle ne s’effectue pas arbitrairement. Si le nombre d’itérations est calcu-lable avantl’arrivée dans la boucle, alors l’instruction for s’impose. Si l‘itération dépend d’une condition quipeut être initialement fausse, alors il faut utiliser une instruction while. Si aucune des deuxhypothèses ci-dessus ne s’applique, alors prendre l’instruction loop en choisissant soigneusement lenombre et le lieu d’utilisation des instructions exit!

Page 72: Cours Ada de A à Z

LE TYPE CHARACTER 72

3.8 LE TYPE CHARACTER

3.8.1 Généralités

Character est un identificateur prédéfini. Il signifie «un objet (constante,variable, expression...) de type Character contiendra un caractère et riend’autre». Le type Character fait partie des types discrets (discrete types, [ARM3.2]).

Les constantes de type Character sont des caractères écrits entre apostrophescomme dans l’exemple 3.11.

Exemple 3.11 Constantes caractères.

'A' 'a' '1' '?' ''' '+' '=' 'é' 'à'

Ces constantes sont ordonnées selon un code, unique pour chaque caractère. Ilexiste plusieurs codes utilisés en informatique (ASCII, EBCDIC, LATIN-1...),mais en Ada le code utilisé est LATIN-1 [ARM A.1], composé de 256 valeursnumérotées à partie de 0, dont les 128 premières sont celles du code ASCII(American Standard Code for Information Interchange). Chaque caractère écritentre apostrophes du code LATIN-1 est imprimable (§ 1.10.2), les caractères decontrôle peuvent être utilisés par le biais de l’attribut Val (§ 3.8.3). De plus, unpaquetage appelé Ada.Characters.Latin_1 définit un identificateur de cons-tante pour chaque caractère du code LATIN-1 [ARM A.3.3].

Les opérations possibles sur les valeurs de type Character sont lescomparaisons (§ 3.1.2) = /= < <= > >= et la concaténation (§ 8.5.2).

Les expressions se limitent aux constantes et variables caractères ainsi qu’auxfonctions et attributs à résultat de type Character.

3.8.2 Affectation

L’affectation s’effectue comme dans l’exemple 3.12.

Exemple 3.12 Affectations d’expressions de type Character à une variable caractère.

with Ada.Characters.Latin_1;

-- ...procedure Exemple_3_12 is

Espace : constant Character := ' '; -- Le caractere appele-- espace (space)

Signe : Character; -- Deux variables caracteresLettre_Courante : Character;

Page 73: Cours Ada de A à Z

LE TYPE CHARACTER 73

begin -- Exemple_3_12

Signe := Espace; -- Affecte le caractere ' ' a SigneLettre_Courante := 'A';Lettre_Courante := Signe;Signe := Ada.Characters.Latin_1.Nul; -- Nul est le caractere... -- de code 0

De manière générale l’affectation s’écrit:nom_de_variable_de_type_Character := expression_de_type_Character;

3.8.3 Attributs First, Last, Succ, Pred, Pos et Val

Les attributs First, Last, Succ et Pred ont la même signification que pourles entiers (§ 2.2.5).

L’attribut prédéfini Pos donne la valeur (entière) du code du caractère auquels’applique l’attribut. De manière symétrique, l’attribut prédéfini Val donne lecaractère correspondant à une valeur entière comprise entre 0 et 255.

Comme pour les entiers et les réels, une tentative d’utilisation d’un code decaractère en dehors de l’intervalle de définition produira une erreur à l’exécutiondu programme.

Exemple 3.13 Utilisation des attributs First, Last, Succ, Pred, Pos et Val.

Character'First donne le caractère de code 0 (nul);Character'Lastdonne 'ÿ', le caractère de code 255;Character'Pred('B')donne 'A';Character'Succ('A')donne 'B';Character'Pos('A')donne 65, le code de 'A';Character'Val(65)donne 'A';Character'Val(97)donne 'a';Character'Val(32)donne ' ';Character'Val (Nombre)provoque une erreur à l’exécution si Nombre a unevaleur hors de l’intervalle 0 .. 255.

3.8.4 Entrées-sorties

Comme pour tous les types simples, Ada offre la possibilité d’effectuer desentrées-sorties sur des valeurs du type prédéfini Character. Celles-ci sontdirectement mises à disposition par le paquetage prédéfini Ada.Text_IO.L’exemple 3.14 montre simplement une lecture et deux écritures de valeurs du type

Page 74: Cours Ada de A à Z

LE TYPE CHARACTER 74

Character.

Exemple 3.14 Entrées-sorties de caractères.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_3_14 is

Lettre : Character; -- Une variable caractere

begin -- Exemple_3_14

Put_Line (" Cet exemple..." );

Get (Lettre); -- Lit un caracterePut (Lettre); -- Ecrit le caractere luPut ('A'); -- Ecrit le caractere A...

En lecture comme en écriture, les caractères tapés ou lus par l’utilisateur duprogramme se présentent sans apostrophes! Lesdites apostrophes ne sont néces-saires que dans le texte source du programme pour les distinguer d’un identi-ficateur, d’un opérateur, etc.

Le caractère lu par Get est le caractère suivant le dernier caractère lu par uneprécédente opération Get, en sautant les fins de lignes éventuelles.

Page 75: Cours Ada de A à Z

RETOUR SUR LA PARTIE DÉCLARATIVE D’UN PROGRAMME 75

3.9 RETOUR SUR LA PARTIE DÉCLARATIVE D’UN PROGRAMME

3.9.1 Déclaration de constantes

Une déclaration de constantes a la forme principale suivante:suite_d_identificateurs : constant type := expression;

avec• la suite_d_identificateurs formée d’un ou de plusieurs identificateurs séparés

par des virgules;• le mot réservé constant qui permet la distinction entre les déclarations de

constantes et de variables (§ 3.9.2).

L’expression doit bien entendu être du même type que le type mentionné et peutêtre aussi compliquée que le désire l’auteur du programme. Comme son noml’indique, une constante possède une valeur fixe définie par l’expression. Il existeune forme particulière de déclaration de constantes:

suite_d_identificateurs : constant := expression_statique;

L’expression doit dans ce cas être statique (sect. 3.10) et d’un type entier ouréel. Une telle constante est appelée nombre nommé (named number). L’exemple3.15 présente des déclarations de constantes et de nombres nommés.

Exemple 3.15 Constantes et nombres nommés.

-- ConstantesNombre_Maximum : constant Integer := 1000;Neper : constant Float := 2.718282;Sentinelle : constant Character := '.';

-- On suppose ci-dessous que Valeur est une variable entiere-- deja declaree et de valeur 9, l’expression vaut donc 2Complique, Pas_Simple : constant Integer := (3 * Valeur – 1) / 10;

-- Nombres nommesPi : constant := 3.141592654;Zero, Nul, Rien: constant := 0;Pour_Cercle : constant := 2.0 * Pi; -- 2.0 * Pi est statique

Lorsque plusieurs constantes sont mentionnées dans la même déclarationcomme Complique et Pas_Simple ou encore Zero, Nul et Rien de l’exemple3.15, cette forme est en fait une abréviation. Elle remplace une suite équivalente dedéclarations où seul un identificateur serait présent dans chacune d’entre elles,donc dans notre cas:

Complique : constant Integer := (3 * Valeur – 1) / 10;

Page 76: Cours Ada de A à Z

RETOUR SUR LA PARTIE DÉCLARATIVE D’UN PROGRAMME 76

Pas_Simple : constant Integer := (3 * Valeur – 1) / 10;

Zero : constant := 0;Nul : constant := 0;Rien : constant := 0;

Cette remarque a son importance lorsque l’expression comporte une fonction(sect. 4.6). En effet, l’expression est calculée pour chaque identificateur, donc lafonction est appelée autant de fois qu’il y a d’identificateurs mentionnés dans lasuite.

3.9.2 Déclaration de variables

Une déclaration de variables a la forme principale suivante:suite_d_identificateurs : type := expression;

avec• la suite_d_identificateurs formée d’un ou de plusieurs identificateurs séparés

par des virgules.

L’expression, appelée valeur initiale (initial value), est optionnelle. Si elle estprésente, elle doit bien entendu être du même type que le type mentionné et sert àdonner une première valeur à la variable. Comme pour les constantes, une suitecomportant plusieurs identificateurs est une abréviation et les mêmes règless’appliquent.

3.9.3 Ordre et élaboration des déclarations

Le langage Ada ne fixe pas d’ordre particulier pour les déclarations. Il fautsimplement que tout identificateur utilisé soit déjà déclaré. Mais les bonneshabitudes de programmation impliquent le regroupement des déclarations quiforment un tout logique. De plus l’usage voudrait que les constantes soientdéclarées avant les variables dans un tel regroupement (exemple 3.16).

Exemple 3.16 Groupements des déclarations dans une partie déclarative.

-- Pour le traitement de donnees numeriquesNombre_Maximum : constant Integer := 1000;Nombre_Courant : Integer;Maximum_Courant : Integer;

-- Pour le traitement de donnees textuellesSentinelle : constant Character := '.'Lettre_Lue : Character;Nombre_lettres_Lues : Integer := 0;

A l’exécution du programme, toute déclaration est élaborée, c’est-à-dire qu’unemplacement mémoire est préparé, voire alloué, à son intention et que celui-ci

Page 77: Cours Ada de A à Z

RETOUR SUR LA PARTIE DÉCLARATIVE D’UN PROGRAMME 77

contient les informations caractérisant l’objet déclaré. Par exemple, l’élaborationde la variable Nombre_Lettres_Lues de l’exemple 3.16 consiste en l’attributiond’un mot mémoire et l’introduction de la valeur initiale zéro dans ce mot mémoire.L’ordre d’écriture des déclarations fixe l’ordre dans lequel ces déclarations vontêtre élaborées à l’exécution.

Page 78: Cours Ada de A à Z

RETOUR SUR LES EXPRESSIONS 78

3.10 RETOUR SUR LES EXPRESSIONS

Une expression statique (static) est une expression calculée à la compilation duprogramme. Par extension on appelle statique toute entité dont les caractéristiquessont connues à la compilation (avant l’exécution du programme).

Une expression dynamique (dynamic) est une expression dont la valeur n’estconnue qu’à l’exécution du programme ou qui peut changer lors de cette exécution.Par extension on appelle dynamique toute entité dont les caractéristiques (oul’existence) ne sont connues qu’à l’exécution du programme.

Une expression qualifiée (qualified) consiste à préciser le type d’une expres-sion par l’utilisation d’un identificateur de type (ou de sous-type, sect. 6.1) sous laforme donnée par le diagramme syntaxique de la figure 3.2. Cette forme d’expres-sion est particulièrement utile pour préciser le type d’agrégats (§ 7.2.2 et 8.2.5) etlors de l’élaboration d’une variable dynamique avec valeur initiale (§ 15.2.3). Ilfaut aussi remarquer la présence discrète mais fondamentale de l’apostrophe quidistingue une expression qualifiée d’une conversion de type (§ 2.5.1). L’exemple3.17 montre des cas simples d’expressions qualifiées.

Figure 3.2 Diagramme syntaxique définissant une expression qualifiée.

Exemple 3.17 Expressions qualifiées.

Integer'(10) le nombre 10 est ici du type Integer;Long_Integer'(10) le nombre 10 est ici du type Long_Integer;Float'(10.0) le nombre 10.0 est ici du type Float;Short_Float'(10.0) le nombre 10.0 est ici du type Short_Float;Character'('.') le caractere '.' est ici du type Character.

identificateur ' expression

Expression qualifiée

( )

Page 79: Cours Ada de A à Z

79

3.11 EXERCICES

3.11.1 Recherche des diviseurs d’un nombre

Ecrire un programme qui trouve tous les diviseurs d’un nombre entier donnépar l’utilisateur du programme.

3.11.2 Décomposition d’un nombre en facteurs premiers

Ecrire un programme qui décompose un nombre entier donné par l’utilisateurdu programme en son produit de facteurs premiers.

3.11.3 Solutions de l’équation du second degré

Ecrire un programme qui trouve les racines d’une équation du second degrédont les coefficients sont donnés par l’utilisateur du programme. La racine carréeest disponible dans le paquetage Ada.Numerics.Elementary_Functions sousle nom Sqrt.

3.11.4 Utilisation des caractères

Ecrire un programme qui compte le nombre d’imbrications des parenthèsesdans une ligne de texte.

3.11.5 Utilisation des booléens

Ecrire un programme qui affiche la table de vérité d’expressions booléennescomme par exemple A and (B or C) où A, B et C sont des variables booléennes.

3.11.6 Petites statistiques sur une ligne de texte

Ecrire un programme qui affiche le nombre de lettres majuscules, de lettresminuscules et de chiffres d’une ligne de texte (ces trois groupes de caractèresforment chacun un intervalle sans caractère «parasite» dans le code LATIN-1).

3.11.7 Triangle de Pascal

Ecrire le triangle de Pascal formé des coefficients du binôme de Newton. Le

coefficient i du binôme vaut , i et n commençant à 0.ab n n!

i! n i–( )!×---------------------------

Page 80: Cours Ada de A à Z

POINTS À RELEVER 80

3.12 POINTS À RELEVER

3.12.1 En général

• Les caractères permettent des manipulations rudimentaires de textes.

• Les caractères sont ordonnés selon un code dépendant du système utilisé.

• Les déclarations devraient toujours être placées de manière à former desgroupes logiques.

• Un objet statique est un objet dont les caractéristiques sont déjà connues àla compilation.

• Un objet dynamique est un objet dont les caractéristiques (ou l’existence)ne sont connues qu’à l’exécution du programme.

3.12.2 En Ada

• L’instruction if (sélection) permet l’exécution d’une branche d’instruc-tions parmi plusieurs, éventuellement aucune, en fonction de la valeurd’expressions booléennes.

• Les expressions booléennes s’utilisent en particulier dans l’instruction ifet dans la boucle while.

• Les entrées-sorties de valeurs booléennes nécessitent une déclarationspéciale.

• La boucle for s’utilise lorsque le nombre d’itérations est calculable avantl’arrivée dans la boucle. Une fois ce nombre fixé, il ne varie plus.

• La variable de contrôle de la boucle for est déclarée implicitement etn’existe que pour les instructions répétées par for.

• La variable de contrôle change de valeur à chaque itération. Cette variationest toujours de plus (ou moins) 1.

• La boucle while s’utilise lorsque le nombre d’itérations n’est pas calcu-lable avant l’entrée dans la boucle et si l’itération dépend d’une conditionqui peut être fausse initialement.

• La boucle généralisée loop s’utilise lorsque le nombre d’itérations n’estpas calculable avant l’entrée dans la boucle et qu’il est nécessaire ou utilede quitter la boucle ailleurs qu’avant la première instruction qu’ellecontient.

• Attention à ne pas créer de boucle infinie avec while et surtout loop!

• L’instruction exit permet de quitter n’importe quelle boucle et devraits’utiliser essentiellement avec loop.

Page 81: Cours Ada de A à Z

POINTS À RELEVER 81

• Il existe deux sortes de constantes, les nombres nommés et les constantesproprement dites.

• L’ordre des déclarations est libre dans une partie déclarative à conditionque tout identificateur utilisé soit déjà déclaré.

Page 82: Cours Ada de A à Z

82

C H A P I T R E 4

P R O C É D UR E S E T

Page 83: Cours Ada de A à Z

83 PROCÉDURES ET FONCTIONS

Page 84: Cours Ada de A à Z

MOTIVATION 84

4.1 MOTIVATION

Comment effectuer le dessin donné dans la figure 4.1 (sans les coordonnées despoints, mises pour fixer les idées)?

Figure 4.1 Exemple introductif.

La décomposition (sect. 1.7) de ce problème simple conduit à quatre dessins decarrés identiques, disposés à quatre endroits différents. Un langage tel qu’Adafournit un moyen de ne pas répéter quatre fois les mêmes instructions de dessind’un carré. Ce moyen est une construction appelée procédure (procedure):

-- Cette procedure dessine un carreprocedure Dessiner_Carre is

begin -- Dessiner_Carre

-- Dessin d'un carre de 50 de cote (§ 1.8.3)Line ( 50, 0 );Line ( 0, 50 );Line ( –50, 0 );Line ( 0, –50 );

end Dessiner_Carre;

Une procédure n’est pas exécutée hors de tout contexte. C’est le programmeprincipal (ou une autre procédure) qui déclare la procédure et qui l’appelle. L’appel(call) de la procédure Dessiner_Carre provoque son exécution, donc l’exé-cution des quatre instructions Line. Cet appel est une instruction et s’écrit enutilisant le nom de la procédure comme par exemple:

Dessiner_Carre;

(10,10) (110,10)

(110,110)(10,110)

Page 85: Cours Ada de A à Z

MOTIVATION 85

Suite à ce premier exemple, les lecteurs attentifs et malins auront remarqué quel’instruction Line est en fait un appel de la procédure Line suivi de ce que l’onappelle des paramètres (sect. 4.3)

Le programme complet réalisant le dessin proposé est donné dans l’exemple4.1.

Exemple 4.1 Réalisation du dessin au moyen d’une procédure.

with Ada.Text_IO; use Ada.Text_IO;with Spider; use Spider;

-- Ce programme trace le dessin de quatre carresprocedure Dessin_Quatre_Carres is

-- Les procedures se declarent dans la partie declarative

-------------------------------------------------------------- Cette procedure dessine un carreprocedure Dessiner_Carre is

Cote : constant := 50; -- Longueur du cote

begin -- Dessiner_Carre

-- Dessin d'un carre de 50 de cote (§ 1.8.3)Line ( Cote, 0 );Line ( 0, Cote );Line ( – Cote, 0 );Line ( 0, – Cote );

end Dessiner_Carre;------------------------------------------------------------

begin -- Dessin_Quatre_Carres

-- Presentation du programme...-- Pour pouvoir dessiner dans la fenetre de dessinInit_Window ("Fenêtre de dessin");

Move_To ( 10,10 ); -- Deplacement au point (10,10)Dessiner_Carre; -- Dessin du carre depuis ce

point...

Move_To ( 110,10 ); -- ... et de meme pour les trois autres

Dessiner_Carre;

Move_To ( 110,110 );Dessiner_Carre;

Move_To ( 10,110 );Dessiner_Carre;

end Dessin_Quatre_Carres;

Page 86: Cours Ada de A à Z

MOTIVATION 86

On remarquera la lisibilité de ce programme, simple il est vrai, grâce à l’intro-duction de Dessiner_Carre.

Les procédures sont des constructions qui:

• améliorent la lisibilité, donc aident à la compréhension d’un programme;• reflètent la décomposition d’un problème en sous-problèmes plus faciles à

analyser et à résoudre;• diminuent la taille des programmes, donc le temps de saisie (!);• réduisent les risques d’erreurs.

Une procédure peut être vue comme une boîte noire (black box). Le program-meur veut utiliser la boîte noire sans se préoccuper de sa structure interne. Cecifacilite l’écriture des programmes, en particulier ceux de grande taille.

Page 87: Cours Ada de A à Z

STRUCTURE DES PROCÉDURES 87

4.2 STRUCTURE DES PROCÉDURES

Une procédure a la même structure qu’un programme principal (sect. 1.9), saufla clause de contexte qui est absente. Si la partie déclarative et le corps sontstructurellement identiques à ceux d’un programme principal, l’en-tête se compose(fig. 4.2) du mot réservé procedure suivi du nom de la procédure et,optionnellement, de paramètres (sect. 4.3) entre parenthèses (exemple 4.2). L’en-tête se termine avant le mot réservé is.

Figure 4.2 Diagramme syntaxique de l’en-tête d’une procédure.

Exemple 4.2 En-têtes de procédure.

-- En-tetes sans parametreprocedure Dessiner_Carreprocedure Presenter_Programme

-- En-tetes avec parametresprocedure Line (X, Y : in Integer)procedure Move_To (X, Y : in Integer := 0)procedure Lire_Nombre (Nombre : out Integer)procedure Calculer_Log (Nombre : in out Float)

En-tête de procédure

procedure identificateur liste de paramètres

Liste de paramètres

identificateurde paramètre

identificateurde type oude sous-type

in

out

in out

( :

, := expression

)

;

access

Page 88: Cours Ada de A à Z

STRUCTURE DES PROCÉDURES 88

Un commentaire (but, description des paramètres, causes d’exceptions... [AUS95]) doit accompagner chaque en-tête de procédure ou de fonction!

Une procédure peut être déclarée dans une partie déclarative comme celle d’unprogramme principal, mais aussi dans celle d’une autre procédure (exemple 4.3) oud’un paquetage (sect. 10.3 et 10.4). Ceci implique que la structure du programmeet de ses procédures pourra refléter fidèlement les étapes de la décomposition d’unproblème par raffinements successifs (sect. 1.7)!

Exemple 4.3 Déclaration de procédures imbriquées.

procedure Programme_Principal is-- Declarations du programme principal, dont la procedure Externe:

...------------------------------------------------------------procedure Externe (...) is-- Declarations de la procedure Externe, dont la procedure-- Interne:

...----------------------------------------------------------procedure Interne (...) is-- Declarations de la procedure Internebegin -- Interne

... -- Instructions de la procedure Interneend Interne;----------------------------------------------------------

begin -- Externe... -- Instructions de la procedure Externe

end Externe;------------------------------------------------------------

begin -- Programme_Principal... -- Instructions du programme principal

end Programme_Principal;

L’exécution des instructions d’une procédure, par contraction l’exécution d’uneprocédure, est commandée par l’exécution de l’instruction d’appel de la procédure.La figure 4.3, utilisant le programme Dessin_Quatre_Carres (sect. 4.1), illustrele mécanisme utilisé:

Page 89: Cours Ada de A à Z

STRUCTURE DES PROCÉDURES 89

Figure 4.3 Exécution d’une procédure.

procedure Dessiner_Carre is

Cote : constant := 50;

begin -- Dessiner_CarreLine ( Cote, 0 );Line ( 0, Cote );Line ( – Cote, 0 );Line ( 0, – Cote );

end Dessiner_Carre;

procedure Dessin_Quatre_Carres is

begin -- Dessin_Quatre_Carres...

Dessiner_Carre;Move_To ( 110,10 );...

end Dessin Quatre Carres;

appel

retour

Move_To ( 10,10 );

Page 90: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 90

4.3 PARAMÈTRES DES PROCÉDURES

4.3.1 Paramètres d’entrée

La procédure Dessiner_Carre est bien utile pour dessiner un carré. Elle leserait plus encore si elle permettait le dessin d’un carré de n’importe quelle taille.Pour cela il faudrait que la valeur de la constante Cote soit donnée au moment del’appel de la procédure.

Ce mécanisme existe! Il faut transformer la constante Cote en un paramètre(parameter), en sachant que la déclaration des paramètres a lieu dans l’en-tête desprocédures (fig. 4.2). La procédure Dessiner_Carre devient:

-- Cette procedure dessine un carre dont la longueur du cote est-- passee en parametreprocedure Dessiner_Carre ( Cote : in Integer ) isbegin -- Dessiner_Carre

-- Dessin d'un carre dont le cote vaut CoteLine ( Cote, 0 );Line ( 0, Cote );Line ( -Cote, 0 );Line ( 0, -Cote );

end Dessiner_Carre;

L’instruction d’appel de la procédure est modifiée en spécifiant entre paren-thèses la valeur (expression entière) qui doit être donnée au paramètre Cote(exemple 4.4). Il paraît naturel que le type du paramètre et le type de la valeurspécifiée doivent être identiques.

Exemple 4.4 Appels de la procédure Dessiner_Carre avec paramètre.

Dessiner_Carre (50);le carré aura 50 unités de côté;Dessiner_Carre (Nb_Entier);le carré aura Nb_Entier unités de côté;Dessiner_Carre((10 + Nombre) / 3);le carré aura un côté égal à la valeurde (10 + Nombre) / 3.

Page 91: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 91

Figure 4.4 Passage de paramètre en entrée.

4.3.2 Précisions concernant les paramètres de procédure

Les paramètres déclarés et utilisés dans les procédures sont appelés paramètresformels (formal parameters); les valeurs (constantes, variables, expressions) spé-cifiées à l’appel sont appelées paramètres effectifs (effective parameters).

Dans la procédure Dessiner_Carre, la valeur représentant la longueur d’uncôté est transmise de l’extérieur vers l’intérieur de la procédure (fig. 4.4) à l’appelde la procédure. On parle dans ce cas de paramètre d’entrée pour le paramètreCote. Le mot réservé in est utilisé pour un paramètre d’entrée. Un tel paramètreest une constante utilisable à l’intérieur de la procédure uniquement.

N’importe quel type peut être utilisé dans une déclaration de paramètre, maisseul un identificateur doit être mentionné.

Le lecteur malin aura remarqué que, par exemple, l’instruction (§ 2.6.9)Put ( "Donnez les dimensions d'une brique " );

est l’appel d’une procédure Put avec le paramètre d’entrée "Donnez lesdimensions d'une brique ".

4.3.3 Paramètres de sortie

La procédure Dessiner_Carre va être encore modifiée afin de calculer lasurface du carré en plus du dessin, et cette valeur sera fournie à l’extérieur de laprocédure. Le mécanisme des paramètres va également permettre la transmissionde la valeur de la surface. La procédure devient:

-- Cette procedure dessine un carre dont la longueur du cote est-- passee en parametre. Elle calcule la surface qui est ensuite-- transmise par parametre a l'exterieur, a l'appelantprocedure Dessiner_Carre ( Cote : in Integer;

procedure Dessiner_Carre ( Cote : in Integer) is

begin -- Dessiner_Carre

Line ( Cote, 0 );Line ( 0, Cote );Line ( - Cote, 0 );Line ( 0, - Cote );

end Dessiner_Carre;

Dessiner_Carre ( 50 );

Page 92: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 92

Surface : out Integer) is

begin -- Dessiner_Carre

-- Dessin d'un carre dont le cote vaut CoteLine ( Cote, 0 );Line ( 0, Cote );Line ( -Cote, 0 );Line ( 0, -Cote );

-- Calcul de la surface du carreSurface := Cote ** 2;

end Dessiner_Carre;

La déclaration du paramètre Surface comprend le mot réservé out. Celasignifie que Surface est une variable utilisable à l’intérieur de la procédure etqu’elle permet de transmettre une valeur de l’intérieur vers l’extérieur. Cette valeurest transmise à la fin de l’exécution de la procédure (fig. 4.5). Un tel paramètre estappelé paramètre de sortie.

Les paramètres effectifs doivent être ici (exemple 4.5) des variables (et non desexpressions) puisqu’ils vont recevoir une valeur. Ici également les paramètresformels et effectifs doivent être de (n’importe quel) même type.

Exemple 4.5 Appels corrects ou incorrects de la procédure Dessiner_Carre.

Dessiner_Carre (50, Aire);est un appel correct de la procédureDessiner_Carre ci-dessus (si Aire est de type Integer);Dessiner_Carre ( 50, 30 );est un appel incorrect de la procédureDessiner_Carre ci-dessus (comment le nombre 30 pourrait-il «recevoir»une valeur?).

Le fait que Dessiner_Carre calcule une surface est un peu artificiel. En effetson nom laisse croire à une simple procédure de dessin. Le calcul a été introduitdans le seul but d’illustrer un paramètre de sortie tout en conservant par ailleurs lereste de la procédure tel quel.

Page 93: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 93

Figure 4.5 Passage de paramètre en sortie.

Le lecteur malin aura remarqué que, par exemple, l’instruction (§ 2.6.9):Get ( Longueur_Mur );

est l’appel d’une procédure Get avec un paramètre de sortie qui recevra une valeurentière donnée par l’utilisateur du programme.

4.3.4 Paramètres d’entrée et de sortie

Un paramètre d’entrée et de sortie permet de transmettre des valeurs del’extérieur vers l’intérieur d’une procédure et inversement. Soit la procédureMajusculiser qui, comme son nom l’indique, transforme une lettre minuscule enmajuscule:

-- Cette procedure calcule la majuscule du caractere passe en-- parametre a condition qu'il soit une lettre, c'est-a-dire qu'il-- appartienne a l'intervalle [a..z]. Si ce n'est pas le cas, la-- procedure ne fait rien.procedure Majusculiser ( Caractere : in out Character ) is

-- La difference entre le code d'une lettre minuscule et celui-- d'une majuscule est donnee par la difference des codes des

-- lettres 'a' et 'A'Decalage : constant Integer :=

Character'Pos('a') - Character'Pos('A');

begin -- Majusculiser

if Caractere >= 'a' and Caractere <= 'z' thenCaractere := Character'Val (

Character'Pos(Caractere) - Decalage);

end if;

procedure Dessiner_Carre ( Cote : in Integer;

begin -- Dessiner_Carre

Line ( Cote, 0 );Line ( 0, Cote );Line ( - Cote, 0 );Line ( 0, - Cote );

end Dessiner_Carre;

Dessiner_Carre ( 50, Aire);

Surface : out Integer ) is

Surface := Cote ** 2;

Page 94: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 94

end Majusculiser;

Au début de l’appel de la procédure Majusculiser, la valeur du paramètreeffectif sera passée au paramètre formel Caractere. Puis la valeur de Caracteresera modifiée si c’est une lettre minuscule. A la sortie de la procédure (retour àl’appelant), la valeur de Caractere sera transmise au paramètre effectif (fig. 4.6).

Comme dans le cas d’un paramètre de sortie, le paramètre effectif doit être iciune variable puisqu’il va recevoir une valeur. Les paramètres formels et effectifsdoivent également être de (n’importe quel) même type.

Figure 4.6 Passage de paramètre en entrée et en sortie.

4.3.5 Remarques

Comme lors de l’affectation, une erreur se produira à l’exécution si le passaged’une valeur entre paramètres formel et effectif n’est pas possible, par exemple dufait de la violation d’une contrainte (§ 6.1.1).

Si le mode de passage d’un paramètre n’est pas explicitement mentionné, alorsce paramètre sera implicitement considéré comme un paramètre d’entrée (in).

Lorsqu’une procédure possède plusieurs paramètres ayant des modes depassage différents, et en l’absence d’autres conventions, il est conseillé decommencer par les paramètres d’entrée puis par les paramètres d’entrée et de sortieet de terminer par les paramètres de sortie.

Il est également souhaitable de n’écrire qu’un seul paramètre par ligne de codesource; cela augmentera la lisibilité de l’en-tête.

La procédure Majusculiser pourrait (devrait) en fait être une fonction (sect.4.6). De telles fonctions de manipulation de caractères existent dans le paquetage

procedure Majusculiser ( Caractere : in out Character ) is

begin -- Majusculiser

end Majusculiser;

Majusculiser ( Lettre);

if Caractere in 'a' .. 'z' then -- § 6.1.5Caractere := Character’Val(Character’Pos(Caractere) - Decalage);

end if;

Decalage : constant Integer :=Character'Pos('a') - Character'Pos('A');

Page 95: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 95

prédéfini Ada.Characters.Handling [ARM A.3.2].

Dans la procédure Majusculiser, la condition du test devrait en fait s’écrireif Caractere in 'a'..'z' then (§ 6.1.5).

Les paramètres notés comme access ne seront pas traités dans cet ouvrage.

4.3.6 Valeurs par défaut des paramètres d’entrée

Soit l’en-tête modifié de la procédure Dessiner_Carre (§ 4.3.1):procedure Dessiner_Carre ( Cote : in Integer := 50)

la présence d’une expression (constante 50) pour le paramètre Cote implique qu’ilest possible d’appeler la procédure Dessiner_Carre sans mentionner deparamètre effectif (exemple 4.6)! Lors de l’appel, la valeur du paramètre Cote seracelle donnée par l’évaluation de l’expression (ici 50). L’expression joue donc lerôle de valeur par défaut (default value) du paramètre Cote.

Exemple 4.6 Appels de la procédure Dessiner_Carre avec ou sans paramètre effectif.

Dessiner_Carre (40);le carré aura 40 unités de côté; la valeur par défautn’est ni calculée ni utilisée;Dessiner_Carre;le carré aura 50 unités de côté, valeur donnée par le calculde l’expression par défaut (constante 50).

Seuls les paramètres d’entrée peuvent avoir une valeur par défaut. Il faut doncobligatoirement mentionner un paramètre effectif pour tout paramètre formeld’entrée et de sortie ou de sortie uniquement.

4.3.7 Appel de procédure avec notation par position ou par nom

Les appels de procédure vus jusqu’ici utilisaient tous la notation par positionpour les paramètres. La notation par position signifie que l’association paramètreformel-paramètre effectif est effectuée selon la position de chacun d’entre eux, enséparant les paramètres effectifs par une virgule. Il est possible et parfoisnécessaire de préciser explicitement comment effectuer cette association. Dans cecas il faut, toujours à l’appel, mentionner explicitement à quel paramètre formelcorrespond un paramètre effectif (exemple 4.7), ceci en utilisant le symbole =>(flèche). Cette façon de faire s’appelle notation par nom.

Exemple 4.7 Appels par position ou par nom.

Dessiner_Carre ( 50, Aire );notation par position;Dessiner_Carre (Cote => 50,notation par nom.Surface => Aire );

Page 96: Cours Ada de A à Z

PARAMÈTRES DES PROCÉDURES 96

En utilisant la notation par nom, il est possible de ne plus respecter l’ordre desparamètres formels. Finalement, le mélange des deux notations est possible enrespectant le fait que, dès qu’un paramètre est noté par nom, alors tous les suivantsdoivent l’être aussi (exemple 4.8).

Exemple 4.8 Appels par nom ou mélangés.

Dessiner_Carre (Surface => Aire,ordre différent;Cote => 50 );

Dessiner_Carre (50, Surface => Aire );mélange.

4.3.8 Précisions sur le passage des paramètres

La copie d’une valeur entre paramètre effectif et paramètre formel (ou inver-sement) constitue ce qui est appelé le passage par valeur. Ceci signifie que lavaleur du paramètre effectif ne change pas lors de l’exécution de la procédure saufsi, à la fin, la valeur du paramètre formel est recopiée dans le paramètre effectif.

Le passage par référence entre paramètre effectif et paramètre formel n’estqu’une vue de l’esprit. Il faut en effet considérer que, dans ce cas, le paramètreformel n’est qu’un nouveau nom pour le paramètre effectif. Ceci signifie que, si parla suite la valeur du paramètre formel est modifiée, la valeur du paramètre effectifl’est de la même manière!

La norme Ada laisse une certaine liberté à l’implémentation pour effectuer lepassage des paramètres par valeur ou par référence. Mais elle impose cependantque les valeurs d’un type scalaire ou accès (scalar ou access types, [ARM 3.2])soient toujours passées par valeur, alors que d’autres (non décrites ici) le sontobligatoirement par référence.

Page 97: Cours Ada de A à Z

NOTIONS DE PORTÉE ET DE VISIBILITÉ DES IDENTIFICATEURS 97

4.4 NOTIONS DE PORTÉE ET DE VISIBILITÉ DES IDENTIFICATEURS

A la notion de procédure est étroitement associée celle de région. Une régionest un nom donné à une structure comme une procédure, une fonction (sect. 4.6) ouun bloc (§ 6.3.5). Comme les procédures peuvent être emboîtées les unes dans lesautres (sect. 4.2), des régions le seront également. Cette hiérarchie (appelée parfoisstructure de blocs) définit des niveaux (level) de régions:

• le programme principal forme la région de niveau 1;• les procédures déclarées directement dans le programme principal forment

chacune une région de niveau 2;• les procédures déclarées dans des procédures de niveau 2 forment chacune

une région de niveau 3;• de manière générale, les procédures déclarées dans des procédures de

niveau N forment chacune une région de niveau N+1.

Le numéro de niveau est appelé la profondeur (depth) du niveau. Une régionde niveau 5 est plus profonde qu’une région de niveau 2.

Exemple 4.9 Imbrication de régions.

-- Debut region Aprocedure Imbrication is -- 1

Max : constant := 10; -- Trois identificateurs declares-- 2

Lettre : Character; -- dans la region A-- 3Nombre : Integer; -- 4

-------------------------------------------------------------- Debut region Bprocedure Niveau_2 (Caractere : in Character ) is-- 5

Nombre : Integer; -- Deux declares dans la region B-- 6

begin -- Niveau_2...

end Niveau_2;-- Fin region B

-------------------------------------------------------------- Debut region Cprocedure Aussi_Niveau_2 is -- 7

Nombre : Float; -- Un seul dans la region C-- 8

------------------------------------------------------------ Debut region Dprocedure Niveau_3 (Caractere : in Character ) is-- 9

Nombre : Integer; -- Trois identificateurs declares-- 10

Page 98: Cours Ada de A à Z

NOTIONS DE PORTÉE ET DE VISIBILITÉ DES IDENTIFICATEURS 98

Ok : Boolean; -- dans la region D-- 11

begin -- Niveau_3...

end Niveau_3;-- Fin region D----------------------------------------------------------

begin -- Aussi_Niveau_2...

end Aussi_Niveau_2;-- Fin region C------------------------------------------------------------

begin -- Imbrication...

end Imbrication;-- Fin region A

L’exemple 4.9 montre une hiérarchie à trois niveaux: le programme principalappelé Imbrication forme le niveau 1, les procédures Niveau_2 etAussi_Niveau_2 constituent chacune une région et forment le niveau 2,finalement la procédure Niveau_3 forme une région de niveau 3.

De plus on définit la notion de portée d’un identificateur qui est la zone de larégion dans laquelle est déclaré un identificateur, zone commençant à sadéclaration et se terminant à la fin de la région. Enfin un identificateur est(directement ou indirectement) visible dans sa portée mais inexistant en dehors.

Les questions qui se posent maintenant sont les suivantes: quels identificateursa-t-on le droit d’utiliser dans une région donnée, et quels objets désignent-ils?

Soit la liste des identificateurs déclarés dans l’exemple 4.9:

• Imbricationidentificateur (nom du programme) unique;• Maxidentificateur (de constante) déclaré une fois;• Lettreidentificateur (de variable) déclaré une fois;• Nombreidentificateur (de variable) déclaré quatre fois;• Okidentificateur (de variable) déclaré une fois;• Caractereidentificateur (de paramètre) déclaré deux fois;• Niveau_2identificateur (de procédure) déclaré une fois;• Aussi_Niveau_2identificateur (de procédure) déclaré une fois;• Niveau_3identificateur (de procédure) déclaré une fois.

Quelles sont à présent la portée et la visibilité de ces identificateurs dans lesquatre régions de l’exemple 4.9? Le tableau 4.1 donne la réponse à cette

Page 99: Cours Ada de A à Z

NOTIONS DE PORTÉE ET DE VISIBILITÉ DES IDENTIFICATEURS 99

interrogation.

Les identificateurs qui portent la mention oui pour une région R (R mis pour A,B, C ou D) sont visibles directement, c’est-à-dire utilisables tels quels dans larégion R et désignent l’objet mentionné dans l’avant-dernière colonne.

Les identificateurs qui portent la mention non pour une région R n’existent pasdans la région R et ne seront donc jamais utilisables dans la région R.

Les identificateurs qui portent la mention non* pour une région R sont cachéspar un identificateur identique déclaré dans ladite région ou dans une régionenglobante. Un identificateur caché est inutilisable tel quel dans la région R. Maisun identificateur caché peut être rendu visible indirectement, donc utilisable dansla région R, en préfixant son nom par le nom de la structure englobante et ainsi desuite jusqu’à la structure dans laquelle il a été déclaré et en séparant chaque nompar un point. On appelle nom développé une telle écriture.

Par exemple, l’identificateur Nombre déclaré à la ligne 4 est caché dans la

Tableau 1.2 Portée et visibilité des identificateurs.

Identificateur Portée dans la région

A B C DObjet Déclaré

à la ligne

Imbrication A oui oui oui oui prog. princ. 1

Max A oui oui oui oui const. 10 2

Lettre A oui oui oui oui var. caract. 3

Nombre A oui non* non* non* var. entière 4

Nombre B non oui non non var. entière 6

Nombre C non non oui non* var. réelle 8

Nombre D non non non oui var. entière 10

Ok D non non non oui var. bool. 11

Caractere B non oui non non param. car. 5

Caractere D non non non oui param. car. 9

Niveau_2 B oui oui oui oui procédure 5

Aussi_Niveau_2

C oui non oui oui procédure 7

Niveau_3 D non non oui oui procédure 9

Page 100: Cours Ada de A à Z

NOTIONS DE PORTÉE ET DE VISIBILITÉ DES IDENTIFICATEURS 100

région B par le même identificateur Nombre déclaré à la ligne 6. Mais en écrivantImbrication.Nombre dans la région B, l’on se réfère à l’identificateur déclaré àla ligne 4!

Une conséquence de tout ceci est que plusieurs objets déclarés dans des régionsdifférentes peuvent porter le même nom sans que l’on risque de les confondre. Deplus il est toujours possible en Ada de préfixer un identificateur par le nom de lastructure englobante. Donc les noms développés ainsi obtenus peuvent être assezcomplexes, comme:

• Imbrication.Niveau_2.Nombre• Imbrication.Aussi_Niveau_2.Nombre• Aussi_Niveau_2.Niveau_3.Caractere

Pour l’instant, la déclaration de deux identificateurs identiques dans la mêmepartie déclarative est interdite mais il existe quelques exceptions à cette règle (sect.4.8).

Page 101: Cours Ada de A à Z

IDENTIFICATEURS LOCAUX ET GLOBAUX 101

4.5 IDENTIFICATEURS LOCAUX ET GLOBAUX

Les deux définitions suivantes s’emploient également pour caractériser unidentificateur (exemple 4.10):

• un identificateur est local (local) à une région s’il est déclaré dans laditerégion;

• un identificateur est global (global) à une région R s’il est déclaré dans unerégion englobant R et si R fait partie de la portée de cet identificateur.

Exemple 4.10 Identificateurs locaux et globaux dans l’exemple 4.9.

Maxlocal au programme principal, global à toutes les procédures;Caractere (à la ligne 5)local à la procédure Niveau_2;Niveau_2local au programme principal, global aux procéduresAussi_Niveau_2 et Niveau_3;Nombre (à la ligne 8)local à la procédure Aussi_Niveau_2, global à laprocédure Niveau_3.

Il faut insister sur le fait qu’un identificateur de procédure est local à la régionenglobant la procédure, alors qu’un paramètre de procédure est local à la régionformée par la procédure.

Il faut encore mentionner au lecteur un peu effrayé par cette terminologie queles notions ainsi définies sont conformes à une certaine logique. Avec un peud’habitude toutes ces explications paraissent même partiellement superflues. Unebonne programmation respectera cependant toujours le principe de localité desdéclarations (note 4.1).

NOTE 4.1 Principe de localité des déclarations.

En général, la déclaration d’un identificateur doit s’effectuer le plus localement possible. Le respectde cette règle permettra d’éviter absolument les variables globales nuisibles à la fiabilité desprogrammes (sect. 1.5), en particulier lors de l’adaptation du code.

Page 102: Cours Ada de A à Z

FONCTIONS 102

4.6 FONCTIONS

Une fonction (function) est également une construction permettant destructurer les programmes. Tout ce qui a été dit pour les procédures est valable pourles fonctions (structure, déclaration, imbrication, etc.), exception faite pour lesdifférences mentionnées dans les paragraphes qui suivent.

Une fonction a pour but de calculer une valeur appelée résultat (result) ouvaleur de retour (return value) de la fonction. L’en-tête d’une fonction (fig. 4.7)comprend le mot réservé function suivi du nom de la fonction et, option-nellement, de paramètres (sect. 4.3), puis du nouveau mot réservé return pré-cédant le nom du type du résultat (exemple 4.11). Seuls des paramètres d’entréesont autorisés dans l’en-tête d’une fonction.

Figure 4.7 Diagramme syntaxique définissant l’en-tête d’une fonction.

Exemple 4.11 En-têtes de fonctions.

-- En-tetes sans parametrefunction Nombre_Aleatoire return Floatfunction Annee_Actuelle return Integer

-- En-tetes avec parametresfunction Cube (X : in Integer) return Integerfunction Log_Naturel (Nombre : in Float := 2.71828) return Floatfunction Est_Pair (Nombre : Integer) return Boolean

L’appel d’une fonction n’est pas une instruction (contrairement à l’appel deprocédure) mais fait toujours partie d’une expression. Il consiste à nommer lafonction et à donner la liste des paramètres effectifs entre parenthèses si nécessaire.A l’appel de la fonction, les paramètres sont transmis puis elle est exécutée et sonrésultat (voir ci-après) est utilisé pour calculer l’expression à laquelle la fonctionappartient (exemple 4.12). L’appel d’une fonction est plus prioritaire que n’importequel opérateur ou attribut.

En-tête de fonction

function identificateur liste de paramètres return identificateurde type

Page 103: Cours Ada de A à Z

FONCTIONS 103

Exemple 4.12 Appels des fonctions de l’exemple 4.11.

Variable_Reelle := Nombre_Aleatoire;Put ( Cube (Nombre) );Un := Log_Naturel;Resultat := 2.0 * Log_Naturel (Variable_Reelle) / 5.7;if Pair ( Cube (Nombre) ) then ... end if;

Le résultat d’une fonction est obtenu grâce à l’instruction return. Cetteinstruction a la forme suivante:return expression;

où• l’expression doit être du même type que celui mentionné dans l’en-tête

de la fonction, après le mot réservé return!

L’exécution de cette instruction termine celle de la fonction et le résultat decelle-ci est la valeur de l’expression. Plusieurs instructions return peuvent êtreplacées à l’intérieur d’une fonction. Dans ce cas, la fonction se termine par l’exécu-tion de la première d’entre-elles. L’exemple 4.13 illustre le corps d’une fonction.

Exemple 4.13 Exemple complet de fonction.

-- Calcule le cube du nombre passe en parametrefunction Cube (X : in Integer) return Integer is

begin -- Cube

return X ** 3;

end Cube;

Une fonction doit impérativement se terminer par l’exécution d’une instructionreturn. Si ce n’est pas le cas et que l’exécution arrive à la fin de la fonction (auend final), une erreur survient et l’exception Program_Error (sect. 7.3) est levée.

Page 104: Cours Ada de A à Z

SPÉCIFICATION ET CORPS DES SOUS-PROGRAMMES 104

4.7 SPÉCIFICATION ET CORPS DES SOUS-PROGRAMMES

On appelle traditionnellement sous-programme (subprogram, subroutine) uneprocédure ou une fonction. Pour des raisons qui deviendront plus tard impératives(sect. 10.3), il est possible de déclarer un sous-programme en deux parties (voiraussi note 4.2) nommées spécification (specification) et corps (body). Une spécifi-cation consiste en l’en-tête suivi immédiatement d’un point-virgule, alors que lessous-programmes complets vus jusqu’ici constituent en fait des corps, composésde l’en-tête suivi du mot réservé is, puis de la partie déclarative (éventuellementvide) et finalement des instructions encadrées par les mots réservés begin et end(exemple 4.14). Même si le langage Ada permet plus de souplesse il faut, poursimplifier, retenir que l’en-tête présent dans la spécification doit être strictement lemême que celui du corps.

Exemple 4.14 Spécifications et corps.

-- Deux specifications...procedure Dessiner_Carre ( Cote : in Integer );-- Noter le ;function Cube ( X : in Integer ) return Integer;-- Noter le ;

-- ... et les corps correspondants:

-- Cette procedure dessine un carre dont la longueur du cote est-- passee en parametreprocedure Dessiner_Carre ( Cote : in Integer ) is-- Noter le isbegin -- Dessiner_Carre

-- Dessin d'un carre dont le cote vaut CoteLine ( Cote, 0 );Line ( 0, Cote );Line ( –Cote, 0 );Line ( 0, –Cote );

end Dessiner_Carre;

-- Calcule le cube du nombre passe en parametrefunction Cube (X : in Integer) return Integer is-- Noter le isbegin -- Cube

return X ** 3;end Cube;

NOTE 4.2 Déclarations des spécifications et des corps des procédures et des fonctions.

Comme l’exemple 4.14 pourrait le suggérer, le fait de déclarer d’abord toutes les spécifications puistous les corps est une bonne habitude de programmation, augmentant la lisibilité d’un programme.Cependant le langage Ada laisse libre l’ordre des déclarations (§ 3.9.3).

Page 105: Cours Ada de A à Z

SPÉCIFICATION ET CORPS DES SOUS-PROGRAMMES 105

Page 106: Cours Ada de A à Z

SURCHARGE DES SOUS-PROGRAMMES 106

4.8 SURCHARGE DES SOUS-PROGRAMMES

Jusqu’à présent, il était interdit de déclarer, dans la même partie déclarative,deux identificateurs identiques, alors que cette possibilité est autorisée si lesdéclarations se trouvent dans deux parties déclaratives distinctes. Dans ce derniercas la deuxième déclaration peut cacher la première (sect. 4.4).

Or, à certaines conditions, deux ou plusieurs sous-programmes peuvent porterle même identificateur et être déclarés dans la même partie déclarative. Cesconditions doivent permettre de distinguer les sous-programmes entre eux. Commeleurs noms sont identiques, il faut autre chose pour permettre cette distinction.

Deux sous-programmes sont distincts si leurs profils (profile) sont distincts. Leprofil d’une procédure consiste en le nombre et l’ordre des types des paramètres.Le profil d’une fonction consiste en le nombre et l’ordre des types des paramètresainsi que le type du résultat. Deux procédures sont donc distinctes si le nombre oul’ordre des types des paramètres sont différents (les noms des paramètres, lesmodes de passage ou les valeurs par défaut ne jouent aucun rôle). De même, deuxfonctions sont distinctes si le nombre ou l’ordre des types des paramètres, ou letype du résultat sont différents (ici aussi les noms des paramètres ou les valeurs pardéfaut ne jouent aucun rôle). La surcharge (overloading) est le fait de pouvoirdéclarer des sous-programmes de même nom mais cependant distincts. Enfin, onappelle homologues des sous-programmes qui ne sont pas distincts. Des spéci-fications distinctes ou non sont données dans l’exemple 4.15.

Exemple 4.15 Spécifications distinctes ou non.

procedure P ( Param_1 : in T_Param_1; -- Procedure donneeParam_2 : out T_Param_2 );

----------------------------------------------------------------procedure P; -- Distincte de la procedure donnee

procedure P ( Param : in T_Param); -- Egalement distincte

procedure P ( Param_1 : in T_Param_1; -- Egalement distincteParam_2 : out T_Param_2;Param_3 : in out T_Param_3);

-- Distincte si T_Param_1 est different de T_Param_2procedure P ( Param_1 : in T_Param_2;

Param_2 : out T_Param_1 );

-- Homologue de la procedure donneeprocedure P ( Param_1 : in T_Param_1;

Param_2 : in T_Param_2 );----------------------------------------------------------------function F ( Param : T_Param ) return T_Res;-- Fonction donnee----------------------------------------------------------------function F return T_Res; -- Distincte de la fonction donnee

-- Distincte si T_Res2 est different de T_Res

Page 107: Cours Ada de A à Z

SURCHARGE DES SOUS-PROGRAMMES 107

function F ( Param : T_Param ) return T_Res2;

-- Homologue de la fonction donneefunction F ( Param : T_Param := ...) return T_Res;

Dans tous les cas, un sous-programmes surchargé doit être appelé sans ambi-guïté. Cela signifie que le nombre, le type des paramètres effectifs et le type attendupour le résultat d’une fonction doivent permettre au compilateur de déterminer quelsous-programme est appelé. Si aucun, ou plus d’un sous-programme correspond àl’appel, celui-ci est alors ambigu et le compilateur générera une erreur.

Exemple 4.16 Appels de procédures surchargées.

procedure P; -- Trois procedures surchargeesprocedure P ( I : in Integer := 0);procedure P ( I : in Float );...P ( 1 ); -- Deuxieme procedure appeleeP ( 1.0 ); -- Troisieme procedure appeleeP; -- Appel ambigu (erreur a la

compilation)

Dans l’exemple 4.16, il est impossible d’appeler la première procédure puisquela notation P; pourrait signifier non seulement l’appel de la première procéduremais aussi l’appel de la deuxième avec utilisation de la valeur par défaut!

Page 108: Cours Ada de A à Z

SURCHARGE DES OPÉRATEURS 108

4.9 SURCHARGE DES OPÉRATEURS

Le langage Ada permet de déclarer des fonctions-opérateurs qui surchargentcertains opérateurs prédéfinis. Ces opérateurs surchargeables sont:

• les opérateurs abs not + – (unaires);• les opérateurs and or xor mod rem = /= < <= > >= + – * / ** & (binaires).

Il faut cependant qu’une fonction surchargeant un opérateur unaire n’ait qu’unparamètre, alors qu’une fonction surchargeant un opérateur binaire doit en avoirexactement deux. Le «nom» de la fonction-opérateur est celui de l’opérateur entreguillemets. L’appel peut se faire sous la forme fonctionnelle, mais pour des raisonspratiques, comme pour l’opérateur surchargé (exemple 4.17).

Exemple 4.17 Déclarations et appels de fonctions-opérateurs.

-- Addition entre un entier et un reelfunction "+" ( I : Integer;

F : Float ) return Float;

function "+" ( F : Float;I : Integer ) return Float;

-- Pour illustrer une fonction-operateur a un parametrefunction "abs" ( Valeur : Un_Type ) return Un_Type;

X := "+"( 3, 2.3 ); -- Premiere fonction "+" appeleeX := 3 + 2.3; -- Premiere fonction "+" appeleeX := 2.3 + 3; -- Seconde fonction "+" appeleeX := 2.0 + 3.0; -- Operateur "+" predefini pour les

reelsV := abs Val; -- Val de type Un_Type (sect. 5.1)

Pour être complet, remarquons que si l’opérateur d’égalité "=" est surchargéavec un résultat de type Boolean, alors l’opérateur d’inégalité "/=" estimplicitement redéfini. Finalement l’opérateur d’inégalité "/=" peut être redéfiniexplicitement uniquement si le résultat de la fonction-opérateur qui le surchargen’est pas Boolean.

Page 109: Cours Ada de A à Z

COMPLÉMENTS 109

4.10 COMPLÉMENTS

Comme mentionné auparavant (sect. 4.7), on nomme traditionnellement sous-programme une construction comme une procédure ou une fonction qui, par sastructure, rappelle un programme. En Ada ledit programme est d’ailleurs uneprocédure (sect. 1.9) alors que c’est une fonction dans le langage C.

Une instruction return (sans expression) peut être présente dans le corpsd’une procédure. Dans ce cas, son exécution provoque simplement la fin del’exécution de la procédure, comme si le end final avait été atteint.

Page 110: Cours Ada de A à Z

110

4.11 EXERCICES

4.11.1 Dessin d’un paysage

Ecrire un programme qui dessine un paysage schématique où le dessin dechaque élément du paysage est effectué par une procédure. Il faudra donc créer, parexemple, des procédures comme Dessiner_Sapin, Dessiner_Rocher,Dessiner_Chalet, Dessiner_Fenêtre_Chalet, etc.

4.11.2 Calcul du nombre de jours entre deux dates

Ecrire un programme qui calcule le nombre de jours entre deux dates, enintroduisant deux sous-programmes, le premier qui retourne le nombre de joursd’un mois donné et le second qui indique si une année est bissextile ou non ensachant qu’une année est bissextile si elle est divisible par 400, ou alors par 4 maispas par 100.

4.11.3 Conversions d’unités de mesure

Ecrire des procédures de conversion d’unités de mesure anglo-saxonnes (pieds,pouces, livre, etc.) en unités SI (système international: mètre, kilo etc.) et inver-sement.

4.11.4 Conversions d’unités de mesure

Reprendre l’exercice 4.11.3 et transformer les procédures en fonctions.

4.11.5 Transformation de procédure en fonction

Transformer en fonction la procédure Majusculiser du paragraphe 4.3.4.

4.11.6 Notations par position et par nom

Soit la spécification de procédure suivante:procedure P (Par_Logique : in Boolean;

Par_Entier : Integer;Par_Reel : in out Float;Par_Caractere : out Character);

Ecrire des appels de P en utilisant la notation par nom, puis celle par position,et enfin en mélangeant les deux.

4.11.7 Surcharge de sous-programmes

Parmi les spécifications suivantes, quelles sont celles qui se surchargent?procedure P;procedure P ( E : in Integer);procedure P ( A : Integer);procedure P ( E : in out Integer);procedure P ( E : in Integer := 0);

Page 111: Cours Ada de A à Z

111

procedure P ( E : out Integer);procedure P ( E : in Integer;

S : out Float);procedure P ( E : in Float;

S : out Float);procedure P ( E : in Integer;

S : out Float);

Page 112: Cours Ada de A à Z

POINTS À RELEVER 112

4.12 POINTS À RELEVER

4.12.1 En général

• Les procédures et les fonctions constituent les sous-programmes.

• Les sous-programmes servent à regrouper des instructions pour faciliter lalisibilité des programmes et pour remplacer cette suite d’instructions par unseul appel.

• Les paramètres de sous-programmes permettent aux sous-programmes deproduire des résultats qui dépendent de ces paramètres.

• Les paramètres dits formels sont ceux déclarés dans l’en-tête d’un sous-programme.

• Les paramètres effectifs sont les valeurs transmises à un sous-programmeou les variables qui recueilleront des valeurs calculées par le sous-programme.

• La portée d’un identificateur est la zone de texte dans laquelle l’iden-tificateur est utilisable.

• Un identificateur est local à une région s’il est déclaré à l’intérieur de larégion.

• Un identificateur est global à une région s’il est déclaré hors de la région etutilisable dans la région.

• Il faudrait absolument respecter le principe de localité des déclarations.

4.12.2 En Ada

• Les procédures peuvent comporter des paramètres d’entrée, d’entrée et desortie ou de sortie.

• Les fonctions ne peuvent comporter que des paramètres d’entrée.

• Une fonction retourne toujours un seul résultat qui peut être de n’importequel type.

• Les paramètres formels d’entrée peuvent mentionner une valeur par défaut.

• La notation par position, par nom ou un mélange des deux permet l’écrituredes paramètres effectifs dans l’appel d’un sous-programme.

• Un identificateur visible directement peut être mentionné tel quel.

• L’utilisation d’un identificateur caché nécessite un nom développé.

• Un sous-programme peut être séparé en une spécification et un corps.

• Il est possible de déclarer des opérateurs comme des fonctions.

Page 113: Cours Ada de A à Z

POINTS À RELEVER 113

• Les sous-programmes et les opérateurs peuvent être surchargés.

• L’instruction return termine l’exécution d’un sous-programme et fournitle résultat dans le cas d’une fonction.

• Les spécifications devraient toutes être déclarées avant les corps.

Page 114: Cours Ada de A à Z

114

C H A P I T R E 5

T Y P E S , T Y P E S É N U M É R AT I F S ,

Page 115: Cours Ada de A à Z

115 TYPES, TYPES ÉNUMÉRATIFS, INSTRUCTION CASE

Page 116: Cours Ada de A à Z

NOTION DE TYPE 116

5.1 NOTION DE TYPE

5.1.1 Motivation

La connaissance des types et des instructions en Ada est relativement limitéejusqu’à présent. Quatre types de base (Integer, Character, Boolean, Float) ethuit instructions (affectation, if, while, for, loop, exit, return et appel deprocédure) ont été expliqués.

Le chapitre en cours ainsi que les suivants présentent tous les autres types dulangage Ada à savoir les types énumératifs, numériques, articles, tableaux, chaînesde caractères, pointeurs et fichiers ainsi que les instructions case, null, raise etl’instruction-bloc.

Ceci va nous permettre de réaliser des programmes importants non seulementdu fait de leur corps parfois volumineux mais aussi et surtout à cause des donnéescomplexes qu’ils vont manipuler. Les données ainsi que la manière de lesreprésenter forment ce que l’on appelle les structures d’information ou structuresde données (data structures) des programmes.

5.1.2 Généralités

Un type (type) est formé d’un groupe de valeurs et d’opérations possibles surces valeurs. Le tableau 5.1 donne quelques exemples de types prédéfinis.

Lorsque l’on parle, par exemple, de «valeur de type Integer», cela veut dire«une valeur appartenant à une catégorie particulière de nombres entiers(Integer), valeur avec laquelle on peut additionner, soustraire, multiplier, diviser,prendre le reste de la division...».

Comme déjà mentionné, les types permettent de structurer les données. Ilspermettent aussi au compilateur de générer du code effectuant des vérifications devalidité des données comme, par exemple, détecter le débordement de capacité

Tableau 5.1 Exemples de types et de leurs opérations.

Nom du type Valeurs du type Opérations possibles

Integer tous les nombres entiers comprisentre les bornes Integer'Firstet Integer'Last

affectation, addition, soustraction,comparaison, attributs, etc.

Charac-ter

tous les caractères compris entre lesdeux bornes Character'Firstet Character'Last

affectation, comparaison, attributs,etc.

Boolean les deux valeurs False etTrue

affectation, comparaison, attributs,etc.

Page 117: Cours Ada de A à Z

NOTION DE TYPE 117

lorsqu’un nombre entier devient trop grand. Mais pour le programmeur ils ontl’incomparable avantage de le forcer à respecter la règle des types. Celle-ci,particulièrement stricte en Ada, définit que doivent en particulier être du mêmetype:

• tous les opérandes d’une expression;• le type de l’expression et celui de la variable dans une instruction

d’affectation;• le type d’un paramètre formel et celui du paramètre effectif correspondant.

Cette règle permet au compilateur de détecter, avant l’exécution du programme,des erreurs commises par le programmeur appelées erreurs de sémantique. Uneerreur de sémantique se produit lorsque la syntaxe d’une instruction est respectéemais que l’instruction est néanmoins incorrecte (exemple 5.1).

Exemple 5.1 Erreurs de sémantique.

if 3 + 4 then...3 + 4 n’est pas booléen;Variable_Entiere := 3.5;3.5 n’est pas entier;for I in 1..'9' loop... 1 et '9' ne sont pas du même type;Ada.Float_Text_IO.Put ( 10 );le type du paramètre formel (Float, § 2.6.6) estdifférent du type du paramètre effectif (Integer).

En plus des types prédéfinis, le programmeur peut créer ses propres types àpartir de constructions syntaxiques adéquates. Pour pouvoir utiliser ses proprestypes il faut d’abord les déclarer dans une ou plusieurs parties déclaratives. Sichaque catégorie de type possède son style propre de déclaration, le début de ladéclaration d’un type est commun à une majorité de types:

type Nom_Du_Type is

Deux cas particuliers existent mais ne seront pas développés ici (les typestâches et objets protégés). L’exemple 5.2 présente une liste non exhaustive dedéclarations de types.

Exemple 5.2 Déclarations de types.

-- Sect. 5.2type Jours_De_La_Semaine is ( Lundi, Mardi, Mercredi, Jeudi,

Vendredi, Samedi, Dimanche );

-- Sect. 5.2type Mois_De_L_Annee is ( Janvier, Fevrier, Mars, Avril, Mai,

Juin, Juillet, Aout, Septembre,Octobre, Novembre, Decembre );

Page 118: Cours Ada de A à Z

NOTION DE TYPE 118

type Date is record -- § 7.2.1Jour : Integer;Mois : Mois_De_L_Annee;Annee : Integer;

end record;

type Tableau is array (1..10) of Float; -- § 8.2.2

type Monnaie is new Float; -- Sect. 19.2

type Pointeur is access Date; -- Sect. 15.2

type Table is private; -- Sect. 16.2

type Queue is limited private; -- Sect. 16.9

task type Locomotive; -- Les deux cas particuliersprotected type Tampon is ... end Tampon;

La notion de type va donc permettre de catégoriser les données traitées, ce quiimplique une représentation plus agréable et un traitement plus sûr (règle des ty-pes!) de ces mêmes données.

Par convention, on peut faire précéder le nom d’un type par le préfixe T_ ou lefaire suivre par le suffixe _Type, ce qui permet de préciser que cet identificateurest le nom d’un type (et non celui d’une variable par exemple).

Page 119: Cours Ada de A à Z

TYPES ÉNUMÉRATIFS 119

5.2 TYPES ÉNUMÉRATIFS

5.2.1 Motivation

Comment représenter les jours de la semaine par exemple? Jusqu’à présent, laseule façon de faire était de déclarer sept constantes de la manière suivante:

Lundi : constant := 0;Mardi : constant := 1;Mercredi : constant := 2;

etc.

Ceci peut conduire à des erreurs ou être fastidieux à déclarer. Un typeénumératif va avantageusement résoudre ce genre de problèmes.

5.2.2 Généralités

Les types énumératifs (enumeration types) permettent de déclarer des valeursen les énumérant sous la forme d’une suite de noms. Il est ainsi possible d’écrire:

type T_Jours_De_La_Semaine is ( Lundi, Mardi, Mercredi, Jeudi,Vendredi, Samedi, Dimanche );

Cette déclaration permet l’utilisation du type T_Jours_De_La_Semaineformé des valeurs Lundi, Mardi... Dimanche. L’exemple 5.3 présente deux autrestypes énumératifs.

Exemple 5.3 Deux exemples traditionnels de types énumératifs.

type T_Couleurs_Arc_En_Ciel is ( Rouge, Orange, Jaune, Vert,Bleu, Indigo, Violet );

type T_Mois_De_L_Annee is ( Janvier, Fevrier, Mars, Avril, Mai,Juin, Juillet, Aout, Septembre,Octobre, Novembre, Decembre );

Les constantes d’un type énumératif sont par définition les identificateursénumérés entre les parenthèses.

Les opérations possibles (en plus de l’affectation et du passage en paramètre)sur les valeurs d’un type énumératif sont les comparaisons = /= <= < >= >. En effetles valeurs d’un type énumératif sont ordonnées, codées selon leur ordre dedéclaration. A chaque valeur énumérée correspond un numéro d’ordre (nombreentier). La première valeur porte le numéro 0, la seconde le numéro 1, etc. Dansl’exemple du type T_Jours_De_La_Semaine, Lundi porte le numéro 0, Mardiporte le numéro 1, etc. Donc:

Lundi < Mardi < Mercredi < Jeudi < Vendredi < Samedi < Dimanche

Les expressions se limitent aux constantes, variables et attributs (§ 5.2.4)

Page 120: Cours Ada de A à Z

TYPES ÉNUMÉRATIFS 120

applicables aux valeurs du type énumératif considéré. Finalement, les typesénumératifs font partie des types discrets (discrete types, [ARM 3.2]).

5.2.3 Affectation

L’affectation se fait de manière habituelle, comme dans l’exemple 5.4.

Exemple 5.4 Affectation de valeurs d’un type énumératif.

-- ...procedure Exemple_5_4 is

type T_Jours_De_La_Semaine is ( Lundi, Mardi, Mercredi, Jeudi,Vendredi, Samedi, Dimanche );

Demain : T_Jours_De_La_Semaine; -- Trois variables de ce typeHier : T_Jours_De_La_Semaine;Jour : T_Jours_De_La_Semaine := Lundi;

begin -- Exemple_5_4

Demain := Jour; -- Affecte la valeur Lundi a DemainJour := Jeudi; -- Affecte la valeur Jeudi a JourDemain := T_Jours_De_La_Semaine'Succ ( Jour );-- § 5.2.4Hier := T_Jours_De_La_Semaine'Pred ( Jour );-- § 5.2.4...

5.2.4 Attributs First, Last, Succ, Pred, Pos et Val

Les attributs First, Last, Succ et Pred sont applicables aux valeurs d’untype énumératif, ce qui permet maintenant de préciser que ces attributs sont en faitapplicables aux valeurs de n’importe quel type discret (discrete type, [ARM 3.2]).Comme pour les caractères, les attributs Pos et Val permettent de travailler avecles numéros d’ordre des valeurs énumérées. Mais les cas sont rares où une tellemanipulation est indispensable! Quelques cas d’utilisation de ces attributs sontprésentés dans l’exemple 5.5.

Exemple 5.5 Utilisation des attributs First, Last, Succ, Pred, Pos et Val.

T_Jours_De_La_Semaine'First donne la première valeur, à savoir Lundi;T_Jours_De_La_Semaine'Lastdonne Dimanche;T_Jours_De_La_Semaine'Pred (Mardi)donne Lundi;T_Jours_De_La_Semaine'Succ (Mardi)donne Mercredi;T_Jours_De_La_Semaine'Pos (Lundi)donne 0, le numéro d’ordre de lavaleur Lundi;T_Jours_De_La_Semaine'Val (6)donne Dimanche;T_Jours_De_La_Semaine'Pred (Lundi)provoque une erreur de compilation;

Page 121: Cours Ada de A à Z

TYPES ÉNUMÉRATIFS 121

T_Jours_De_La_Semaine'Val (Nombre)provoque une erreur à l’exécution siNombre > 6.

5.2.5 Entrées-sorties

Comme pour tous les types simples, Ada offre la possibilité d’effectuer desentrées-sorties sur des valeurs énumérées. Sans en décrire ni expliquer le méca-nisme utilisé (sect. 17.4), ces entrées-sorties sont disponibles, utilisables commepour les booléens (§ 3.4.5) après avoir effectué la déclaration suivante:

package ES_Jours is newAda.Text_IO.Enumeration_IO ( T_Jours_De_La_Semaine );

Exemple 5.6 Entrées-sorties de valeurs énumérées.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_5_6 is

type T_Jours_De_La_Semaine is ( Lundi, Mardi, Mercredi, Jeudi,Vendredi, Samedi, Dimanche );

Jour : T_Jours_De_La_Semaine; -- Une variable de ce type

package ES_Jours is new Enumeration_IO(T_Jours_De_La_Semaine);

use ES_Jours; -- § 2.6.9

begin -- Exemple_5_6

Put_Line (" Cet exemple..." );

Get (Jour); -- Lit un jour de la semainePut (Jour); -- Affiche en majuscules la valeur

luePut (Mardi); -- Affiche MARDIPut (T_Jours_De_La_Semaine'Val(6)); -- Affiche DIMANCHE...

Comme suggéré dans l’exemple 5.6, les valeurs énumérées sont affichées enmajuscules sur le nombre minimum de positions. Il est cependant possible demodifier ces aspects d’affichage [ARM A.10.10], à savoir le nombre de positionset la casse.

5.2.6 Types énumératifs, types Boolean et Character

Le type Boolean est en fait un type énumératif prédéfini:type Boolean is (False, True);

Ceci n’enlève rien aux notions présentées dans la section concernée (sect. 3.4)

Page 122: Cours Ada de A à Z

TYPES ÉNUMÉRATIFS 122

mais permet simplement de préciser que tout ce qui s’applique aux typesénumératifs est également valable pour le type Boolean.

Le type prédéfini Character fait aussi partie des types énumératifs car il estpossible de déclarer des caractères entre apostrophes comme valeurs énumérées.Cette dernière notion n’est cependant pas présentée dans cet ouvrage car, exceptionfaite du type Character (et parfois Wide_Character, sect. 19.3), elle est peuutilisée dans la pratique.

5.2.7 Types énumératifs et instruction for

Un type énumératif peut être utilisé pour donner l’intervalle de variation d’uneboucle for (§ 3.5.2). En utilisant le type T_Jours_De_La_Semaine ces trois formessont équivalentes:

• for Jour in Lundi..Dimanche loop ... end loop;• for Jour in T_Jours_De_La_Semaine'First..Dimanche loop• ...

• end loop;• for Jour in T_Jours_De_La_Semaine loop ... end loop;

Cette dernière forme utilise le nom du type comme intervalle, ce qui signifieque l’intervalle comporte toutes les valeurs, de la première à la dernière. Cetteforme d’intervalle (§ 6.1.1) permet d’assurer, même en cas de modification du typeT_Jours_De_La_Semaine, que l’intention du programmeur est respectée ipsofacto, c’est-à-dire que la variable de boucle prendra toujours toutes les valeurs dutype énumératif.

Page 123: Cours Ada de A à Z

INSTRUCTION CASE 123

5.3 INSTRUCTION CASE

5.3.1 Motivation

Les sections traitant de l’instruction if (sect. 3.1 à 3.3) ont montré commentprogrammer des sélections. Ici prend place la programmation d’un choix multiple,basé non pas sur les valeurs d’expressions booléennes comme l’instruction ifgénéralisée (sect. 3.3) mais sur les valeurs d’un type discret. Cette sélection seraréalisé par l’instruction case.

Comment poser une question à l’utilisateur du programme en supposant quecelui-ci répond par un chiffre et que l’exécution du programme se poursuit enfonction de ce chiffre? Jusqu’à présent ce problème serait résolu par une instructionif généralisée. La façon suivante de procéder est plus lisible et plus sûre:

Reponse : Integer; -- Nombre donne par l'utilisateur...Get ( Reponse ); -- Reponse donnee par l'utilisateur

case Reponse iswhen 1 =>...; -- Traitement si Reponse est 1

when 2 | 3 =>...; -- Traitement si Reponse est 2 ou 3

when 4 .. 6 =>...; -- Traitement si Reponse est 4, 5 ou 6

when 7 .. 9 | 99 =>...; -- Traitement si Reponse est entre 7-- et 9, ou 99

when others =>...; -- Traitement si Reponse est autre-- chose qu'un nombre entre 1 et 9

ou 99end case;

5.3.2 Forme générale de l’instruction case

La forme générale de l’instruction case est:case expression iswhen choix_1 => traitement_1;-- Premiere branchewhen choix_2 => traitement_2;-- Deuxieme branchewhen choix_3 => traitement_3;-- Troisieme branche...when others => autre_traitement;-- Derniere brancheend case;

avec• expression d’un type discret (discrete type, [ARM 3.2]);• un nouveau mot réservé when délimitant les branches de l’instruction;• les choix_n statiques (sect. 3.10), formés de valeurs ou d’intervalles

séparés par des barres verticales, valeurs et bornes d’intervalle du type del’expression;

Page 124: Cours Ada de A à Z

INSTRUCTION CASE 124

• les traitements_n composés d’une ou de plusieurs instructions;• le mot réservé others qui représente toutes les autres valeurs du type.

L’exécution d’une instruction case débute par l’évaluation de l’expression.Puis la branche (le traitement) correspondant à la valeur est choisie et exécutée.Puis le reste de l’instruction case est sauté et l’exécution se poursuit après la fin(end case) de l’instruction.

5.3.3 Compléments sur l’instruction case

Un choix (représentant une valeur possible de l’expression) ne peut apparaîtrequ’une seule fois.

Si l’expression a une valeur ne correspondant à aucun des choix mentionnésexplicitement, la branche commençant par when others, qui doit être la dernière,est exécutée.

L’instruction null qui ne fait rien (!) peut être utilisée dans une branche oùaucun traitement ne doit être effectué. L’instruction null n’est d’ailleurs pasrestreinte à une telle branche mais est utilisable partout où une instruction estautorisée, et utile là où une instruction est exigée par le langage Ada mais où leprogrammeur ne veut rien faire.

Si les choix mentionnés explicitement (autres que others) couvrent toutes lesvaleurs possibles de l’expression, alors la branche commençant par when othersest optionnelle. Donc, si la variable Jour est du type T_Jours_De_La_Semaine(§ 5.2.2), alors la forme suivante est autorisée:

case Jour is

-- Appel d'une procedurewhen Lundi .. Vendredi => Travailler;

-- Appel d'une autre procedurewhen Samedi => Faire_La_Fête;

-- Ne rien faire le dimanche!when Dimanche => null;

end case;

Page 125: Cours Ada de A à Z

125

5.4 EXERCICES

5.4.1 Déclaration de types énumératifsDéclarer des types énumératifs pour représenter:

• les couleurs de l’arc-en-ciel;• les capitales des pays de l’Union européenne.

5.4.2 Utilisation de types énumératifs

Ecrire un programme qui affiche toutes les valeurs d’un type énumératif, ainsique le prédécesseur, le successeur et la position de chacune d’entre elles.

5.4.3 Calcul du nombre de jours entre deux datesReprendre l’exercice 4.11.2 et modifier la solution de manière à ce que le sous-

programme qui retourne le nombre de jours d’un mois donné utilise le typeT_Mois_De_L_Annee (§ 5.2.2) et une instruction case.

5.4.4 Petites statistiques sur une ligne de texteReprendre l’exercice 3.11.6 et modifier la solution de manière à ce que la

catégorisation des caractères lus soit effectuée par une instruction case.

5.4.5 Mini-calculatriceEcrire un programme qui simule une mini-calculatrice en calculant des

expressions formées d’opérandes réels, des opérateurs +, –, *, / et terminées par lesymbole = comme 2.0 + 3.5 * 4.2 = par exemple. Pour des raisons de simplicité, lapriorité des opérateurs peut être ignorée (l’expression est alors simplementcalculée de gauche à droite).

Page 126: Cours Ada de A à Z

POINTS À RELEVER 126

5.5 POINTS À RELEVER

5.5.1 En général

• Un type est formé d’un groupe de valeurs et d’opérations possibles sur cesvaleurs.

• Les types servent à structurer les données et à permettre des vérifications àla compilation comme à l’exécution des programmes.

• La règle des types renforce la fiabilité des programmes en permettant ladétection d’erreurs par le compilateur.

• Certains types sont prédéfinis, d’autres peuvent être construits par le pro-grammeur.

5.5.2 En Ada

• Un type énumératif permet l’énumération des valeurs (discrètes) du type.

• Les types énumératifs et les types entiers forment les types discrets.

• Les entrées-sorties de valeurs énumérées nécessitent une déclaration spé-ciale.

• L’instruction case (sélection) permet l’exécution d’une branche d’ins-tructions parmi plusieurs, éventuellement aucune, en fonction de la valeurd’une expression discrète.

Page 127: Cours Ada de A à Z

127

C H A P I T R E 6

S O U S -T Y P E S , T Y P E S N U M É R I Q

Page 128: Cours Ada de A à Z

128 SOUS-TYPES, TYPES NUMÉRIQUES, EXCEPTIONS

Page 129: Cours Ada de A à Z

SOUS-TYPES 129

6.1 SOUS-TYPES

6.1.1 Généralités

Un sous-type (subtype) permet d’appliquer une contrainte (constraint) à untype appelé type de base. Cette contrainte peut prendre différentes formes selon quele type de base est discret, réel ou autre. Pour l’instant, l’une des formes les plusutilisées, nécessaire pour les notions à venir est la contrainte d’intervalle,applicable à tout type discret [ARM 3.2]. Elle consiste à restreindre l’ensemble desvaleurs possibles d’un tel type (valeurs qui forment en fait déjà un intervalle) en unintervalle plus petit (exemple 6.1).

Un intervalle (range) s’écrit en respectant l’une des trois formes suivantes:borne_inf .. borne_sup identificateur_de_type_ou_sous_type attribut_range

où• borne_inf et borne_sup sont des expressions d’un type de base discret;• identificateur_de_type_ou_sous_type d’un type de base égale-

ment discret ;• attribut_range sera introduit plus loin (§ 8.2.7).

Exemple 6.1 Exemples d’intervalles.

0..9valeurs entières de 0 à 9, le type de base est Integer;Lundi..Vendredivaleurs énumérées de Lundi à Vendredi, le type de baseest T_Jours_De_La_Semaine (§ 5.2.2);'A'..'Z'lettres majuscules, le type de base est Character;Integer'First..–1nombres entiers négatifs, le type de base est Integer;I..J – 1nombres entiers de I à J–1, le type de base est un type entier (celuide I et J) avec le fait que, si I >= J, l’intervalle est vide;Val_1..Val_2les valeurs de Val_1 à Val_2, le type de base peut êtren’importe quel type discret;Characterl’intervalle des 256 valeurs du type prédéfini Character (§3.8.1);T_Jours_De_La_Semainel’intervalle des 7 valeurs (noms des jours) du typeT_Jours_De_La_Semaine (§ 5.2.2).

Une contrainte d’intervalle (sur un type discret) a la forme suivante:range intervalle

où• range est un nouveau mot réservé;• intervalle est soit borne_inf..borne_sup soit attribut_range

Page 130: Cours Ada de A à Z

SOUS-TYPES 130

(identificateur_de_type_ou_sous_type est ici interdit).

Il est maintenant possible de donner la forme générale de la déclaration d’unsous-type d’un type discret par le diagramme syntaxique de la figure 6.1.

Figure 6.1 Diagramme syntaxique définissant un sous-type discret.

Exemple 6.2 Déclarations de sous-types de types discrets.

subtype T_Chiffre is Integer range 0 .. 9;

subtype T_Jours_Travail is T_Jours_De_La_Semainerange Lundi..Vendredi;

subtype T_Majuscules is Character range 'A'..'Z';

subtype T_Negatifs is Integer range Integer'First .. –1;

subtype T_Dynamique is Integer range 1 .. Max;-- Max, variable-- entiere

subtype T_Entiers is Integer;

Les deux dernières déclarations de l’exemple 6.2 nécessitent des explicationscomplémentaires. Le sous-type T_Dynamique obtient la borne supérieure par lavaleur de la variable Max. Cette valeur est donc fournie à l’exécution et non à lacompilation. Il faut donc s’assurer que Max ait une valeur bien définie avant ladéclaration de T_Dynamique. De plus le sous-type T_Entiers, comme aucunecontrainte n’est donnée, est en fait un synonyme d’Integer. Cette façon de fairepeut être utilisée pour surnommer n’importe quel type. Il existe des situations, enrelation avec l’utilisation de paquetages (sect. 10.5), où un tel surnommage estpratique pour changer le nom d’un type exporté.

6.1.2 Utilisation et utilité des sous-types

Un sous-type n’introduit donc pas un nouveau type mais permet de restreindreles valeurs utilisables d’un type. Un identificateur de sous-type peut être utilisé, en

Déclaration de sous-type discret

Type_de_basesubtype Identificateur is

Contrainte

;

d’intervalle

Page 131: Cours Ada de A à Z

SOUS-TYPES 131

général, là où un identificateur de type est permis.

Exemple 6.3 Utilisation de sous-types de l’exemple 6.2.

Chiffre : T_Chiffre;variable dont les valeurs doivent se situer entre 0 et9;Jour : T_Jours_Travail;variable dont les valeurs doivent se situer entreLundi et Vendredi;T_Jours_Travail'Firstattribut de valeur Lundi;T_Jours_Travail'Lastattribut de valeur Vendredi;procedure P (Lettre : in T_Majuscules);Le paramètre effectif devraêtre une lettre majuscule.

L’attribution d’une valeur à un objet dont la déclaration comporte unecontrainte, donnée par un sous-type par exemple, est autorisée si la valeur respectela contrainte (exemple 6.4). Dans le cas contraire, une erreur (l’exceptionConstraint_Error, sect. 6.3) est générée à l’exécution du programme.

Exemple 6.4 Attribution de valeurs à des objets dont la déclaration comporte une contrainte.

Chiffre : T_Chiffre;deux variables contraintes par un sous-type;Jour : T_Jours_Travail;

Nombre : Integer;une variable de type Integer;Lettre : Character;une variable de type Character;...

Jour := Mercredi;toujours correct;Nombre := Chiffre;toujours correct;Chiffre := Nombre;correct si la valeur de Nombre est entre 0 et 9, une erreurest générée dans le cas contraire;Jour := Samedi;erreur générée, Samedi n’est pas jour de travail;P ( 'A' );toujours correct (P est declarée dans l’exemple 6.3);P ( Lettre );correct si la valeur de Lettre est une majuscule, une erreurest générée dans le cas contraire.

Le principal intérêt des sous-types réside dans le fait que leur utilisation permetla détection d’erreurs à l’exécution (note 6.1), lorsque des valeurs inappropriéessont attribuées à un objet (affectation à une variable, passage en paramètre, etc.).

NOTE 4.3 Des erreurs à l’exécution sont moins graves que des résultats faux.

Page 132: Cours Ada de A à Z

SOUS-TYPES 132

Il faut toujours avoir à l’esprit qu’un programme qui produit des résultats faux est plus difficile àcorriger qu’un programme qui se termine brutalement par une erreur, surtout si l’apparition del’erreur donne des indications précises sur l’endroit erroné du programme. L’arrivée d’erreurs, aussifrustrantes soient-elles, doit donc être considérée comme un symptôme moins grave que laproduction de résultats incertains.

Les sous-types imposent donc au programmeur une réflexion plus approfondiedans le choix des valeurs pour des variables et permettent la vérification àl’exécution des contraintes sur ces variables. Il faut préciser que cette vérificationest effectuée par du code adéquat généré par le compilateur.

6.1.3 Sous-types prédéfinis

Il n’existe que deux sous-types prédéfinis dont la déclaration est la suivante:subtype Natural is Integer range 0 .. Integer'Last;subtype Positive is Integer range 1 .. Integer'Last;

Ces deux sous-types d’Integer s’utilisent lorsqu’un objet entier doit contenirune valeur positive ou nulle (Natural) ou strictement positive (Positive).

6.1.4 Attributs applicables aux sous-types

De manière générale, les attributs applicables à un type sont utilisables avectout sous-type de ce type (exemple 6.5). Il faut simplement préciser que, si la valeurretournée par un attribut appliqué à un sous-type appartient au type de base, ellen’est pas soumise à la contrainte imposée par le sous-type.

Exemple 6.5 Attributs et sous-types (voir exemple 6.2).

T_Jours_Travail'Firstattribut de valeur Lundi;T_Jours_Travail'Succ(Vendredi)attribut de valeur Samedi;T_Jours_Travail'Pred(Jour)erreur générée à l’exécution si Jour vaut Lundi;T_Jours_Travail'Val(0)attribut de valeur Lundi;T_Jours_Travail'Val(6)attribut de valeur Dimanche;T_Chiffre'Pos(0)attribut de valeur 0;T_Negatifs'Lastattribut de valeur –1;T_Negatifs'Succ(T_Negatifs'Last)attribut de valeur 0.

Finalement, comme le nom d’un sous-type discret représente également l’in-tervalle des valeurs du sous-type, ce nom peut s’utiliser partout où un intervalle estautorisé (sauf dans une contrainte d’intervalle), en particulier dans les instructionsfor (sect. 3.5) et case (sect. 5.3) comme par exemple:

-- La boucle s'effectuera cinq fois

Page 133: Cours Ada de A à Z

SOUS-TYPES 133

for Jour in T_Jours_Travail loop...;

end loop;

case Jour is -- Jour du type T_Jours_De_La_Semaine

when T_Jours_Travail => ...; -- Travaillerwhen Samedi | Dimanche => ...; -- Se reposer

end case;

6.1.5 Tests d’appartenance

Il existe deux tests d’appartenance (ressemblant à deux opérateurs) in et notin, de priorité égale à celle des opérateurs de comparaison. Ils permettent devérifier si une valeur appartient à un intervalle, bornes comprises (exemple 6.6). Letype de l’intervalle (ou du sous-type) doit être un type discret [ARM 3.2] alors quele résultat est naturellement de type Boolean. Comme in et not in ne sont pasdes opérateurs, ils ne peuvent donc pas être surchargés (sect. 4.9).

Exemple 6.6 Utilisation de tests d’appartenance.

Jour in T_Jours_Travailvrai si Jour est un jour de travail;Jour not in T_Jours_Travailvrai si Jour est Samedi ou Dimanche;Nombre in T_Chiffrevrai si Nombre est un chiffre;Lettre in 'A'..'Z'vrai si Lettre est une majuscule;Indice in T_Ligne'Range§ 8.2.7.

Page 134: Cours Ada de A à Z

TYPES NUMÉRIQUES 134

6.2 TYPES NUMÉRIQUES

6.2.1 Motivation

les types numériques prédéfinis en Ada (Integer, Float, etc.) suffisent pourles applications courantes. Ils ont l’avantage de correspondre, en général, à desvaleurs et des opérations efficacement implémentées par le matériel. Ils ont parcontre l’inconvénient de dépendre de l’implémentation, c’est-à-dire de voir leurintervalle de valeurs et leur précision, pour les types réels, varier d’une machine àl’autre ou selon les compilateurs. Cette situation se retrouve dans la grandemajorité des langages de programmation. L’adaptation à un nouveau matérield’une application utilisant les types prédéfinis peut de ce fait poser de nombreusesdifficultés.

Une des particularités intéressantes d’Ada est de permettre au concepteur dedéfinir lui-même ses types numériques afin d’une part de choisir un bon équilibreentre efficacité et portabilité (sect. 1.5) et d’autre part de renforcer les contrôles ducompilateur dans le but d’éviter de mélanger des données numériques, comme deseuros et des dollars dans des calculs bancaires par exemple.

6.2.2 Types entiers signés

Toute implémentation d’Ada comprend le type prédéfini Integer. Maiscomme mentionné auparavant (§ 2.2.7), d’autres types tels que Short_Integerou Long_Integer peuvent également être fournis. Cependant tous sontdépendants de la machine utilisée. Pour renforcer la portabilité d’une applicationet compléter les contrôles du compilateur (note 6.2), Ada permet donc ladéclaration de types entiers signés (exemple 6.7) de la manière suivante:

type identificateur is range borne_inf .. borne_sup;

où• borne_inf et borne_sup sont des expressions entières statiques (sect.

3.10) appartenant à l’intervalle des nombres entiers disponibles sur lamachine (§ 2.5.2).

Exemple 6.7 Déclaration de types entiers signés.

type T_Octet_Signe is range –128 .. 127;type T_Indice_Signe is range 0 .. 1000;type T_Euros is range –1E12 .. 1E12;type T_Dollar is range –1E12 .. 1E12;

Ces types sont dits signés car la suite de bits implémentant toute valeur d’un teltype verra l’un de ses bits (souvent celui de poids le plus fort) interprété comme bitde signe.

Page 135: Cours Ada de A à Z

TYPES NUMÉRIQUES 135

Les opérations possibles applicables à de tels types sont les mêmes que pour letype prédéfini Integer (§ 2.2.2) avec la particularité que le second opérande del’opérateur arithmétique ** doit toujours être du sous-type Natural.

L’affectation, le passage en paramètre et l’utilisation d’attributs s’effectuentégalement comme pour Integer. Pour réaliser des entrées-sorties, il faut avoirdéclaré

package ES_Entiers_Signes is newAda.Text_IO.Integer_IO ( Id_Type_Entier_Signe );

où• ES_Entiers_Signes est le nom du paquetage d’entrées-sorties créé,

nom choisi par le programmeur;• Id_Type_Entier_Signe est l’identificateur du type entier signé.

Une fois cette déclaration effectuée et de manière analogue aux types énumé-ratifs (§ 5.2.5), il est alors possible d’utiliser Get et Put pour lire et écrire desvaleurs du type Id_Type_Entier_Signe. Le préfixe ES_Entiers_Signes doitêtre mentionné, sauf si une clause use ES_Entiers_Signes suit cette ligne dedéclaration.

Le lecteur malin aura réalisé que le nom développé Ada.Integer_Text_IO(sect. 2.6) vient d’une déclaration prédéfinie qui s’écrit

package Ada.Integer_Text_IO is newAda.Text_IO.Integer_IO ( Integer );

NOTE 6.1 Avantages apportés par l’utilisation de types entiers signés.

L’un des avantages d’utiliser ses propres types entiers signés réside dans le fait que ceux-ci serontidentiques dans n’importe quelle implémentation. Si la déclaration d’un tel type ne pouvait êtreréalisée sur une machine donnée, le compilateur refuserait ladite déclaration. Dans ce cas il faudraitmodifier le type avec la conséquence que la portabilité de l’application en souffrirait.Le second avantage de l’utilisation de tels types se situe dans les contrôles du compilateur quiverifiera ainsi l’absence de mélange de valeurs entières de types différents.

Attention à ne pas confondre une déclaration de type entier signé avec une dé-claration de sous-type entier (§ 6.1.1).

6.2.3 Types entiers non signés (types modulo)

Il est parfois pratique de travailler avec des entiers non signés, c’est-à-dire dontl’intervalle de définition va de zéro à une borne supérieure positive. C’est le caslorsque l’on veut, par exemple, effectuer des calculs «modulo N». Ada permet ladéclaration de types entiers non signés ou types modulo de la manière suivante:

type identificateur is mod le_modulo;

Page 136: Cours Ada de A à Z

TYPES NUMÉRIQUES 136

où• le_modulo est une expression entière positive statique (sect. 3.10), souvent

une puissance de 2; par définition, l’intervalle des valeurs du type esttoujours 0 .. le_modulo – 1 (exemple 6.8).

Exemple 6.8 Déclaration de types entiers non signés.

type T_Octet is mod 256;valeurs de 0 à 255;type T_Mot is mod 65536;valeurs de 0 à 65535;type T_Indice is mod 1000;valeurs de 0 à 999.

Ces types sont dits non signés car la suite de bits implémentant toute valeurd’un tel type n’aura aucun bit interprété comme bit de signe.

Les opérations applicables à de tels types sont les mêmes que pour le typeprédéfini Integer (§ 2.2.2) avec la particularité que le second opérande del’opérateur arithmétique ** doit toujours être du sous-type Natural.

La principale caractéristique de ces types est qu’il ne peut jamais se produire dedébordement de capacité car toute l’arithmétique est effectuée moduloLe_Modulo. De plus, comme c’est le cas pour l’arithmétique d’un processeur, lesopérateurs and, or, xor et not sont applicables à des opérandes d’un type modulo,considérés alors comme des suites de bits.

L’affectation, le passage en paramètre et l’utilisation d’attributs s’effectuentégalement comme pour Integer. Pour réaliser des entrées-sorties, il faut avoirdéclaré

package ES_Entiers_Non_Signes is newAda.Text_IO.Modular_IO ( Id_Type_Entier_Non_Signe );

où• ES_Entiers_Non_Signes est le nom du paquetage d’entrées-sorties

créé, nom choisi par le programmeur;• Id_Type_Entier_Non_Signe est l’identificateur du type entier non

signé.

De manière analogue aux types énumératifs (§ 5.2.5), il est alors possibled’utiliser Get et Put pour lire et pour écrire des valeurs d’un type commeId_Type_Entier_Non_Signe. Le préfixe ES_Entiers_Non_Signes doit êtrementionné, sauf si cette ligne de déclaration est suivie d’une clause useES_Entiers_Non_Signes.

6.2.4 Types réels point-flottant

Toute implémentation d’Ada comprend le type point-flottant prédéfini Float.

Page 137: Cours Ada de A à Z

TYPES NUMÉRIQUES 137

Mais comme mentionné auparavant (§ 2.3.5) d’autres types tels que Short_Floatou Long_Float peuvent également être disponibles. Encore une fois tous sontdépendants de la machine utilisée. Pour renforcer la portabilité d’une applicationmais aussi pour assurer la précision souhaitée dans les applications numériques(calcul scientifique), Ada permet la déclaration de types réels point-flottant(exemple 6.9) de la manière suivante:

type identificateur is digits nb_chiffres;

où• nb_chiffres (statique) représente la précision désirée.

Notons tout de suite qu’il est encore possible de contraindre un tel type endéfinissant un intervalle statique (§ 6.1.1).

Exemple 6.9 Déclaration de types point-flottant.

type T_Reel_9 is digits 9;type T_Reel_12 is digits 12;type T_Unite_6 is digits 6 range 0.0 .. 0.999999;

Les opérations possibles applicables à de tels types sont les mêmes que pour letype prédéfini Float (§ 2.3.2). L’affectation, le passage en paramètre et l’utili-sation d’attributs s’effectuent également comme pour Float.

Pour réaliser des entrées-sorties, il faut avoir déclarépackage ES_Reels_Point_Flottant is new

Ada.Text_IO.Float_IO ( Id_Type_Reel_Point_Flottant );

où• ES_Reels_Point_Flottant est le nom du paquetage d’entrées-sorties

créé, nom choisi par le programmeur;• Id_Type_Reel_Point_Flottant est l’identificateur du type réel point-

flottant.

De manière analogue aux types énumératifs (§ 5.2.5), il est alors possibled’utiliser Get et Put pour lire et pour écrire des valeurs d’un type commeId_Type_Reel_Point_Flottant. Le préfixe ES_Reels_Point_Flottantdoit être mentionné, sauf si cette ligne de déclaration est suivie d’une clause useES_Reels_Point_Flottant.

La réalisation d’applications numériques demanderait un approfondissement del’utilisation des types réels point-flottant, ce qui dépasse l’objectif de ce texte.

6.2.5 Types réels point-fixe

Page 138: Cours Ada de A à Z

TYPES NUMÉRIQUES 138

Un type réel point-fixe est utile s’il est nécessaire de travailler avec desnombres réels pour lesquels la différence entre deux nombres consécutifs estconstante (er-reur absolue) comme lors du calcul de durées temporelles ou dans lesapplications comptables. Un tel type se déclare de la manière suivante (exemple6.10):

type identificateur is delta erreur range borne_inf .. borne_sup;

où• erreur (statique) représente l’erreur tolérée;• borne_inf et borne_sup sont des expressions réelles statiques.

Exemple 6.10 Déclaration de types point-fixe.

type T_Reel_01 is delta 0.1 range –1.0 .. 1.0;type T_Secondes is delta 0.001 range 0.0 .. 86_400.0;

Les opérations possibles applicables à de tels types sont les mêmes que pour letype prédéfini Float (§ 2.3.2) avec quelques particularités non détaillées ici. Demanière un peu surprenante, il n’existe qu’un seul type point-fixe prédéfini nomméDuration (semblable au type T_Secondes), utilisé lorsqu’il est nécessaire detenir compte du temps (applications temps réel). Les entrées-sorties sont possiblesgrâce au paquetage Ada.Text_IO.Fixed_IO utilisé de manière semblable à celleprésentée pour les types point-flottant. Mentionnons encore qu’il existe des typesréels point-fixe décimaux non traités dans ce texte.

6.2.6 Conversions entre types numériques

Comme introduit précédemment entre Integer et Float (§ 2.5.1), il esttoujours possible de convertir explicitement (exemple 6.11) une valeur d’un typenumérique en une valeur identique ou approchée d’un autre type numérique appelétype cible (target type). La valeur numérique résultant de la conversion doit bienévidemment respecter les contraintes du type (ou sous-type) cible.

Exemple 6.11 Conversions entre types entiers.

Octet_Signe : T_Octet_Signe; -- Quatre variables entieresOctet : T_Octet := 64;Billet : T_Euros;Valeur : Integer := 100;...Octet_Signe := T_Octet_Signe ( Octet ); -- Valeur 64 convertieBillet := T_Euros ( Valeur ); -- Valeur 100 convertieOctet := T_Octet ( 3 * Billet ); -- Erreur a l'execution

Page 139: Cours Ada de A à Z

TYPES NUMÉRIQUES 139

6.2.7 Types entier et réel universels

Les constantes numériques (les nombres et les nombres nommés) ne possèdentpas de type explicite, mais sont par convention d’un type (entier ou réel) appeléuniversel. Une valeur de type entier universel peut être utilisée partout où unevaleur entière est autorisée, sans nécessiter de conversion explicite. Cette règles’applique de manière similaire aux valeurs de type réel universel.

Certains attributs, comme Pos (§ 5.2.4), Digits (§ 2.3.4), ou encore Length(§ 8.2.7) retournent un résultat entier également universel.

6.2.8 Remarque sur la portabilité d’une application

Il faut cependant noter qu’obtenir un programme entièrement portable en ce quiconcerne l’utilisation des nombres demande un soin non seulement dans la décla-ration des types numériques mais encore dans l’utilisation des opérateurs. En effetdes résultats intermédiaires (valeurs d’expressions partielles) hors de l’intervallede définition peuvent provoquer ou non une erreur à l’exécution (exemple 6.12).

Exemple 6.12 Cas de non-portabilité d’une expression arithmétique.

-- ...procedure Exemple_6_12 is

type T_Octet is range –128 .. 127; -- Un type entier signe

Nombre : T_Octet; -- Deux variables de ce typeTemp : T_Octet;

begin -- Exemple_6_12

Temp := 100; -- Toujours correct

Nombre := (Temp+Temp) / 2; -- Affecte la valeur 100 à Nombre ou-- provoque une erreur a

l'execution-- car Temp+Temp est hors de T_Octet

...

Pour assurer une portabilité intégrale, il faudrait que le résultat de touteexpression complète ou partielle soit toujours dans l’intervalle de définition dutype utilisé.

Page 140: Cours Ada de A à Z

EXCEPTIONS 140

6.3 EXCEPTIONS

6.3.1 Motivation

La notion d’erreur à l’exécution a déjà été rencontrée à plusieurs reprises. Dansla majorité des cas il s’agissait du dépassement d’une borne inférieure ousupérieure d’un intervalle. Or, très souvent, une erreur d’exécution dans uneapplication provoque sa fin brutale avec messages d’erreurs, bombes, fichierscorrompus, etc. Mais un programme écrit en Ada peut récupérer et traiter deserreurs d’exécution si le concepteur de l’application l’a prévu, ceci parce que lelangage met à disposition un mécanisme de gestion des erreurs appelées exceptions(exceptions).

Ce paragraphe va montrer comment traiter ces erreurs. La déclaration et la levéeexplicite (par une instruction particulière) d’exceptions seront présentées plus loin(chap. 13).

6.3.2 Généralités

En Ada, il existe quatre exceptions prédéfinies:

• Constraint_Error, qui est provoquée par toute violation de contrainte,par exemple en cas de dépassement d’une borne inférieure ou supérieured’un intervalle, ou en divisant par 0. C’est de loin l’exception qui survientle plus fréquemment!

• Program_Error, qui survient lors de la violation d’une structure decontrôle comme l’arrivée sur le end final d’une fonction;

• Storage_Error, qui apparaît lors d’un dépassement de mémoire;

• Tasking_Error, qui ne sera pas détaillée ici.

Le fait qu’une erreur d’exécution se produise est appelé levée (raising) del’exception (exemple 6.13).

Exemple 6.13 Situations où l’exception Constraint_Error est levée.

Fortune : Positive;trois déclarations (§ 6.1.3 et 5.2.2);Code : Integer := 10;

Jour : T_Jours_De_La_Semaine := Dimanche;

...

Fortune := 0;le nombre 0 n’appartient pas au sous-type Positive;Get ( Fortune );exception si l’utilisateur donne un nombre négatif ou nul;Fortune := System.Max_Int;exception si Positive’Last inférieur àSystem.Max_Int (§ 2.5.2);Fortune := Fortune – 1;exception si Fortune de valeur 1;Fortune := Fortune / 0;division par 0;

Page 141: Cours Ada de A à Z

EXCEPTIONS 141

Jour := T_Jours_De_La_Semaine'le successeur de Dimanche n’existe pas;Succ ( Jour );Jour := T_Jours_De_La_Semaine'pas de jour de numéro d’ordre égal à 10.Val ( Code );

6.3.3 Levée (automatique) et propagation d’une exception

Pendant l’exécution d’un programme, une exception peut être levée par uneerreur dans une instruction. Dans les cas simples présentés dans l’exemple 6.14l’exception, toujours Constraint_Error, provoque la fin du programme. Eneffet, lors de la levée d’une exception, les instructions restantes du corps sontabandonnées et, dans le cas d’un programme principal, celui-ci est quitté avec, engénéral, un message d’erreur qui dépend de l’implémentation.

Exemple 6.14 Cas simples de levée de l’exception Constraint_Error.

-- Premier exempleprocedure Exemple_6_14_1 is

Nombre : Positive;

begin -- Exemple_6_14_1

...

Nombre := 0; -- 0 n'appartient pas au sous-type Positive

...

end Exemple_6_14_1;

----------------------------------------------------------------- Deuxieme exempleprocedure Exemple_6_14_2 is

Nombre : Integer := 0;

procedure P ( Valeur : in Natural ) is ... end P;

begin -- Exemple_6_14_2

...

P ( Nombre – 3 ); -- –3 n'appartient pas au sous-type Natural

...

end Exemple_6_14_2;

Une exception peut être levée non seulement lors d’une instruction mais aussià l’élaboration (§ 3.9.3) d’une déclaration. L’exemple 6.15, qui n’est pas écrit demanière très structurée (!), va permettre d’illustrer ces deux situations et, à nouveau

Page 142: Cours Ada de A à Z

EXCEPTIONS 142

et dans les détails, le phénomène de propagation d’une exception.

Exemple 6.15 Levées d’exceptions.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

-- ...procedure Principal is

Nombre : Integer; -- Nombre dont on calcule les puissances

-------------------------------------------------------------- ...function Exposant_Maximum return Natural is

Exposant : Natural; -- Exposant jusqu'auquel seront-- calculees les puissances

begin -- Exposant_MaximumPut ( "Veuillez donner l'exposant maximum: " );Get ( Exposant ); -- 1Skip_Line;return Exposant;

end Exposant_Maximum;-------------------------------------------------------------- ...procedure Afficher_Puissances ( Nombre : in Integer ) is

Limite : Positive := Exposant_Maximum;-- 2

begin -- Afficher_PuissancesPut_Line ( "Et voici la liste des puissances:" );

-- Affichage de toutes les puissances souhaiteesfor N in Natural'First..Limite loop

if N rem 8 = 0 then -- Maximum huit puissances par ligneNew_Line;

end if;Put ( Nombre ** N ); -- 3

end loop;

New_Line;

end Afficher_Puissances;begin -- Principal

-- Presentation du programmePut ( "Calcul des puissances d'un nombre entier jusqu'a " );Put_Line ( "un exposant maximum." );

-- Lire le nombre dont on veut calculer les puissancesPut ( "Veuillez donner ce nombre: " );Get ( Nombre ); -- 4Skip_Line;

-- Affichage des puissances du nombre donne par l'utilisateurAfficher_Puissances ( Nombre );

Page 143: Cours Ada de A à Z

EXCEPTIONS 143

end Principal;

Les lignes suivies d’un commentaire formé d’un seul numéro sont celles où uneexception est susceptible d’être levée. En effet, si l’utilisateur donne un nombrehors de l’intervalle des valeurs du type Integer, Data_Error (sect. 12.5) seralevée à la ligne 4 car Nombre est du type Integer et si l’utilisateur donne unexposant maximum négatif, elle sera levée à la ligne 1 car Exposant est du sous-type Natural. Si l’utilisateur donne un exposant nul, Constraint_Error seralevée à la ligne 2 car Limite est du sous-type Positive et si l’exposant est assezgrand, elle sera levée à la ligne 3 car la valeur de l’expression Nombre ** N serahors de l’intervalle des valeurs du type Integer et ne pourra donc pas être passéeen paramètre à la procédure Put.

Que devient l’exception après sa levée? Elle interrompt dans tous les cas lecours normal de l’exécution et provoque soit un «saut» à la fin du corps (sect. 4.7)contenant la ligne (cas 1, 3 et 4) soit l’abandon de la partie déclarative (cas 2). Lasuite dépend de la ligne ayant provoqué sa levée. En l’absence de tout traitement(§ 6.3.4), comme c’est le cas dans notre exemple, si l’exception provient de la ligne4, alors le programme Principal se termine et l’environnement Ada va afficherun message d’erreur contenant entre autres le nom de l’exception. Si l’exceptionprovient de la ligne 3, la procédure Afficher_Puissances se termine etl’exception est à nouveau levée au point d’appel de ladite procédure (ce quiprovoquera la fin du programme principal comme cela vient d’être mentionné). Cephénomène est appelé propagation (propagation) de l’exception.

Si l’exception provient de la ligne 2, la procédure Afficher_Puissances setermine sans avoir commencé l’exécution de sa première instruction et l’exceptionest propagée au point d’appel dans le programme principal. Finalement, sil’exception provient de la ligne 1, Exposant_Maximum se termine sans retournerde valeur et l’exception est propagée au point d’appel, c’est-à-dire à la ligne 2.

Il faut encore insister sur le fait qu’une exception est levée à l’exécution et quesa propagation est donc dynamique: l’exception remonte selon les appels de procé-dure ou de fonction jusqu’au programme principal (en l’absence de traitement).

6.3.4 Traitement d’une exception

Il n’est jamais agréable de voir une application se terminer brutalement par unmessage d’erreur. Le traitement (handling) d’une exception en Ada consiste àintercepter l’exception pour l’empêcher de remonter jusqu’au système d’ex-ploitation et à rétablir la situation en supprimant ou en évitant la cause de l’erreur.Mais il faut toujours garder à l’esprit qu’il vaut mieux voir apparaître une erreurqu’obtenir des résultats faux à l’exécution d’une application (note 6.1). Il ne fautdonc réagir et supprimer l’exception que si l’application est capable d’y faire face,

Page 144: Cours Ada de A à Z

EXCEPTIONS 144

de retrouver un état cohérent.

Le traitement d’une exception consiste à compléter la fin d’un corps deprocédure, de fonction, ou encore de bloc (§ 6.3.5), immédiatement avant le endfinal, par une zone particulière appelée traite-exception (exception handler). Ellea la forme suivante, ressemblant à celle de l’instruction case (§ 5.3.2):

exceptionwhen choix_1 => traitement_1;-- premiere branchewhen choix_2 => traitement_2;-- deuxieme branche...when others => autre_traitement;-- derniere branche

avec• un nouveau mot réservé exception qui débute le traite-exception, après

la dernière instruction du corps et avant le end final;• les choix_n formés d’un ou de plusieurs identificateurs d’exception

séparés par des barres verticales;• les traitements_n composés d’une ou de plusieurs instructions;• le mot réservé others qui représente tous les autres cas d’exceptions; cette

branche est optionnelle.

Lorsqu’une exception est levée dans les instructions du corps contenant un teltraite-exception, l’exécution est transférée dans la branche comportant le nom del’exception si celle-ci est mentionnée explicitement ou dans la dernière branche sicelle-ci est présente. Puis l’exécution se poursuit normalement dans le traitementde la branche et l’exception est éliminée. Si le nom de l’exception ne fait pas partiedes choix et s’il n’y a pas de branche commençant par when others, l’exceptionest propagée, comme si le traite-exception n’existait pas.

Il faut d’ailleurs noter que si l’exécution d’une instruction du traite-exceptionlève une exception (!), celle-ci est également propagée.

Il n’est pas possible de revenir là où l’exception s’est produite, sauf par unartifice utilisant la structure de bloc, car le traitement dans une branche du traite-exception remplace le reste du corps et termine ainsi l’exécution du corps. Unexemple d’utilisation d’un bloc pour répéter une instruction ayant provoqué uneexception est donné dans le paragraphe 6.3.5.

Exemple 6.16 Modification de la fonction et de la procédure de l’exemple 6.15.

-- ...function Exposant_Maximum return Natural is

Exposant : Natural; -- Exposant jusqu'auquel seront-- calculees les puissances

begin -- Exposant_Maximum

Put ( "Veuillez donner l'exposant maximum: " );

Page 145: Cours Ada de A à Z

EXCEPTIONS 145

Get ( Exposant ); -- 1Skip_Line;

return Exposant;

exception

when Constraint_Error =>Skip_Line; -- Eliminer les caracteres restantsPut_Line ( "Exposant negatif, 1 arbitrairement." );return 1;

when others =>Skip_Line; -- Eliminer les caracteres restantsPut_Line ( "Exposant errone, 20 arbitrairement." );return 20;

end Exposant_Maximum;-------------------------------------------------------------- ...procedure Afficher_Puissances ( Nombre : in Integer ) is

Limite : Positive := Exposant_Maximum;-- 2

begin -- Afficher_PuissancesPut_Line ( "Et voici la liste des puissances:" );

-- Affichage de toutes les puissances souhaiteesfor N in Natural'First..Limite loop

if N rem 8 = 0 then -- Maximum huit puissances par ligneNew_Line;

end if;

Put ( Nombre ** N ); -- 3

end loop;New_Line;

exception

when Constraint_Error =>Put_Line ( "Puissance trop grande. Fin de l'affichage" );

end Afficher_Puissances;

Avec les modifications de l’exemple 6.16, la levée de l’exceptionConstraint_Error à la ligne 1 provoque le transfert de l’exécution dans le traite-exception de la fonction où un message d’avertissement est affiché, puis la fonctionse termine en retournant la valeur arbitraire 20. Avec ces mêmes modifications, lalevée de l’exception Constraint_Error à la ligne 3 provoque le transfert del’exécution dans le traite-exception de la procédure où un message d’avertissementest affiché, puis le programme se termine normalement.

Mais malgré ces modifications, l’exception Constraint_Error levée à laligne 2 n’est pas traitée par le traite-exception placé à la fin de la procédureAfficher_Puissances. En effet, une exception levée par une déclaration d’un

Page 146: Cours Ada de A à Z

EXCEPTIONS 146

sous-programme est toujours propagée au point d’appel du sous-programme, quecelui-ci ait ou non un traite-exception. Pour éviter ce problème il faudraitnaturellement remplacer Positive par Natural dans la déclaration.

Finalement, il est intéressant de voir comment traiter les cas des lignes 1 et 4 sil’on veut que le programme s’exécute sans erreur, avec des valeurs autorisées. Lasolution va nécessiter l’utilisation de la notion de bloc.

6.3.5 Notion de bloc

Un bloc (block) est une instruction particulière ayant la forme suivante:id_bloc: declarepartie_declarative;beginsuite_d_instructions;exceptionbranches_de_traitement;end id_bloc;

avec• id_bloc le nom du bloc; cet identificateur est optionnel;• un nouveau mot réservé declare débutant la partie déclarative du bloc, le

tout aussi optionnel;• la suite_d_instructions composée d’une ou de plusieurs instruc-

tions entre les mots réservés begin et end;• le traite-exception, lui aussi optionnel.

L’exécution d’un bloc consiste en l’élaboration des déclarations puisl’exécution de la suite d’instructions. Lorsque le bloc est terminé, les objets locauxdisparaissent et les instructions qui suivent sont exécutées. Si une exceptionsurvient dans la suite d’instructions, elle peut être traitée par le traite-exception. Sielle survient dans une déclaration, elle est propagée à l’unité englobante. Commec’est une instruction, un bloc peut donc s’utiliser partout où une instruction estpermise.

Un bloc peut être utilisé pour traiter localement une exception (exemple 6.17),ou encore pour déclarer des objets dont certaines caractéristiques ne sont connuesqu’après les instructions précédant le bloc.

Exemple 6.17 Modification de la fonction de l’exemple 6.16.

-- ...function Exposant_Maximum return Natural is

Exposant : Natural; -- Exposant jusqu'auquel seront-- calculees les puissances

begin -- Exposant_Maximum

Page 147: Cours Ada de A à Z

EXCEPTIONS 147

loop -- Boucler si Constraint_Error ou une autrePut ( "Exposant maximum: " ); -- exception levee

begin -- Bloc sans partie declarativeGet ( Exposant ); -- Instructions du blocSkip_Line;

return Exposant;

exception -- Traite-exception du blocwhen Constraint_Error =>

Skip_Line; -- Eliminer les caracteres restantsPut_Line ( "Exposant negatif. Recommencer." );

when others =>Skip_Line; -- Eliminer les caracteres restantsPut_Line ( "Exposant errone. Recommencer." );

end;end loop;

end Exposant_Maximum;

L’exécution de la fonction va s’arrêter sur l’instruction Get pour que l’utilisa-teur donne un exposant. S’il se trompe (nombre négatif, trop grand ou contenant uncaractère interdit), une exception est levée par l’instruction Get et provoque letransfert dans le traite-exception où l’une des deux branches est exécutée. Puis laboucle recommence et l’utilisateur donne à nouveau un exposant. Lorsquel’exposant est correct, la fonction se termine par l’instruction return Exposant.

6.3.6 Compléments

Il ne faut pas oublier que les paramètres d’entrée-sortie ou de sortie doivent êtrecorrectement affectés à la sortie d’une procédure, même si cette sortie se fait parl’exécution d’une branche d’un traite-exception. De même il faut veiller à terminertoute branche d’un traite-exception d’une fonction par une instruction return.

L’utilisation des traite-exceptions doit se limiter aux cas exceptionnels!L’exemple ci-dessous doit être évité et écrit différemment:

begin -- Debut d'un blocJour := T_Jours_de_La_Semaine'Succ ( Jour ); -- § 5.2.2

exceptionwhen Constraint_Error => Jour := T_Jours_de_La_Semaine'First;

end;

Il est en effet facile de détecter le dernier jour de la semaine et de modifier Jouren conséquence sans utiliser d’exception.

Il faut absolument éviter d’utiliser des traite-exceptions comportant la branchewhen others => null; car de telles branches font disparaître les exceptions, cequi rend la recherche des causes d’erreurs difficile lorsque l’utilisateur duprogramme se rend compte que celui-ci ne se comporte pas comme prévu.

Page 148: Cours Ada de A à Z

EXCEPTIONS 148

En plus des quatre exceptions prédéfinies (§ 6.3.2), certains paquetages faisantpartie de la norme définissent et exportent d’autres exceptions. C’est le cas dupaquetage Ada.IO_Exceptions [ARM A.13] qui déclare huit exceptionsrelatives aux opérations d’entrées-sorties comme Put et Get par exemple.

Page 149: Cours Ada de A à Z

149

6.4 EXERCICES

6.4.1 Utilisation de types et sous-types entiers signés

Soient les déclarations:subtype T_Sous_Entier is Integer range 1..20;type T_Millier is range -1000..1000;

I : Integer := 0;M : T_Millier := T_Millier'Last;

Donner la valeur et le type des expressions ci-dessous, ou expliquer pourquoicertaines sont fausses:

M / II + 10 / T_Sous_Entier(I)M - 10 + T_Millier(I + 10) * (I + 1)100 - Integer(M) * T_Sous_Entier'Last

6.4.2 Utilisation de types entiers non signés

Soient les déclarations:type T_Huit is mod 8;

H : T_Huit := 1;

Donner la valeur des expressions ci-dessous:T_Huit'Succ((H + 1) + 5)(H + 3) ** 4not HH xor 2

Pourquoi l’expression et l’affectation ci-dessous sont-elles fausses?H + 10H := 10;

6.4.3 Utilisation d’un test d’appartenance

Récrire la fonction Majusculiser du paragraphe 4.3.4 en utilisant un testd’appartenance.

6.4.4 Traitement d’exceptions

Ecrire un traite-exception qui affiche des messages en fonction des exceptionsConstraint_Error et Program_Error.

Page 150: Cours Ada de A à Z

POINTS À RELEVER 150

6.5 POINTS À RELEVER

6.5.1 En général

• Des erreurs à l’exécution sont graves, mais des résultats faux, ou quiparaissent être corrects, le sont tout autant.

6.5.2 En Ada

• Un sous-type restreint les valeurs du type de base en imposant unecontrainte.

• Un intervalle est une première forme de contrainte.

• Le nom d’un sous-type discret peut représenter l’intervalle restreint desvaleurs, sauf dans une contrainte d’intervalle.

• Un identificateur de sous-type s’utilise partout où un identificateur de typeest possible.

• Des attributs sont applicables aux sous-types.

• Les types numériques comprennent les types entiers et les types réels.

• Integer, Float sont des exemples particuliers, prédéfinis, de typesnumériques.

• Les types entiers peuvent être signés ou non.

• N’importe quelle valeur numérique peut être convertie en une valeur d’unautre type numérique.

• Les entrées-sorties de valeurs numériques nécessitent une ou plusieursdéclarations spéciales.

• L’avantage d’utiliser ses propres types numériques réside dans le fait queceux-ci seront identiques dans n’importe quelle implémentation et dans lescontrôles renforcés du compilateur.

• Les exceptions permettent la gestion des erreurs d’exécution d’unprogramme.

• Constraint_Error est l’exception rencontrée le plus fréquemment etsignale qu’une contrainte a été violée.

• Une exception est levée par l’exécution d’une instruction ou par l’élabo-ration d’une déclaration.

• Une exception peut être traitée dans un traite-exception placé à la fin d’uncorps.

• En l’absence de traitement, une exception est propagée à la structureappelante.

Page 151: Cours Ada de A à Z

POINTS À RELEVER 151

• Attention aux valeurs des paramètres d’entrée et de sortie, ou de sortie,ainsi qu’au résultat de fonction en cas de traitement d’exception dans unsous-programme.

• Les exceptions sont faites pour les cas exceptionnels (!).

• Les exceptions s’utilisent pour détecter des erreurs; il ne faut donc pas lesfaire disparaître simplement par la branche when others => null.

• En Ada, un bloc est une instruction particulière formée d’une partiedéclarative optionnelle et d’un corps.

Page 152: Cours Ada de A à Z

152

C H A P I T R E 7

A R T I C L E S S A N S

Page 153: Cours Ada de A à Z

153 ARTICLES SANS DISCRIMINANTS

Page 154: Cours Ada de A à Z

MOTIVATION 154

7.1 MOTIVATION

Les données traitées jusqu’à présent sont toutes d’un type élémentaire scalaire(scalar types, [ARM 3.2]), c’est-à-dire d’un type définissant un ensemble devaleurs individuelles. Or il existe des informations constituées de plusieurs élé-ments (composantes) formant un tout comme par exemple:

• les dates chronologiques (année, mois, jour);• les nombres complexes (parties réelles et imaginaires, ou module et

argument);• les fiches bibliographiques (titre du livre, auteur, date de parution, ISBN...);• les fiches personnelles (nom, prénom, âge, sexe, taille...).

La nature de ces composantes, souvent de types différents, conduit à utiliser unestructure permettant la définition explicite de chacun de ces éléments: les articles.Selon les besoins et comme dans la réalité, les informations contenues dans unarticle pourront être manipulées comme un tout ou alors gérées individuellement.Par exemple, une fiche personnelle peut être imprimée ou archivée mais il estpossible de modifier uniquement l’âge ou la taille d’une personne.

Page 155: Cours Ada de A à Z

GÉNÉRALITÉS 155

7.2 GÉNÉRALITÉS

7.2.1 Types articles contraints

En Ada, un article (record) est formé d’un ou de plusieurs éléments appeléschamps où chacun d’eux peut être de n’importe quel type. Un article doit êtredéclaré dans une partie déclarative en utilisant un type article visible (sect. 4.4),défini préalablement dans une (autre) partie déclarative. Un ou plusieurs champspeuvent comporter une valeur initiale, comme c’est aussi le cas pour les variables(§ 3.9.2).

Exemple 7.1 Types articles contraints et déclarations d’articles.

type T_Jour is range 1..31;

type T_Date is -- Pour traiter des dates

recordJour : T_Jour;Mois : T_Mois_De_L_Annee; -- § 5.2.2Annee : Natural;

end record;

type T_Complexe is -- Pour traiter des nombres complexes

recordPartie_Reelle : Float; -- Champ sans valeur initialePartie_Imaginaire : Float := 0.0; -- Champ avec valeur

end record; -- initiale

Noel : constant T_Date := ...; -- Pour le 25 decembre et pourPaques : constant T_Date := ...; -- Paques; voir § 7.2.2 pour

la-- valeur de ces constantes

Nombre : T_Complexe; -- Pour representer un nombre complexe

-- Pour afficher une dateprocedure Afficher ( Date : in T_Date );

-- Fonction qui calcule et retourne la date du jour suivantfunction Jour_Suivant ( Date : in T_Date ) return T_Date;

Les types articles font partie des types composés (composite types, [ARM 3.2]).Seuls les types articles sans discriminants (contraints) seront exposés dans cechapitre. Les types articles avec discriminants (non contraints) feront l’objetultérieurement d’une présentation particulière (chap. 11).

La forme générale d’un type article sans discriminants est la suivante:type identificateur is

Page 156: Cours Ada de A à Z

GÉNÉRALITÉS 156

recordsuite_de_champs_1 : identificateur_de_type_ou_sous_type:= expression_1;suite_de_champs_2 : identificateur_de_type_ou_sous_type:= expression_2;suite_de_champs_3 : identificateur_de_type_ou_sous_type:= expression_3;...suite_de_champs_N : identificateur_de_type_ou_sous_type:= expression_N;end record;

où• record est un nouveau mot réservé;• identificateur est le nom du type;• suite_de_champs_i est une liste d’identificateurs séparés par des virgules et

désignant les champs du type article;• identificateur_de_type_ou_sous_type donne le type du champ et éven-

tuellement une contrainte;• expression_i est optionnelle et représente la valeur initiale du champ.

7.2.2 Expressions, agrégats et opérations sur les articles

Les expressions d’un type article sont réduites aux constantes, variables etparamètres de ce type. Si les variables et les paramètres n’amènent aucuncommentaire particulier, ce n’est pas le cas des constantes qui nécessitent unevaleur lors de leur déclaration. A cet effet, le langage Ada permet la constructionde valeurs d’un type article, appelées agrégats (aggregates) et composées d’unevaleur par champ de l’article. Les valeurs des champs (fig. 7.1) sont séparées pardes virgules et le tout est mis entre parenthèses; ces valeurs sont simplementénumérées (notation par position), ou écrites en utilisant la notation par nom(l’ordre d’écriture est alors libre), ou en mélangeant ces deux façons de faire encommençant obligatoirement par la notation par position (exemple 7.2). Cesdifférentes possibilités rappellent celles de l’association entre paramètres formelset effectifs lors de l’appel d’un sous-programme (§ 4.3.7).

Page 157: Cours Ada de A à Z

GÉNÉRALITÉS 157

Figure 7.1 Diagramme syntaxique définissant un agrégat d’article.

Exemple 7.2 Agrégats d’articles.

-- Agregats du type T_Date (probablement)( 24, Novembre, 1997 ) -- Agregat par position( 24, Novembre, Annee => 1997 ) -- Deux agregats, par position( 24, Mois => Novembre, Annee => 1997 ) -- puis par nom( Jour => 24, Mois => Novembre, Annee => 1997 )-- Deux agregats ( Mois => Novembre, Jour => 24, Annee => 1997 )-- par nom

-- Agregats du type T_Complexe (probablement)( 0.0, –1.0 )( 0.0, Partie_Imaginaire => –1.0 )( Partie_Reelle => 0.0, Partie_Imaginaire => –1.0 )( Partie_Reelle | Partie_Imaginaire => 0.0 )( others => 0.0 )( 2.0 * X, Partie_Imaginaire => X / 3.0 ) -- On suppose que X

-- est une variable-- de type Float

Le compilateur doit toujours déterminer le type d’un agrégat en considérant lestypes articles visibles là où l’agrégat est présent. Pour éviter des erreurs decompilation, en particulier lorsque others est utilisé, il est toujours possible dequalifier l’agrégat, c’est-à-dire de préciser son type en le préfixant par le nom dutype suivi d’une apostrophe (exemple 7.3), comme pour les expressions qualifiées(sect. 3.10).

(

Agrégat article

expression )

,

|

=>identificateurde champ

others => expression

expression

,

Page 158: Cours Ada de A à Z

GÉNÉRALITÉS 158

Exemple 7.3 Agrégats qualifiés.

-- Agregats du type T_DateT_Date'( 24, Novembre, 1997 )T_Date'( 24, Novembre, Annee => 1997 )T_Date'( Mois => Novembre, Jour => 24, Annee => 1997 )

-- Agregats du type T_ComplexeT_Complexe'( 0.0, –1.0 )T_Complexe'( others => 0.0 )

Une constante d’article se déclare donc comme une constante d’un typescalaire, en utilisant un agrégat comme valeur de la constante. Notons au passageque les agrégats servent également à donner la valeur initiale d’une variable articleou la valeur par défaut d’un paramètre article (exemple 7.4).

Les opérations possibles sur les articles (en plus de l’affectation et du passageen paramètre) sont l’égalité = et l’inégalité /= .

7.2.3 Affectation

L’affectation se fait de manière habituelle, comme dans l’exemple 7.4.

Exemple 7.4 Affectation et passage en paramètre de valeurs d’un type article.

-- ...procedure Exemple_7_4 is

type T_Complexe is -- Pour traiter des nombres complexes

recordPartie_Reelle : Float;Partie_Imaginaire : Float;

end record;

I : constant T_Complexe := (0.0, 1.0); -- Une constante etNombre_1 : T_Complexe; -- deux variables deNombre_2 : T_Complexe := (1.0, 0.0); -- ce type------------------------------------------------------------procedure Exemple (Z : in T_Complexe := (0.0, 0.0) ) is

...end Exemple;

begin -- Exemple_7_4

Nombre_1 := I;Nombre_1 := Nombre_2;Nombre_2 := ( 3.5, 4.8 );

-- Qualification souhaitable mais pas indispensableNombre_2 := T_Complexe'(others => 0.0);

Page 159: Cours Ada de A à Z

GÉNÉRALITÉS 159

-- Qualification possible mais pas indispensableExemple ( T_Complexe'(3.5, 4.8) );Exemple ( (3.5, 4.8) );Exemple ( I );

-- Utilisation de la valeur par defautExemple;...

Il faut insister sur le fait que les champs d’un type article peuvent être den’importe quel type y compris un type article. Mais dans le cas d’un type composépour un champ [ARM 3.2] il faut alors que ce type soit contraint (§ 7.2.1 et 8.2.2)ou un type article à discriminants avec valeurs par défaut (sect. 11.3). Un articlesans discriminants est toujours contraint. Enfin, une fonction peut retourner unarticle comme résultat.

Il existe des attributs applicables à un article ou un type article mais leurutilisation est réservée à des situations particulières. Par contre, il n’y a pas d’en-trées-sorties prédéfinies sur les articles.

Tout agrégat doit être complet, c’est-à-dire qu’il doit contenir exactement uneexpression pour chaque champ. Cette règle est parfaitement naturelle puisqu’unagrégat représente un article avec une valeur pour chacun de ses champs! De plus,un agrégat formé d’une seule expression doit être écrit en utilisant la notation parnom.

Page 160: Cours Ada de A à Z

ACCÈS AUX CHAMPS D’UN ARTICLE 160

7.3 ACCÈS AUX CHAMPS D’UN ARTICLE

Il est très souvent nécessaire d’accéder à un champ particulier d’un article etnon à l’article complet. Pour réaliser cette opération, il s’agit de préfixer le nom duchamp d’un article par le nom de l’article et de séparer les deux identificateurs parun point. Cette notation est identique à celle rencontrée dans d’autres contextes toutà fait différents, comme lorsqu’il fallait utiliser des noms développés pour rendreun objet visible directement (sect. 4.4).

Le résultat de cette construction est un champ du type spécifié à la déclarationdu champ, utilisable de manière absolument identique à une constante ou variablede ce type. Donc partout où une constante ou variable d’un type donné peut êtreutilisée, un champ de ce type peut l’être également.

Exemple 7.5 Accès aux champs d’un article.

with Ada.Float_Text_IO; use Ada.Float_Text_IO;

-- ...procedure Exemple_7_5 is

type T_Complexe is -- Pour traiter des nombres complexes

recordPartie_Reelle : Float;Partie_Imaginaire : Float := 0.0;

end record;

I : constant T_Complexe := (0.0, 1.0); -- Une constante etNombre_1 : T_Complexe; -- deux variables deNombre_2 : T_Complexe; -- ce type

begin -- Exemple_7_5

-- Apres l'affectation Nombre_1 vaudra ( 0.0, 0.0 )Nombre_1.Partie_Reelle := I.Partie_Reelle;

-- Apres l'affectation Nombre_2 vaudra ( 0.0, 1.0 )Nombre_2 := ( Nombre_1.Partie_Reelle, I.Partie_Imaginaire );

Get ( Nombre_1.Partie_Imaginaire );

-- Apres l'affectation Nombre_2 vaudra ( 1.0, 1.0 )Nombre_2.Partie_Reelle := Nombre_1.Partie_Reelle + 1.0;

Put ( Nombre_2.Partie_Reelle );...

Page 161: Cours Ada de A à Z

161

7.4 EXERCICES

7.4.1 Déclaration de types articles contraintsDéclarer des types articles permettant de représenter:

• un vecteur à quatre composantes;• une couleur par trois proportions, chacune d’une des couleurs fondamen-

tales Rouge, Vert et Bleu;• un véhicule par son poids, sa longueur, sa largeur et sa hauteur.

7.4.2 Agrégats articlesEcrire des agrégats pour les types de l’exercice 7.4.1. Le type de certains

d’entre eux est-il toujours défini? Utiliser la qualification si nécessaire.

7.4.3 Calcul du nombre de jours entre deux datesReprendre l’exercice 5.4.3 et modifier la solution de manière à utiliser le type

T_Date (§ 7.2.1) pour implémenter les dates.

7.4.4 Opérations sur les nombres complexes

Ecrire les opérations d’addition, de soustraction, de multiplication et de divi-sion de nombres complexes en utilisant le type T_Complexe (§ 7.2.1).

Page 162: Cours Ada de A à Z

POINTS À RELEVER 162

7.5 POINTS À RELEVER

7.5.1 En général

• Un article regroupe des déclarations de types identiques ou différents.

7.5.2 En Ada

• Un agrégat article est une valeur d’un type article, composée d’une valeurpar champ.

• Un identificateur d’un type ou sous-type article peut servir à qualifierl’agrégat.

• Un agrégat d’une seule valeur doit être écrit avec la notation pas nom.

• L’affectation globale d’articles est autorisée, de même que les opérationsd’égalité et d’inégalité.

• En utilisant la notation de nom développé, un champ d’article s’utilisecomme une variable du même type que le champ.

Page 163: Cours Ada de A à Z

163

C H A P I T R E 8

T A B L E

Page 164: Cours Ada de A à Z

164 TABLEAUX

Page 165: Cours Ada de A à Z

MOTIVATION 165

8.1 MOTIVATION

Dans de nombreuses situations le programmeur ressent le besoin de manipulerdes informations plus complexes que de simples valeurs. Les articles (chap. 7)permettent le regroupement de données de types différents. Mais ceux-ci sont àproscrire si ce regroupement comprend plusieurs dizaines de valeurs de mêmenature. En effet, dans de tels cas, les langages de programmation classiques offrenttous la notion de tableau, particulièrement adaptée à des situations telles que:

• le calcul vectoriel ou matriciel;• l’enregistrement et le traitement de valeurs comme des mesures effectuées

sur des appareillages externes à l’ordinateur;• la définition d’une application (au sens mathématique du terme) d’un

ensemble de valeurs dans un autre;• la gestion de structures simples telles que piles, listes, queues, etc. (chap. 14

et 15);• la gestion de structures complexes comme les tables, les arbres ou encore

les graphes;• la gestion des tampons d’entrées-sorties dans les systèmes d’exploitation.

Comme déjà mentionné, ces données ont la particularité d’être composées deplusieurs valeurs de même nature. En effet, un vecteur est un n-tuple de valeursréelles, les mesures sont souvent de simples nombres réels ou encore un tampond’entrée-sortie peut contenir des dizaines de caractères. Il faut alors comprendreque la constitution d’une telle donnée est complètement définie lorsque:

• le nombre de valeurs composant cette donnée est connu;• le type de ces valeurs est fixé.

Ces caractéristiques conduisent à la réalisation de structures appelées tableaux.

Page 166: Cours Ada de A à Z

GÉNÉRALITÉS 166

8.2 GÉNÉRALITÉS

8.2.1 Définitions

Un tableau (array) est formé d’un ou de plusieurs éléments (components,appelés aussi composantes) tous de même type. Un ou plusieurs indices (indexes)sont associés à un tableau et permettent d’identifier, de distinguer les éléments. Lesindices sont souvent appelés les dimensions (dimensions) du tableau. Finalementon nomme longueur (length) d’une dimension la taille (nombre de valeurs) del’intervalle correspondant.

En Ada, les indices d’un tableau prennent toujours leur valeur dans des inter-valles (§ 6.1.1). Les types tableaux font partie des types composés (compositetypes, [ARM 3.2]). On en distingue deux catégories: les types tableaux contraintset non contraints.

8.2.2 Types tableaux contraints

L’exemple 8.1 présente la déclaration d’un type tableau contraint T_Ligne etd’un tableau Ligne de ce type. Les éléments de Ligne sont indicés de 0 à 79 etcontiendront chacun un caractère. Les tableaux peuvent naturellement être passésen paramètres de sous-programme (procédure Afficher) ou constituer le résultatd’une fonction comme Suite.

Exemple 8.1 Types tableaux contraints et déclarations de tableaux.

-- Type tableau pour traiter des lignes de 80 caracteresMax_Longueur_Ligne : constant := 80,

type T_Ligne is array (0..Max_Longueur_Ligne – 1) of Character;

Ligne : T_Ligne; -- Une ligne (de 80 caracteres)

-- Procedure qui affiche une ligne donnee en parametreprocedure Afficher ( Ligne : in T_Ligne );

-- Fonction qui donne une ligne composee du caractere passe en-- parametre et repete 80 foisfunction Suite ( Caractere : Character ) return T_Ligne;

La forme générale d’un type tableau contraint (constrained) est la suivante:type identificateur is array ( intervalle_1, intervalle_2, ..., intervalle_N )of identificateur_de_type_ou_sous_type;

où• identificateur est le nom du type, array et of de nouveaux mots réservés;• intervalle_i est un intervalle (§ 6.1.1) définissant le type et fixant les bornes

du ième indice;

Page 167: Cours Ada de A à Z

GÉNÉRALITÉS 167

• identificateur_de_type_ou_sous_type donne le type des éléments etéventuellement une contrainte.

Concernant les types des indices et des éléments, il faut savoir que le type desindices doit être discret alors que le type des éléments peut être n’importe lequelsauf un type (ou sous-type) tableau non contraint ou un article à discriminants sansvaleurs par défaut (§ 11.2.1).

8.2.3 Types tableaux non contraints

L’exemple 8.2 présente la déclaration de deux types tableaux non contraintsT_Vecteur et T_Matrice ainsi que trois tableaux Vecteur_2, Vecteur_3 etMatrice. Contrairement au paragraphe précédent, les indices ne sont mentionnésque lors de la déclaration des tableaux, les types tableaux n’indiquant que le type(ou sous-type) de ces indices. Les paramètres de sous-programme ou le résultatd’une fonction peuvent naturellement être d’un type tableau non contraint.

Exemple 8.2 Types tableaux non contraints et déclarations de tableaux.

-- Type tableau pour traiter des vecteurs de n'importe quelle-- taille puisque les indices ne sont pas fixes par le type!type T_Vecteur is array (Integer range <>) of Float;

Vecteur_2 : T_Vecteur (1..2); -- Un vecteur a deux composantesVecteur_3 : T_Vecteur (–1..1); -- Un vecteur a trois composantes

----------------------------------------------------------------- Type tableau pour traiter des matrices de n'importe quelle-- taille!type T_Matrice is array (Integer range <>, Integer range <>)

of Float;

Matrice : T_Matrice (1..2, 1..3); -- Une matrice a deux dimensions

----------------------------------------------------------------- Fonction qui retourne la norme de n’importe quel vecteur de-- type T_Vecteurfunction Norme ( Vecteur : in T_Vecteur ) return Float;

----------------------------------------------------------------- Procedure qui inverse la matrice donnee en parametreprocedure Inverser ( Matrice : in out T_Matrice );

----------------------------------------------------------------- Fonction qui redonne l'inverse de n’importe quelle matrice de-- type T_Matricefunction Inverse ( Matrice : in T_Matrice ) return T_Matrice;

La forme générale d’un type tableau non contraint (unconstrained) est lasuivante:

Page 168: Cours Ada de A à Z

GÉNÉRALITÉS 168

type identificateur is array ( indice_indefini_1, ..., indice_indefini_N, )of identificateur_de_type_ou_sous_type;

où• identificateur est le nom du type, array et of deux mots réservés;• indice_indefini_i est un intervalle particulier de forme générale

identificateur_indice_de_type_ou_sous_type range <>

qui définit uniquement le type et les bornes minimale et maximale del’indice;

• identificateur_de_type_ou_sous_type donne le type des éléments etéventuellement une contrainte.

Comme dans le cas des types tableaux contraints, le type des indices doit êtrediscret alors que le type des éléments peut être n’importe lequel sauf un type (ousous-type) tableau non contraint ou un article à discriminants sans valeurs pardéfaut (§ 11.2.1).

8.2.4 Sous-types tableaux

Il est possible et pratique de déclarer un ou plusieurs sous-types d’un type (debase) tableau non contraint. La forme générale d’une telle déclaration est lasuivante:

subtype identificateur is id_type_tableau (intervalle_1, ..., intervalle_N);

où• identificateur est le nom du sous-type;• id_type_tableau est le nom d’un type tableau non contraint;• intervalle_i est un intervalle (§ 6.1.1) fixant les bornes, et du type du ième

indice.

Exemple 8.3 Types, sous-types et déclarations de tableaux.

-- Type tableau contraint pour traiter des lignes de 80 caracteresMax_Longueur_Ligne : constant := 80;type T_Ligne is array (0..Max_Longueur_Ligne – 1) of Character;

----------------------------------------------------------------- Autre style equivalent mais preferable a la declaration-- precedentesubtype T_Long_Ligne is Integer range 0..Max_Longueur_Ligne – 1;type T_Ligne_Bis is array ( T_Long_Ligne ) of Character;

---------------------------------------------------------------Ligne : T_Ligne; -- Une ligne (de 80 caracteres)Ligne_Bis: T_Ligne_Bis; -- Une autre ligne (de 80

caracteres)

subtype T_Ligne_40 is T_Ligne (0..39); -- INTERDIT car T_Ligne est

Page 169: Cours Ada de A à Z

GÉNÉRALITÉS 169

-- deja contraint!

-------------------------------------------------------------------------------------------------------------------------------- Type tableau non contraint pour traiter des vecteurs de-- n'importe quelle taille!type T_Vecteur is array (Integer range <>) of Float;

-- Sous-types tableau pour traiter des vecteurs a deux composantesNombre_Composantes : constant := 2;

---------------------------------------------------------------subtype T_Vecteur_2 is T_Vecteur (1..Nombre_Composantes);

-- Autre style, preferable, pour la declaration precedentesubtype T_2_Composantes is Integer range 1..Nombre_Composantes;subtype T_Vecteur_2_Bis is T_Vecteur ( T_2_Composantes );

----------------------------------------------------------------- Quatre vecteurs a deux composantes du meme type T_VecteurVecteur_2 : T_Vecteur_2;Vecteur_2_Bis : T_Vecteur_2_Bis;Vecteur_2_Ter : T_Vecteur (0..1);Vecteur_2_Autre : T_Vecteur (T_2_Composantes);-- Fonction qui retourne la norme de vecteurs a deux composantes-- de type T_Vecteurfunction Norme_2_Composantes ( Vecteur : in T_Vecteur_2 )

return Float;

----------------------------------------------------------------- Fonction qui retourne la norme de tout vecteur de type T_Vecteurfunction Norme ( Vecteur : in T_Vecteur ) return Float;

----------------------------------------------------------------- Type tableau non contraint pour traiter des matrices de toute-- taille!type T_Matrice is array (Integer range <>, Integer range <>)

of Float;

-- Sous-types tableau pour traiter des matrices 2 x 3 et 4 x 4subtype T_Matrice_2_3 is T_Matrice (1..2, 1..3);subtype T_Matrice_4_4 is T_Matrice (0..3, 0..3);

----------------------------------------------------------------- Une matrice constante 2 x 3Matrice_2_3 : constant T_Matrice_2_3 := ( (0.0,1.0,2.0),

(3.0,4.0,5.0) );Matrice_4_4 : T_Matrice_4_4; -- Une matrice 4 x 4Matrice_3_2 : T_Matrice (1..3, –1..0); -- Une matrice 3 x 2

----------------------------------------------------------------- Fonction qui redonne l'inverse d'une matrice 2 x 3 de type-- T_Matricefunction Inverse_2_3 ( Matrice : in T_Matrice_2_3 )

return T_Matrice_2_3;

----------------------------------------------------------------- Fonction qui rend l'inverse de toute matrice de type T_Matrice

Page 170: Cours Ada de A à Z

GÉNÉRALITÉS 170

function Inverse ( Matrice : in T_Matrice ) return T_Matrice;

Parmi les différentes possibilités de déclarations de types et sous-types ta-bleaux, certaines d’entre elles offrent une flexibilité et une sécurité maximales lorsde la conception du code Ada (note 8.1).

NOTE 8.1 Style déclaratif des types, sous-types et objets tableaux.

En général, il faut préférer les types tableaux non contraints aux types tableaux contraints demanière à pouvoir créer des paramètres (de procédure, fonction, etc.) tableaux généraux où seulsdes types non contraints sont indiqués, les bornes des paramètres étant déduites des paramètreseffectifs (§ 8.2.5).De même, il est pratique de déclarer des sous-types tableaux contraints pour pouvoir ensuitedéclarer des constantes, variables ou encore agrégats (§ 8.2.5) entièrement définis.

L’application de la note 8.1 est illustrée dans l’exemple 8.3 avec les types noncontraints T_Vecteur et T_Matrice, les sous-types contraints T_Vecteur_2,T_Vecteur_2_Bis, T_Matrice_2_3 et T_Matrice_4_4, la (matrice) constanteMatrice_2_3, les variables Vecteur_2, Vecteur_2_Bis et Matrice_4_4 etenfin les fonctions Norme et Inverse.

Finalement, il ressort de la syntaxe qu’un sous-type tableau est en généralcontraint, c’est-à-dire que sa déclaration comporte la mention explicite des bornesinférieure et supérieure d’un intervalle. Une seule exception à cela: un sous-typetableau peut surnommer (§ 6.1.1) un type non contraint auquel cas il est aussi noncontraint comme dans l’exemple suivant:

subtype T_N_Tuple is T_Vecteur;

8.2.5 Expressions, agrégats et opérations sur les tableaux

Les expressions d’un type tableau quelconque comprennent les constantes,variables, paramètres et agrégats. Dans le cas des tableaux unidimensionnels, ilexiste en plus la notion de tranche de tableau (§ 8.5.1). Comme pour les articles ilfaut insister sur la création d’agrégats qui, ici, sont formés d’une expression parvaleur d’indice (exemple 8.4).

Le diagramme de la figure 8.1 ne l’indique pas mais un agrégat tableau doit êtrenoté entièrement par position, ou entièrement par nom pour un indice donné. Lemélange des notations est ici interdit. Seule exception: others => est autorisé(mentionné à la fin de l’agrégat) si le reste est noté par position.

Page 171: Cours Ada de A à Z

GÉNÉRALITÉS 171

Figure 8.1 Diagramme syntaxique définissant un agrégat de tableau.

Exemple 8.4 Agrégats tableaux.

-- Agregats du type T_Ligne ou T_Ligne_Bis (probablement)(0..Max_Longueur_Ligne-1 => ' ')-- 80 espaces, notation par nom(T_Long_Ligne => ' ') -- 80 espaces, notation par nom

-- Agregats du type T_Vecteur (probablement)(0.0, 0.0, 1.0, 1.0) -- Vecteur a 4 composantes(0.0, 0.0) -- Vecteur a 2 composantes(1..6 => 0.0, 7 | 8 => 1.0, 9 => 1.0) -- Vecteur a 9 composantes

-- Agregats du type T_Matrice (probablement)( (0.0, 0.0), (1.0, 1.0) ) -- Matrice carree 2 x 2( 1 => (0.0, 0.0), 2 => (1.0, 1.0) ) -- Matrice carree 2 x 2( 1..3 => (0.0, 0.0) ) -- Matrice 3 x 2( 1..3 => (1..2 => 0.0) ) -- Matrice 3 x 2

Les agrégats de tableaux peuvent paraître compliqués. Pour que le compilateurpuisse vérifier leur validité et réserver de la mémoire pour les implémenter, il fauten effet toujours que leur longueur et leurs bornes soient bien définies (enparticulier lorsque others est utilisé), ce qu’il faut constamment garder à l’espritpour bien comprendre les explications qui suivent. Un agrégat est toujours contigucar c’est un tableau même si la syntaxe ne le montre pas explicitement!

Comme pour les articles, le compilateur doit toujours connaître le type d’un

(

Agrégat tableau

expression )

,

|

=>valeur d’indice

others

intervalle

=>

expression

expression

,

Page 172: Cours Ada de A à Z

GÉNÉRALITÉS 172

agrégat tableau. Une bonne manière de procéder consiste en la qualification del’agrégat (exemple 8.5) en le préfixant par un identificateur de type ou de sous-typesuivi d’une apostrophe (sect. 3.10). Mais en plus il faut que l’intervalle de valeurs,pour chaque indice, soit défini et corresponde à celui du type de l’agrégat.

Exemple 8.5 Agrégats qualifiés.

-- Agregats du type T_LigneT_Ligne'(0..Max_Longueur_Ligne – 1 => ' ')T_Ligne'(T_Long_Ligne => ' ')

-- Agregats du type T_VecteurT_Vecteur'(0.0, 0.0, 1.0, 1.0) -- Vecteur a 4 composantesT_Vecteur_2'(0.0, 0.0) -- Vecteur a 2 composantesT_Vecteur_2'( others => 0.0) -- Vecteur a 2 composantes

-- Agregats du type T_MatriceT_Matrice'( (0.0, 0.0), (1.0, 1.0) ) -- Matrice carree 2 x 2T_Matrice'( 1 => (0.0, 0.0), -- Matrice carree 2 x 2

2 => (1.0, 1.0) )T_Matrice_2_3'( 1..2 => (0.0, 0.0, 0.0) ) -- Matrice 2 x 3

Les règles définissant la validité ou non d’un agrégat, sa longueur et ses bornesse basent sur l’agrégat lui-même ainsi que sur son contexte, c’est-à-dire la situationoù l’agrégat est utilisé. A des fins de simplicité, seuls des cas simples vont être pré-sentés. Pour plus de détails, la théorie complète peut être consultée dans [BAR 97].

Il faut préciser que lors d’une affectation, du passage en paramètre ou del’attribution de la valeur par défaut, les bornes d’un agrégat n’ont pas besoin d’êtreidentiques à celles de l’objet affecté, il y a conversion automatique des bornes del’agrégat.

Exemple 8.6 Agrégats tableaux, longueurs et bornes.

-- Type tableau pour traiter des vecteurs de n'importe quelle-- tailletype T_Vecteur is array (Integer range <>) of Float;

------------------------------------------------------------------ pour traiter des vecteurs a deux composantesNombre_Composantes : constant := 2;subtype T_Vecteur_2 is T_Vecteur (1..Nombre_Composantes);

------------------------------------------------------------------ fonction qui retourne la norme de vecteurs a deux composantesfunction Norme_2_Composantes (Vecteur : in T_Vecteur_2)

return Float;

----------------------------------------------------------------

Page 173: Cours Ada de A à Z

GÉNÉRALITÉS 173

-- fonction qui retourne la norme de n'importe quel vecteur de type-- T_Vecteurfunction Norme (Vecteur : in T_Vecteur) return Float;

------------------------------------------------------------------ Dans ce qui suit, la longueur et la borne inferieure se-- rapportent a l'agregat------------------------------------------------------------------ Affectation: longueur 2, borne inferieure 1Vecteur_1 : constant T_Vecteur_2 := (1.0, others => 0.0);Vecteur_2 : T_Vecteur_2 := (1.0, 2.0);

------------------------------------------------------------------ Passage en parametre: longueur 2, borne inferieure 1

L1 : Float := Norme_2_Composantes ( (1.0, 2.0) );

------------------------------------------------------------------ Passage en parametre: longueur 3,-- borne inferieure Integer'First

L2 : Float := Norme ( (1.0, 2.0, 3.0) );

------------------------------------------------------------------ Passage en parametre: longueur 3,-- borne inferieure 5

L2 := Norme ( (5 => 1.0, 6 => 2.0, 7 => 3.0) );

-- Qualification: longueur 2, borne inferieure 1Vecteur_3 : T_Vecteur := T_Vecteur_2'(others =>0.0);Vecteur_3 := T_Vecteur_2'(1..2 => 0.0);L3 : Float := Norme ( T_Vecteur_2'(others => 0.0) );

------------------------------------------------------------------ ATTENTION-----------

-- Utilisation d'agregats interdite

Vecteur_4 : T_Vecteur := (others => 0.0); -- Longueur inconnue

L4 : Float := Norme ( (1.0, others => 0.0) ); -- Longueur inconnue

-- Exception Constraint_Error levee

Vecteur_3 := (1..4 => 1.0); -- Longueurs differentes

Vecteur_3 := T_Vecteur_2'(2..3 => 1.0); -- Bornes differentes

L3 := Norme_2_Composantes ( (1..3 => 0.0) ); -- Longueurs differentes

Comme certains agrégats de l’exemple 8.6 l’ont peut-être suggéré, les valeursd’indice ou les intervalles de la notation par nom doivent être statiques, sauf dansun seul cas: celui où l’agrégat est de la forme (intervalle => expression). Ici l’intervallepeut comporter des bornes dynamiques; un intervalle vide (§ 8.2.8), qui définitalors un agrégat ne contenant aucune valeur, est donc possible.

Une constante tableau se déclare donc en utilisant un agrégat comme valeur.Les agrégats servent également à donner la valeur initiale d’une variable tableau

Page 174: Cours Ada de A à Z

GÉNÉRALITÉS 174

ou la valeur par défaut d’un paramètre tableau (exemple 8.6).

Les opérations possibles sur les tableaux (en plus de l’affectation et du passageen paramètre) sont l’égalité = et l’inégalité /=.

Il faut cependant relever que, lors du passage en paramètre (de sous-programme) d’un tableau, le paramètre formel hérite des bornes du paramètreeffectif si le paramètre formel est d’un type (ou sous-type) tableau non contraint;dans le cas contraire, les bornes sont fixées par le type (ou sous-type) contraint lui-même. Comme l’héritage a lieu à l’exécution, la connaissance des bornes duparamètre formel ne peut se faire que par l’utilisation d’attributs comme First etLast (§ 8.2.7).

A noter que les tableaux unidimensionnels comportent des opérations sup-plémentaires (sect. 8.5).

8.2.6 Affectation

L’affectation se fait de manière habituelle mais la longueur de l’expression (iciun tableau), pour chaque indice, doit être identique à celle de la variable sinon l’ex-ception Constraint_Error sera levée (note 8.2). Par contre, les bornes corres-pondantes n’ont pas besoin d’être identiques (§ 8.2.5).

NOTE 8.2 Rôle des longueurs lors de l’affectation de tableaux.

Lors de l’affectation de tableaux, leurs longueurs doivent être identiques faute de quoi l’exceptionConstraint_Error sera levée (§ 6.3.2).

La figure 8.2 illustre l’affectation d’un tableau avec un agrégat, en rappelantque dans ce cas, l’agrégat est en fait un tableau.

Figure 8.2 Affectation d’un tableau avec un agrégat.

8.2.7 Attributs First, Last, Length et Range

Les attributs First, Last, Length et Range sont applicables aux indices d’untableau, ou d’un type ou sous-type tableau contraint. First(N) et Last(N)donnent la première, respectivement la dernière valeur d’indice de la dimension N.Length(N) fournit la longueur (§ 8.2.1) de la dimension N. Finalement, Range(N)

Vecteur_2 := (1.0, 2.0);

tableau Vecteur_2

agrégat1.0 2.0

Page 175: Cours Ada de A à Z

GÉNÉRALITÉS 175

représente l’intervalle lui-même des indices de la dimension N. Il faut noter que Ndoit être statique mais peut être omis. Dans un tel cas d’omission, ces attributss’appliquent à la première dimension. Les types, sous-types, variables et fonctionsutilisés dans l’exemple 8.7 sont déclarés dans les exemples 8.1 et 8.2

Exemple 8.7 Utilisation des attributs First, Last, Length et Range.

T_Ligne’First(1) donne la première valeur d’indice de la première (seule)dimension, 0;T_Ligne’Firstcomme ci-dessus, 0;T_Vecteur_2’Lengthdonne le nombre de valeurs de la première (seule)dimension, 2;T_Vecteur_2’Rangedonne l’intervalle des indices de la première (seule)dimension, 1..2;T_Matrice_2_3’Lastdonne la dernière valeur d’indice de la premièredimension, 2;T_Matrice_2_3’Last(2)donne la dernière valeur d’indice de la secondedimension, 3;Ligne’First donne la première valeur d’indice de la première (seule)dimension, 0;Vecteur_2’Lastdonne la dernière valeur d’indice de la première (seule)dimension, 2;Matrice_2_3’Length(1)donne le nombre de valeurs de la premièredimension, 2;Matrice_2_3’Range(2)donne l’intervalle des indices de la secondedimension, 1..3;Inverse’First(1)donne la première valeur d’indice de la premièredimension, 1.

8.2.8 Compléments sur les tableaux

Il faut insister sur le fait que les éléments d’un tableau peuvent être de n’importequel type y compris un type ou sous-type tableau. Mais dans le cas d’un typecomposé [ARM 3.2], il faut alors qu’il soit un type article sans discriminant (chap.7), un type tableau contraint (§ 8.2.2) ou un type article à discriminants avecvaleurs par défaut (sect. 11.3).

Un tableau doit toujours être contraint lors de son élaboration, c’est-à-dire queles bornes de toutes ses dimensions doivent avoir une valeur définie à sa création.

Il n’y a pas d’entrées-sorties prédéfinies sur les tableaux, à l’exception destableaux du type prédéfini String (§ 9.2.1).

Page 176: Cours Ada de A à Z

GÉNÉRALITÉS 176

Tout agrégat doit être complet, c’est-à-dire qu’il doit contenir exactement uneexpression par valeur d’indice puisque c’est en fait un tableau! De plus, un agrégatformé d’une seule expression doit être écrit en utilisant la notation par nom.

Il est possible de déclarer un tableau sans passer par une déclaration de type.Par exemple:

Tampon : array ( 1..10 ) of Character;

Une telle déclaration devrait se rencontrer uniquement dans des cas particuliers,comme lorsque le tableau est utilisé dans une petite partie de code seulement.

Un intervalle d’indice d’un type tableau peut être vide, c’est-à-dire qu’il n’yaura aucune valeur pour cet intervalle dans n’importe quel tableau de ce type. Unintervalle est vide si sa borne inférieure est plus grande que sa borne supérieure.

Page 177: Cours Ada de A à Z

ACCÈS AUX ÉLÉMENTS D’UN TABLEAU 177

8.3 ACCÈS AUX ÉLÉMENTS D’UN TABLEAU

8.3.1 Accès à un élément particulier d’un tableau

Il est très souvent nécessaire d’accéder à un élément particulier d’un tableau etnon au tableau complet. S’il est vrai qu’un agrégat pourrait parfois résoudre ceproblème, il existe une manière de faire bien plus pratique et plus répandue. Ils’agit de faire suivre le nom du tableau par une valeur pour chaque indice miseentre parenthèses:

identificateur (expression_1, ... , expression_N)

où• identificateur est le nom d’un tableau;• expression_i est la valeur pour l’indice de la ième dimension.

Le résultat de cette construction est un élément du type spécifié à la déclarationdu tableau, utilisable de manière absolument identique à une constante ou variablede ce type (exemple 8.8). Donc partout où une constante ou variable d’un typedonné peut être utilisée, un élément de tableau de ce type peut l’être également.

Exemple 8.8 Accès aux éléments d’un tableau.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_8_8 is

-- Type tableau pour traiter des lignes de 80 caracteresMax_Longueur_Ligne : constant := 80;type T_Ligne is array (0..Max_Longueur_Ligne – 1) of Character;

-- Une ligne de 80 caracteresLigne : T_Ligne := (T_Ligne'Range => ' ');

-- Type tableau pour traiter des matrices de n'importe quelle-- tailletype T_Matrice is array (Integer range <>, Integer range <>)

of Float;

-- Sous-type pour traiter des matrices 2 x 3subtype T_Matrice_2_3 is T_Matrice ( 1..2, 1..3 );

Matrice : T_Matrice_2_3; -- Une matrice 2 x 3

Indice : Natural := 0; -- Deux variables auxiliairesCode : Natural;

begin -- Exemple_8_8

Ligne ( 0 ) := 'A';

-- 'A' est la valeur affecteeLigne ( Max_Longueur_Ligne – 1 ) := Ligne ( T_Ligne'First );Ligne ( 5 * (Indice + 2) ) := 'B';

-- Affiche 'A'

Page 178: Cours Ada de A à Z

ACCÈS AUX ÉLÉMENTS D’UN TABLEAU 178

Put ( Ligne (Indice) );

-- 65 est la valeur affecteeCode := Character'Pos ( Ligne ( T_Ligne'Last ) );

Matrice ( 2, 2 ) := 0.0;Matrice ( 2, 3 ) := Matrice ( 2, 2 ) + 5.0;Matrice ( Matrice'First(1), Matrice'First(2) ) := 1.0;

-- Levera l'exception Constraint_ErrorLigne ( Max_Longueur_Ligne ) := Ligne ( Max_Longueur_Ligne – 1 );...

8.3.2 Remarque importante

Une tentative d’accéder à un élément de tableau inexistant (une valeur d’indicehors de l’intervalle de définition) provoque l’exception Constraint_Error àl’exécution du programme (note 8.3). Par exemple, l’utilisation de l’élémentLigne(expression) (exemple 8.8) générera cette exception si expressionpossède une valeur hors de l’intervalle des indices du tableau Ligne.

NOTE 8.3 Accès à un élément inexistant d’un tableau.

L’accès à un élément inexistant d’un tableau lèvera l’exception Constraint_Error. Comme les accèsà des tableaux sont fréquents dans les programmes, il faut suspecter une telle erreur en casd’apparition de cette exception.

Page 179: Cours Ada de A à Z

CONVERSION ENTRE TYPES TABLEAUX 179

8.4 CONVERSION ENTRE TYPES TABLEAUX

Ada permet de convertir un tableau dans un type cible, autre que celui utilisélors de la déclaration du tableau (exemple 8.9). Cette conversion, qui va laisser telquel le contenu du tableau, permet de passer outre la règle des types (§ 5.1.2)lorsque la situation l’exige. Une conversion n’est cependant pas possible entren’importe quels types tableaux. Il faut que:

• les types aient le même nombre de dimensions;• les types d’indices (discrets) soient identiques ou convertibles (§ 6.2.6);• les types des éléments soient identiques.

Si le type cible est contraint, alors la longueur de chaque dimension du tableaudoit être identique à la longueur de l’intervalle correspondant du type cible et lesbornes du résultat sont celles du type cible (exemple 8.9). Si le type cible est noncontraint, alors chaque intervalle (non nul) d’indice du tableau doit être comprisdans l’intervalle correspondant du type cible et les bornes du résultat sont celles dutableau. Si l’une de ces règles est violée, Constraint_Error sera levée.

Exemple 8.9 Conversion entre types tableaux.

type T_Vecteur is array (Integer range <>) of Float;subtype T_Vecteur_10 is T_Vecteur (1..10);type T_Autre_Vecteur is array (Natural range <>) of Float;

Vecteur : T_Autre_Vecteur (0..100);Vecteur_Test : T_Vecteur (–100..100);...

T_Vecteur ( Vecteur ) bornes de Vecteur, 0 et 100;T_Vecteur ( Vecteur(20..30) ) bornes de la tranche, 20 et 30;T_Vecteur_10 ( Vecteur(20..29) ) bornes de T_Vecteur_10, 1 et 10;T_Autre_Vecteur ( Vecteur_Test ) provoque Constraint_Error à

l’exécution (–100..0 hors de Natural);

T_Vecteur_10 ( Vecteur(20..30) ) provoque Constraint_Error àl’exécution (longueurs différentes).

Page 180: Cours Ada de A à Z

TABLEAUX UNIDIMENSIONNELS 180

8.5 TABLEAUX UNIDIMENSIONNELS

Toutes les notions présentées jusqu’ici s’appliquent naturellement aux tableauxunidimensionnels, c’est-à-dire aux tableaux ne comportant qu’un seul intervalled’indice. Mais des opérations supplémentaires existent pour de tels tableaux.

8.5.1 Tranches de tableaux

Une tranche (slice) de tableau est un tableau partiel défini par un intervallecompris dans l’intervalle de définition des indices du tableau d’origine. Unetranche permet donc de manipuler une partie d’un tableau. Les indices, respec-tivement les éléments de la tranche sont du type des indices, respectivement deséléments du tableau d’origine. Les indices du premier et du dernier élément de latranche forment les bornes du tableau représenté par la tranche. La longueur de latranche est son nombre d’éléments.

Une tranche de tableau se compose du nom du tableau suivi de l’intervalled’indices définissant la tranche, intervalle mis entre parenthèses. Les bornes del’intervalle sont des expressions statiques ou dynamiques du type requis.

Exemple 8.10 Construction et utilisation de tranches de tableaux.

-- On suppose que les types T_Ligne, T_Vecteur et la fonction Norme-- sont declares comme dans les exemples 8.1 et 8.2

Ligne : T_Ligne; -- Une ligne de 80 caracteres

Nombre : constant := 10; -- Une constante et une variableNorme_Vecteur : Float; -- auxiliaires

Vecteur : T_Vecteur ( 1..Nombre ); -- Un vecteur a 10 elements...Ligne (0..9) := (0..9 => '*');Ligne (Ligne'First+1..Nombre) := Ligne (Ligne'First..Nombre–1);

Vecteur (Vecteur'Range) := ( 0.0, 2.0, 2.0, 4.0, 5.0,others => 0.0 );

Vecteur (Nombre–2..Nombre) := Vecteur (Vecteur'First..3);Norme_Vecteur := Norme (Vecteur(Vecteur'First..Vecteur'Last/2));

Comme mentionné précédemment (§ 8.2.6) pour le cas général, l’affectationentre tranches de tableaux d’un même type est possible si la longueur de l’ex-pression est identique à celle de la tranche affectée (note 8.4). Dans ce cas aussi lesbornes correspondantes n’ont pas besoin d’être identiques.

Attention à la fréquente confusion entre Ligne(0) et Ligne(0..0).Ligne(0) est un élément (le premier) du tableau Ligne, donc un caractère, alorsque Ligne(0..0) est une tranche de type T_Ligne, donc un tableau, composéed’un seul élément.

Page 181: Cours Ada de A à Z

TABLEAUX UNIDIMENSIONNELS 181

NOTE 8.4 Affectation de tranches de tableaux.

L’affectation de tranches de tableaux est autorisée pourvu que la variable et l’expression (tableau)soient de même type. Mais leurs longueurs doivent être identiques, faute de quoi l’exceptionConstraint_Error sera levée.

8.5.2 Concaténation

Ada fournit l’opérateur binaire de concaténation (mise bout à bout) de tableaux(ou tranches) unidimensionnels d’un même type. Cet opérateur est noté & etpossède la priorité des opérateurs binaires d’addition et de soustraction (§ 3.4.2).Le résultat est un tableau du même type (exemple 8.11) dont la longueur est lasomme de celle des deux opérandes. La borne inférieure est celle de l’opérande degauche si le type tableau est non contraint, sinon la borne inférieure est celle del’intervalle de définition des indices du type tableau (contraint). La borne supé-rieure se calcule en additionnant les longueurs des opérandes à la borne inférieure,puis en soustrayant 1.

Exemple 8.11 Concaténation de tableaux et de tranches.

-- On suppose que les types T_Ligne et T_Vecteur sont declares-- comme dans les exemples 8.1 et 8.2Ligne : T_Ligne; -- Une ligne de 80 caracteresVecteur : T_Vecteur ( 1..10 ); -- Un vecteur a dix elements

-- Un vecteur a cinq elementsDemi_Vecteur : T_Vecteur ( 1..5 ) := (1..5 => 0.0);...Vecteur := Demi_Vecteur & Demi_Vecteur;Demi_Vecteur := Vecteur (1..2) & (4..6 => 1.0);Vecteur := Demi_Vecteur & Vecteur (1..1) & Demi_Vecteur (1..4);

Ligne (10..15) := Ligne (3..5) & Ligne (23..25);Ligne (0..5) := Ligne (3..5) & Ligne (23..25);

L’un ou les deux opérandes de l’opérateur & peuvent aussi être du type deséléments d’un type tableau (exemple 8.12). Si l’opérande de gauche est une tellevaleur, la borne inférieure du résultat de la concaténation sera toujours celle del’intervalle de définition des indices du type tableau de l’expression ainsi formée.

Exemple 8.12 Concaténation d’éléments de tableaux.

-- T_Ligne et T_Vecteur comme dans les exemples 8.1 et 8.2

Ligne : T_Ligne; -- Une ligne de 80 caracteres

Page 182: Cours Ada de A à Z

TABLEAUX UNIDIMENSIONNELS 182

Vecteur : T_Vecteur ( 1..10 ); -- Un vecteur a dix elements-- Un vecteur a cinq elementsDemi_Vecteur : T_Vecteur ( 1..5 ) := (1..5 => 0.0);...Demi_Vecteur := Demi_Vecteur (1..4) & 1.0;Vecteur := Demi_Vecteur & 1.0 & (4..7 => 0.0);

Ligne (10..15) := '*' & (1..5 => '+');Ligne (0..2) := 'O' & 'U' & 'I';

8.5.3 Comparaison

Les opérateurs d’égalité et d’inégalité sont utilisables avec tous les tableaux.Les autres opérateurs de comparaison <, <=, > et >= peuvent s’appliquer à desopérandes tableaux unidimensionnels dont les éléments sont d’un type discret. Lerésultat de la comparaison se base sur l’ordre lexicographique fondé sur la relationd’ordre du type discret: les éléments sont comparés un à un jusqu’à épuisement del’un des tableaux ou jusqu’à ce que l’une des comparaisons permette de répondreà la question posée (exemple 8.13).

Exemple 8.13 Comparaisons de tableaux.

String'('a','b') < ('a','b','c','d')expression vraie;String'('a','b') > ('a','b','c','d')expression fausse;(1,2) < (1,3,5,7)expression vraie;(2,1) < (1,3,5,7)expression fausse;(2,1) > (1,3,5,7)expression vraie;(2,1) > (2,1)expression fausse;(2,1) >= (2,1)expression vraie.

La qualification par le type prédéfini String (§ 9.2.1) est nécessaire dans cetexemple car il existe un autre type prédéfini Wide_String (non décrit dans cetouvrage) et, sans qualification, le compilateur rejetterait les deux comparaisonspour cause d’ambiguïté.

8.5.4 Opérations booléennes

Les opérateurs logiques (booléens) s’appliquent aux tableaux unidimensionnelsdont le type des éléments est le type Boolean. L’application à un (not) ou à deux(and or xor) opérandes consiste à appliquer l’opérateur élément par élément demanière à construire ainsi le tableau résultat de l’opération (exemple 8.14). Notonsque les deux opérandes tableaux des trois opérateurs binaires doivent donc avoirune longueur identique. De plus, les bornes du tableau résultat sont celles de

Page 183: Cours Ada de A à Z

TABLEAUX UNIDIMENSIONNELS 183

l’unique opérande ou de l’opérande de gauche.

Exemple 8.14 Opérations sur les tableaux de booléens.

not (False, True, False)résultat (True, False, True);(True, True) and (False, True)résultat (False, True);(True, True) or (False, True)résultat (True, True);(True, True) xor (False, True)résultat (True, False).

Page 184: Cours Ada de A à Z

184

8.6 EXERCICES

8.6.1 Déclaration de types tableaux contraints

Déclarer des types tableaux contraints permettant de représenter:

• deux cents nombres réels;• un nombre complexe;• le nombre d’occurrences des lettres (sans distinction de casse) dans un

texte.

8.6.2 Déclaration de types tableaux non contraints

Déclarer des types tableaux non contraints permettant de représenter:

• un ensemble de nombres réels;• un nom de personne.

8.6.3 Agrégats tableaux

Ecrire des agrégats pour les types des exercices 8.6.1 et 8.6.2. Le type decertains d’entre eux est-il toujours défini? Utiliser la qualification si nécessaire.

8.6.4 Tableaux en paramètre

Ecrire un sous-programme qui détermine les valeurs minimale et maximaled’un tableau contenant un ensemble de nombres réels (exercice 8.6.2). Mêmequestion pour un tableau contenant le nombre d’occurrences de lettres (exercice8.6.1)

8.6.5 Opérations sur les vecteurs

Ecrire des sous-programmes réalisant la lecture, l’écriture, la somme et ladifférence de vecteurs du type T_Vecteur (§ 8.2.3).

8.6.6 Opérations sur les matrices carrées

Ecrire des sous-programmes réalisant la lecture, l’écriture, la somme, ladifférence et le produit de matrices carrées du type T_Matrice (§ 8.2.3).

8.6.7 Carrés latins

Ecrire un sous-programme qui vérifie si un carré de n2 cases contenant chacuneun nombre entre 1 et n est latin. Un tel carré est dit latin si sur chaque ligne etchaque colonne, chaque nombre entre 1 et n est présent une et une seule fois.

8.6.8 Triangle de Pascal

Ecrire un programme qui calcule la nième ligne du triangle de Pascal en utilisantle fait que le ième élément de cette ligne se calcule en effectuant la somme des

Page 185: Cours Ada de A à Z

185

éléments i–1 et i de la n–1ième (un seul tableau suffit pour résoudre cet exercice).Les quatre premières lignes sont les suivantes:

11 11 2 11 3 3 1

8.6.9 Calcul du nombre de jours entre deux dates

Reprendre l’exercice 5.4.3 et utiliser un tableau en substitution de l’instructioncase pour obtenir le nombre de jours d’un mois.

Page 186: Cours Ada de A à Z

POINTS À RELEVER 186

8.7 POINTS À RELEVER

8.7.1 En général

• Un tableau est formé d’un ou de plusieurs éléments tous de même type.

• Des indices permettent de numéroter les éléments.

• Par une notation adéquate, un élément de tableau s’utilise comme unevariable du même type que l’élément.

• Eviter à tout prix les débordements (tentatives d’accès) hors des tableaux.

8.7.2 En Ada

• Un type tableau peut être contraint ou non.

• Un tableau est lui toujours contraint.

• On utilise en général des types tableaux non contraints.

• Les sous-types tableaux sont en général contraints.

• Un agrégat tableau est écrit entièrement par nom, ou entièrement parposition.

• Comme pour les articles, tout agrégat tableau doit être complet.

• L’affectation globale de tableaux est autorisée, de même que les opérationsd’égalité et d’inégalité.

• Lors de l’affectation, les longueurs doivent être identiques sinon l’ex-ception Constraint_Error sera levée.

• Des attributs permettent de connaître les caractéristiques des indices.

• Un tableau peut être déclaré sans passer par une déclaration de type.

• Sous certaines conditions, des tableaux d’un type donné peuvent êtreconvertis en des tableaux d’un autre type.

• Les tableaux unidimensionnels représentent un cas particulier souventrencontré dans la pratique.

• Il existe des opérations spéciales dédiées à certains tableaux unidi-mensionnels: tranches de tableaux, concaténation, comparaisons, opéra-tions booléennes.

• Ne pas confondre un élément de tableau et une tranche de tableau delongueur 1.

Page 187: Cours Ada de A à Z

187

C H A P I T R E 9

C H A Î N E S D E

Page 188: Cours Ada de A à Z

188 CHAÎNES DE CARACTÈRES

Page 189: Cours Ada de A à Z

MOTIVATION 189

9.1 MOTIVATION

Le monde est ainsi fait que (presque) toute entité le composant possède un (ouplusieurs) nom(s) permettant de l’identifier, noms parfois complétés par des numé-ros mais il est rare que seul un code numérique soit utilisé. Quel sens aurait en effetla phrase «11 passe à 9 puis à 4» ? Notre imagination nous suggère une action defootball, l’évolution des taux hypothécaires... Cet état de fait implique que touttraitement informatique comporte des manipulations de tels noms appelés chaînesde caractères. Formellement une chaîne de caractères (character string) est unesuite d’un ou de plusieurs caractères accolés, éventuellement aucun (chaîne vide).

Page 190: Cours Ada de A à Z

GÉNÉRALITÉS 190

9.2 GÉNÉRALITÉS

9.2.1 Le type prédéfini String

En Ada, le type prédéfini String permet de définir des chaînes de caractères.En fait, et plus précisément, ce type est le type tableau non contraint suivant:

type String is array ( Positive range <> ) of Character;

Toutes les opérations applicables aux tableaux unidimensionnels (sect. 8.5) sontdonc valables pour le type String (exemple 9.1). Les constantes constituentcependant un cas particulier. Si elles peuvent s’écrire comme des agrégats, il existeune syntaxe plus pratique: l’utilisation sous forme de texte délimité par desguillemets (§ 1.10.3). Mais, contrairement aux agrégats formés de n’importe quelscaractères, ce texte ne peut contenir que des caractères imprimables (§ 1.10.2).

Exemple 9.1 Utilisation de chaînes de caractères.

-- ...procedure Exemple_9_1 is

Bonjour : constant String := "Bonjour";Au_Revoir : constant String :=

('a','u',' ','r','e','v','o','i','r');

Chaine_Vide : constant String := "";Taille_Message : constant := 20;Message : String (1..Taille_Message);

begin -- Exemple_9_1

Message ( Bonjour'Range ) := Bonjour; -- Tranche de tableauMessage ( 9 ) := 'e'; -- Acces aux elements deMessage ( 10 ) := 't'; -- la chaine Message

Message ( 1 .. Bonjour'Length + Au_Revoir'Length ):=Message ( Bonjour'Range ) & Au_Revoir; -- Concatenation

...

9.2.2 Attributs Image et Value

Les attributs définis pour les tableaux (§ 8.2.7) s’appliquent naturellement auxobjets de type String. Mais deux attributs supplémentaires, Image et Value,s’utilisent souvent en relation avec les chaînes de caractères.

L’attribut Image convertit une valeur d’un type scalaire [ARM 3.2] en unechaîne appelée image et formée des caractères représentant, textuellement, lavaleur (la borne inférieure de cette chaîne vaut 1). Plus précisément:

• l’image d’une valeur entière est la suite de caractères correspondante précé-

Page 191: Cours Ada de A à Z

GÉNÉRALITÉS 191

dée d’un signe moins (valeur négative) ou d’un espace (valeur positive);• l’image d’une valeur réelle point-flottant est la suite de caractères

correspondant à la valeur écrite en notation scientifique, précédée d’unsigne moins (valeur négative) ou d’un espace (valeur positive); latransformation est similaire pour une valeur réelle point-fixe;

• l’image d’un identificateur d’un type énumératif est la suite de caractèrescorrespondante, en majuscules;

• l’image d’un caractère imprimable est le caractère lui-même y compris lesdeux apostrophes alors que l’image d’un caractère de contrôle (§ 1.10.2) estun identificateur en majuscules dépendant de l’implémentation.

L’attribut inverse d’Image s’appelle Value. Cet attribut convertit une chaînede caractères constituant textuellement une valeur d’un type scalaire en la valeurcorrespondante de ce type en ignorant d’éventuels espaces suivant ou précédant lavaleur dans la chaîne. Si la transformation n’est pas possible parce que le texte nereprésente pas une valeur valide, l’exception Constraint_Error est générée.

Dans les deux cas (exemple 9.2), le nom de l’attribut est précédé d’unidentificateur de type ou sous-type scalaire définissant le type du paramètre (valeurtransformée).

Exemple 9.2 Utilisation des attributs Image et Value.

Integer'Image( 12 )donne la chaîne " 12";Integer'Image( –12 )donne la chaîne "–12";Float'Image( 12.34 )donne la chaîne " 1.23400E+01";T_Mois_De_L_Annee'Image( Mars )donne la chaîne "MARS" (§ 5.2.2);Character'Image( 'a' )donne la chaîne "'a'";Integer'Value( "12" )donne le nombre entier 12;Integer'Value( " –12 " )donne le nombre entier –12;Float'Value( "1.234E+01" )donne le nombre réel 1.234E+01;T_Mois_De_L_Annee'Value( "MaRs" )donne l’identificateur Mars (§5.2.2);Character'Value( "'a'" )donne le caractère 'a';Natural'Value( "–12" )lèvera Constraint_Error.

9.2.3 Entrées-sorties

L’interaction de l’utilisateur avec le programme nécessite très souvent la saisieet l’affichage de chaînes de caractères. Dans ce but, Ada fournit quatre procéduresprédéfinies dans le paquetage d’entrées-sorties Ada.Text_IO (sect. 19.3). Cesquatre procédures sont:

Page 192: Cours Ada de A à Z

GÉNÉRALITÉS 192

• Put, qui affiche à l’écran une chaîne de caractères (de type String) passéeen paramètre.

• Put_Line, qui affiche à l’écran une chaîne de caractères (de type String)passée en paramètre, puis passe à la ligne suivante.

• Get, qui lit une suite de caractères tapés au clavier et les place dans unevariable de type String passée en paramètre. L’utilisateur doit introduire(au moins) un nombre de caractères correspondant à la longueur duparamètre (§ 8.2.1) car l’appel à Get se termine lorsque le paramètre estcomplètement rempli.

• Get_Line, qui lit une suite de caractères tapés au clavier et les place dansune variable de type String passée comme premier paramètre. L’utili-sateur peut introduire (au moins) un nombre de caractères égal à la longueurdu paramètre (§ 8.2.1) auquel cas l’appel à Get se termine lorsque leparamètre est complètement rempli; ou alors l’utilisateur donne moins decaractères que la longueur du paramètre et termine par une fin de ligne, cequi provoque la fin de l’appel à Get.

Dans les deux cas, un deuxième paramètre de sous-type Natural redonnela position (indice) du dernier caractère tapé par l’utilisateur.

A titre de résumé, voici les en-têtes des procédures telles que définies dansAda.Text_IO:

procedure Put ( Item : in String );procedure Put_Line ( Item : in String );procedure Get ( Item : out String );procedure Get_Line ( Item : out String; Last : out Natural );

Exemple 9.3 Entrées-sorties de chaînes de caractères.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_9_3 is

Bienvenue : constant String :="Bienvenue dans l'exemple 9.3!";

Taille : constant := 10;Chaine : String ( 1..Taille ); -- 10 caracteres au maximum

Nombre_Car_Lus : Natural;

begin -- Exemple_9_3

Put_Line ( Bienvenue );

Put ( "Lecture d'une chaine avec Get (tapez" &Integer'Image(Taille) &" caracteres au moins): " );-- 1

Get ( Chaine ); -- 2

Page 193: Cours Ada de A à Z

GÉNÉRALITÉS 193

Skip_Line; -- 3

Put_Line ( "Voici la chaine que vous avez tapee: " & Chaine );

Put_Line ( "Lecture d'une chaine avec Get_Line" );Put ( " (terminez par une fin de ligne avant" &

Integer'Image(Taille) & " caracteres): ");Get_Line ( Chaine, Nombre_Car_Lus ); -- 4

Put ( "Voici la chaine que vous avez tapee: " );Put_Line ( Chaine(1..Nombre_Car_Lus) ); -- 5...

L’exemple 9.3 nécessite quelques commentaires pour les lignes numérotées de1 à 5. L’appel de Put (ligne 1) illustre une utilisation pratique de l’attribut Image(§ 9.2.2) en conjonction avec l’opérateur de concaténation & (§ 8.5.2). L’utilisationde la procédure Skip_Line (§ 2.6.3) de la ligne 3 est nécessaire pour éviter quel’utilisation d’une fin de ligne après l’introduction de 10 caractères à la ligne 2provoque la lecture d’une chaîne vide à la ligne 4! Enfin la tranche de tableau(sous-chaîne) de la ligne 5 contient les caractères tapés par l’utilisateur à la ligne 4.

NOTE 9.1Longueur des chaînes de caractères lues au clavier.

L’utilisation de chaînes de caractères contenant des éléments non définis ou obsolètes est une erreurfréquente, source de problèmes apparaissant aléatoirement, et souvent difficile à détecter.L’utilisation de tranches de tableau après lecture au clavier et contenant uniquement les caractèrestapés permet d’éviter de tels problèmes à l’exécution du programme.

Page 194: Cours Ada de A à Z

MANIPULATION DE CHAÎNES DE CARACTÈRES 194

9.3 MANIPULATION DE CHAÎNES DE CARACTÈRES

Le travail avec les chaînes de caractères nécessite souvent l’utilisation detranches de tableau et de concaténations mais aussi la recherche ou le décompte demotifs, l’extraction ou la suppression de sous-chaînes, le remplacement de sous-chaînes par d’autres, la substitution systématique d’un caractère par un autre, etc.Un motif (pattern) est la chaîne de caractères particulière qu’il faut rechercher,compter, supprimer, etc., dans un texte.

Les langages de programmation traditionnels offrent en général des outils demanipulation de chaînes de caractères permettant la réalisation d’opérations tellesque celles mentionnées précédemment. Ada ne fait pas exception à cette règle etdéfinit plusieurs paquetages (sect. 10.2) de traitement de chaînes de caractères[ARM A.4]. Le plus simple d’entre eux appelé Ada.Strings.Fixed [ARMA.4.3] permet le travail avec des chaînes de type String. Parmi les opérationsdisponibles dans ce paquetage, mentionnons quelques-unes d’entre elles parmi lesplus classiques:

• Index, fonction qui recherche un motif dans une chaîne et qui retourne laposition du premier caractère du motif dans la chaîne;

• Count, fonction qui compte le nombre d’occurrences d’un motif dans lachaîne;

• Insert, fonction ou procédure qui insère un motif dans une chaîne à partird’une certaine position;

• Delete, fonction ou procédure qui supprime une partie d’une chaîne;• Overwrite, fonction ou procédure qui substitue une partie d’une chaîne

par un motif à partir d’une certaine position.

Exemple 9.4 Utilisation simple de quelques opérations du paquetage Ada.Strings.Fixed.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;with Ada.Strings.Fixed; use Ada.Strings.Fixed;

-- ...procedure Exemple_9_4 is

Texte : String := "Texte extra dans ce contexte";

Ext : constant String := "ext"; -- Deux motifsTex : constant String := "tex";

begin -- Exemple_9_4

Put ( Index (Texte, Ext) ); -- Affiche 2Put ( Index (Texte, Tex) ); -- Affiche 24Put ( Index (Texte, "Tex") ); -- Affiche 1

-- Affiche 3, le nom developpe est indispensable (§ 10.5.2)Put ( Ada.Strings.Fixed.Count (Texte, Ext) );

Page 195: Cours Ada de A à Z

MANIPULATION DE CHAÎNES DE CARACTÈRES 195

Insert (Texte, 12, "bleu ciel"); -- Texte vaut maintenant-- "Texte extrableu ciel dans ce"

Delete (Texte, 12, Texte'Last); -- Texte vaut maintenant-- "Texte extra "

Overwrite (Texte, 12, "-ordinaire");-- Texte vaut maintenant-- "Texte extra-ordinaire "

...

L’exemple 9.4 illustre quelques cas d’utilisation d’opérations disponibles dansle paquetage Ada.Strings.Fixed. La manipulation de chaînes de caractères parles sous-programmes du paquetage Ada.Strings.Fixed est cependant contrô-lée. Certaines opérations ne sont pas autorisées si les chaînes traitées ne les permet-tent pas. Dans de tels cas des exceptions particulières seront levées [ARM A.4.1].

Page 196: Cours Ada de A à Z

196

9.4 EXERCICES

9.4.1 Calcul du nombre de jours entre deux dates

Reprendre l’exercice 8.6.9 et lire les dates sous forme de chaînes de caractères(jour, nom du mois, année) qu’il faut ensuite transformer en type T_Date (§ 7.2.1).

9.4.2 Analyse d’une ligne de texte

Ecrire un programme qui lit une ligne de texte (donnée par l’utilisateur) luesous forme de chaîne de type String et qui affiche tous les mots de la ligne.

9.4.3 Analyse d’un texte

Ecrire un programme qui lit un texte (donné par l’utilisateur ligne après ligne,chacune lue sous forme de chaîne de type String), qui compte les mots de chaqueligne et qui affiche ces valeurs une fois tout le texte lu.

9.4.4 Tri d’un groupe de mots

Ecrire un programme qui lit des mots donnés par l’utilisateur et qui les affichedans l’ordre lexicographique. Un algorithme de tri (pour classer les mots) peut êtreimaginé ou alors repris de la section 18.3 et adapté au problème.

9.4.5 Justification d’une ligne de texte

Ecrire un programme qui lit une ligne de texte (donnée par l’utilisateur) et qui,au choix de l’utilisateur, la justifie à gauche, à droite, ou encore à gauche et à droite.Un seul espace doit séparer les mots pour la justification à gauche ou à droite; lesespaces doivent être répartis équitablement entre les mots pour la justification àgauche et à droite. On fixe arbitrairement à 80 le nombre de positions pour leplacement des mots.

9.4.6 Utilisation du paquetage Ada.Strings.Fixed

Reprendre les exercices 9.4.2 et suivants et les résoudre en utilisant les outilsmis à disposition par le paquetage Ada.Strings.Fixed (sect. 9.3).

9.4.7 Exercice récapitulatif

Ecrire une application qui gère (création, affichage, suppression) des adressestéléphoniques (nom, rue et numéro, ville, numéro de téléphone, adresse email).

Page 197: Cours Ada de A à Z

POINTS À RELEVER 197

9.5 POINTS À RELEVER

9.5.1 En général

• Des outils de manipulation de chaînes de caractères sont très souventprésents dans les langages de programmation.

9.5.2 En Ada

• Le type tableau unidimensionnel prédéfini String permet la manipulationde chaînes de caractères considérées comme des tableaux de caractères.

• Des attributs permettent de transformer une valeur en la chaîne decaractères équivalente et inversement.

• Des entrées-sorties de valeurs de type String sont possibles grâce aupaquetage Ada.Text_IO.

• Les annexes du langage Ada offrent des paquetages de manipulationd’autres chaînes de caractères.

• Attention aux éléments non définis ou obsolètes d’une chaîne de caractères.

Page 198: Cours Ada de A à Z

198

C H A P I T R E 1 0

P A Q U E T AG E S S I M P L E S E T

Page 199: Cours Ada de A à Z

199 PAQUETAGES SIMPLES ET UNITÉS DE COMPILATION

Page 200: Cours Ada de A à Z

MOTIVATION 200

10.1 MOTIVATION

Au fil du développement des applications logicielles, la structuration du codesource a pris de plus en plus d’importance. Les premiers rudiments ont consisté endes instructions équivalentes à la sélection (if, case) ou l’itération (while,loop). Par ailleurs des suites d’instructions ont été réunies en groupes dans desstructures comme les sous-programmes (procedure, function) ou les blocs(begin...end). Ces notions constituaient l’état de l’art lors de l’apparition dulangage Pascal [JEN 78]. Sous la pression du développement de logiciels de plusen plus complexes, des structures plus globales devinrent nécessaires. Il faut citerles modules de Modula-2 [WIR 83], les unités du populaire Turbo-Pascal [BOR 92]et bien entendu les paquetages de Ada. Parallèlement, les classes de Smalltalk[GOL 83] et maintenant de C++ [STR 91] ou Java [JAV 98] permettent de réunirdes attributs (données) et des méthodes (sous-programmes); ces classes constituentégalement une manière de structurer les applications mais la programmation objetne sera pas abordée dans cet ouvrage.

En Ada, les paquetages constituent la pièce maîtresse des (grands) programmeset permettent non seulement de grouper des éléments de programmes (constantes,types, variables, exceptions (sect. 13.2), sous-programmes...) mais encore decontrôler l’accès à ces éléments. Certains d’entre eux s’utiliseront dans et hors dela structure formée par le paquetage, d’autres ne seront visibles qu’à l’intérieur.Finalement, certains types seront opaques (leur structure sera connue dans lepaquetage et invisible à l’extérieur) mais néanmoins utilisables hors du paquetage.Les paquetages simples, par opposition aux paquetages génériques (sect. 17.2) ouaux paquetages avec types privés (sect. 16.2), sont décrits dans ce chapitre.

Page 201: Cours Ada de A à Z

GÉNÉRALITÉS 201

10.2 GÉNÉRALITÉS

En général, un paquetage (package) est constitué de deux parties disjointes etfondamentalement différentes: la spécification et le corps. Dans sa forme la plussimple, la spécification (specification) regroupe des déclarations visibles et utilisa-bles dans et hors du paquetage alors que le corps (body) englobe des élémentsconnus uniquement à l’intérieur. Les déclarations utilisables à l’extérieur du paque-tage sont appelées exportées. Il est important de noter dès maintenant que la duréede vie des variables déclarées dans un paquetage est celle du paquetage lui-même.Pendant l’exécution du programme, ces variables conservent donc leur valeurcourante tant que le paquetage existe. Elles sont parfois appelées variables ré-manentes. La spécification d’un paquetage peut être déclarée dans n’importequelle partie déclarative; son corps doit alors figurer dans la même partiedéclarative, ou, si la partie déclarative fait partie d’une spécification, dans le corpscorrespondant. Enfin, un paquetage peut devenir une unité de bibliothèque (sect.10.6).

Les éléments choisis pour faire partie d’un paquetage doivent toujours tendre àconstituer un tout cohérent, à donner une solution fiable à un problème particulier.A ce titre, et plus que les sous-programmes, les paquetages constituent des briquesélaborées, fondements de toute application d’une certaine envergure.

La spécification fournit la description des outils de résolution que le concepteurd’une application peut utiliser chaque fois qu’il est confronté au problème résolupar le paquetage, alors que le corps implémente ces outils. L’exemple probable-ment le plus cité jusqu’à présent est le paquetage Ada.Text_IO (sect. 19.3) quirésout le problème des entrées-sorties de texte. En particulier, les procédures Putet Get appartiennent à sa spécification et permettent de lire ou d’écrire descaractères ou des chaînes. Leur déclaration et leur mise à disposition suffit au con-cepteur pour savoir comment effectuer ces lectures ou écritures. Il n’a en effet pasbesoin de connaître leur implémentation, cachée dans le corps de Ada.Text_IO.

Figure 10.1Mise à disposition des déclarations de la spécification de Ada.Text_IO.

with Ada.Text_IO;procedure Main is-- Ada.Text_IO, ou-- plus exactement-- sa specification,-- est utilisable-- dans Main...end Main;

Spécification de Ada.Text_IO

Corps de Ada.Text_IO

package Ada.Text_IO is...end Ada.Text_IO;

package body Ada.Text_IO is...end Ada.Text_IO;

Page 202: Cours Ada de A à Z

GÉNÉRALITÉS 202

Pour que le programme principal Main puisse effectuer des entrées-sorties detexte (fig. 10.1), il doit comporter une clause de contexte with Ada.Text_IO quiindique au compilateur que des éléments déclarés dans la spécification deAda.Text_IO vont être utilisés dans ses déclarations et ses instructions (sect.10.5). Par contre, le corps de Ada.Text_IO a automatiquement accès (sans clausewith) aux déclarations de sa spécification. Chaque fois qu’un programmenécessite des entrées-sorties de texte, il pourra utiliser Ada.Text_IO qui n’existequ’en un seul exemplaire. Les paquetages permettent donc la réutilisabilité(reusability) du code.

Parfois, un paquetage ne comprend qu’une spécification. Il s’agit alors defournir des déclarations de constantes, de types, de variables, etc., à l’exclusion destructures nécessitant un corps comme les sous-programmes ou encore lespaquetages. A ce titre, les paquetages Ada.Characters.Latin_1 [ARM A.3.3]et Ada.IO_Exceptions (sect. 12.5) en constituent d’excellents exemples.

Page 203: Cours Ada de A à Z

SPÉCIFICATION DE PAQUETAGE 203

10.3 SPÉCIFICATION DE PAQUETAGE

La structure d’une spécification de paquetage n’introduit pas de réelle nou-veauté dans la syntaxe utilisée. Elle est donnée par le diagramme de la figure 10.2.

Figure 10.2 Syntaxe d’une spécification de paquetage simple.

NOTE 10.1Portée des identificateurs déclarés dans une spécification de paquetage.

La spécification d’un paquetage peut contenir n’importe quelle déclaration sauf des corps. La portée(sect. 4.4) de ces déclarations va non seulement jusqu’à la fin de cette spécification mais encoreenglobe tout le corps et s’étend à toute unité précédée d’une clause de contexte (§ 10.5.1)mentionnant le nom du paquetage.

Lorsque les types privés seront présentés (sect. 16.2), une zone spéciale com-plètera et terminera la partie déclarative. Le nom du paquetage peut être un simpleidentificateur comme Spider (sect. 1.9) ou un nom développé (sect. 4.4) commeAda.Strings.Fixed [ARM A.4.3] ou Ada.Text_IO. La raison nécessitant laprésence de préfixes est en relation avec la notion de paquetage enfant (sect. 10.8).

Exemple 10.1Spécification d’un paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage Nombres_Rationnels is

type T_Rationnel is -- Le type d'un nombre rationnelrecord

Numerateur : Integer;Denominateur : Positive;-- Le signe est au numerateur

end record;

Spécification de paquetage simple

package nom partie déclarativeis end

nom

Nom

identificateur du paquetage

nom du paquetage parent .

;

Page 204: Cours Ada de A à Z

SPÉCIFICATION DE PAQUETAGE 204

Zero : constant T_Rationnel := (0,1); -- Le nombre rationnel 0

-- Addition de deux nombres rationnelsfunction "+" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Soustraction de deux nombres rationnelsfunction "–" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Multiplication de deux nombres rationnelsfunction "*" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Division de deux nombres rationnelsfunction "/" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Puissance d'un nombre rationnelfunction Puissance ( X : T_Rationnel;

Exposant : Natural) return T_Rationnel;

------------------------------------------------------------

end Nombres_Rationnels;

L’exemple 10.1 présente la spécification du paquetageNombres_Rationnels. Elle met à disposition:

• le type T_Rationnel pour la déclaration de nombres rationnels sousforme d’articles composés de deux champs, le numérateur et le déno-minateur du nombre;

• une constante Zero représentant le nombre rationnel 0;

• quatre fonctions-opérateurs permettant des calculs rudimentaires sur lesnombres rationnels;

• une fonction Puissance retournant la puissance d’un nombre rationnel; laraison du choix de l’identificateur Puissance plutôt qu’une surcharge del’opérateur "**" sera donnée plus loin (§ 10.6.2).

Il faut remarquer la présence de la spécification des fonctions et l’absence deleur corps car, comme déjà mentionné, une partie déclarative de paquetage ne peutpas contenir de corps. Il est donc ici indispensable de déclarer les fonctions (et lesprocédures) exportées en deux parties (sect. 4.7).

Page 205: Cours Ada de A à Z

CORPS DE PAQUETAGE 205

10.4 CORPS DE PAQUETAGE

10.4.1 Structure d’un corps de paquetage

La structure d’un corps de paquetage ressemble syntaxiquement à celle d’unespécification. Elle est donnée par le diagramme de la figure 10.3.

Un corps de paquetage, caractérisé par le nouveau mot réservé body, a pour butprincipal de fournir tous les corps des procédures, fonctions, etc., mentionnéesdans sa spécification. Pour ce faire et si nécessaire, il est possible de déclarern’importe quel élément dans sa partie déclarative, élément dont la portée (sect. 4.4)va de sa déclaration jusqu’au end final du corps. Finalement, un corps depaquetage peut utiliser toutes les déclarations de sa spécification.

Figure 10.3 Syntaxe d’un corps de paquetage.

Exemple 10.2 Corps du paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage body Nombres_Rationnels is

-------------------------------------------------------------- Addition de deux nombres rationnelsfunction "+" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "+"

return ( X.Numerateur * Y.Denominateur +Y.Numerateur*X.Denominateur,X.Denominateur * Y.Denominateur );

end "+";

-------------------------------------------------------------- Soustraction de deux nombres rationnels

Corps de paquetage

package nom partie déclarativeis

end

nom

;

body

begin suite d’instructions

Page 206: Cours Ada de A à Z

CORPS DE PAQUETAGE 206

function "–" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "–"

return ( X.Numerateur*Y.Denominateur –Y.Numerateur*X.Denominateur,X.Denominateur * Y.Denominateur );

end "–";

-- Multiplication de deux nombres rationnelsfunction "*" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "*"

return ( X.Numerateur * Y.Numerateur,X.Denominateur * Y.Denominateur );

end "*";

-------------------------------------------------------------- Division de deux nombres rationnelsfunction "/" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "/"

if Y.Numerateur > 0 then -- Diviseur positif

return ( X.Numerateur * Y.Denominateur,X.Denominateur * Y.Numerateur );

elsif Y.Numerateur < 0 then -- Changer de signe

return ( – X.Numerateur * Y.Denominateur,X.Denominateur * abs Y.Numerateur);

else -- Division par zero!

... -- Lever une exception (sect. 13.3)

end if;

end "/";

-------------------------------------------------------------- Puissance d'un nombre rationnelfunction Puissance ( X : T_Rationnel;

Exposant : Natural) return T_Rationnel is

begin -- Puissance

return ( X.Numerateur ** Exposant,X.Denominateur ** Exposant );

end Puissance;------------------------------------------------------------

end Nombres_Rationnels;

Le corps du paquetage Nombres_Rationnels (exemple 10.2) réalise les corpsdes quatre opérateurs arithmétiques de base sur des fractions et celui de la fonctionPuissance, conformément aux déclarations de sa spécification. Aucune autredéclaration n’est nécessaire ou utile dans ce corps.

Page 207: Cours Ada de A à Z

CORPS DE PAQUETAGE 207

Il faut cependant souligner que le paquetage Nombres_Rationnels (exem-ples 10.1 et 10.2), qui est un paquetage Ada parfaitement correct, est pour l’instantuniquement illustratif. En effet, le calcul avec des expressions formées de nombresrationnels et d’opérations mises à disposition par ce paquetage ne serait pas tou-jours exact. Par exemple, l’opérateur prédéfini "=" entre deux nombres rationnelségaux ne fournit pas toujours le résultat correct (cas des nombres rationnels nonirréductibles).

10.4.2 Code d’initialisation d’un paquetage

Le corps d’un paquetage peut se terminer par une suite d’instructions situéeavant le end final et précédée du mot réservé begin placé après la dernièredéclaration du corps. Il s’agit du code d’initialisation qui est exécuté avant toutappel aux éléments exportés du paquetage. Ce code est optionnel et permet, entreautres, d’initialiser des variables du paquetage si elles ne peuvent l’être à ladéclaration, par exemple parce que leur valeur initiale n’est connue qu’aprèsl’exécution d’un sous-programme.

Page 208: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE 208

10.5 UTILISATION D’UN PAQUETAGE

10.5.1 Clauses de contexte

N’importe quel programme principal, et plus généralement toute unité debibliothèque (§ 10.6.1) nécessitant l’utilisation d’un paquetage (ou d’une autreunité de bibliothèque) doit comporter une clause de contexte commençant par lemot réservé with suivi du nom du paquetage. Cette clause sert à indiquer aucompilateur que le programme principal ou l’unité de bibliothèque va utiliser desdéclarations exportées (sect. 10.2) du paquetage.

Lorsque plusieurs paquetages doivent être indiqués, il faut les mentionner dansune ou plusieurs clauses de contexte, en séparant les noms par une virgule s’il y alieu (fig. 10.4).

Figure 10.4 Syntaxe d’une clause de contexte.

Dans le cas d’un paquetage enfant (sect. 10.8), le nom doit comporter tous lespréfixes nécessaires, comme dans with Ada.Text_IO, Ada.Strings.Fixed;par exemple.

Lorsqu’une clause de contexte mentionne un paquetage, tous les identificateursdéclarés dans sa spécification deviennent dès lors utilisables dans le programmeprincipal ou l’unité de bibliothèque sous leur forme développée (sect. 4.4), c’est-à-dire préfixés par le nom du paquetage (exemple 10.3).

Exemple 10.3 Utilisation du paquetage Nombres_Rationnels.

with Nombres_Rationnels;-- ...procedure Exemple_10_3 is

-- Une constante rationnelleUne_Demi : constant Nombres_Rationnels.T_Rationnel := (1, 2);

Nombre_1 : Nombres_Rationnels.T_Rationnel;-- Deux variablesNombre_2 : Nombres_Rationnels.T_Rationnel;

Clause de contexte

with nom

,

;

Page 209: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE 209

begin -- Exemple_10_3

-- AffectationsNombre_1 := Une_Demi;Nombre_2 := Nombres_Rationnels.Zero;

-- Addition de deux nombres rationnelsNombre_2 := Nombres_Rationnels."+" ( Nombre_1, (3, 12) );

-- Division de deux nombres rationnelsNombre_1 := Nombres_Rationnels."/" ( Nombre_1, Nombre_2 );

-- Puissance d’un nombre rationnelNombre_2 := Nombres_Rationnels.Puissance ( Nombre_2, 2 );

-- L'acces aux champs (comme d'habitude) reste bien entendu -- possibleNombre_2.Numerateur := 5;...

Une clause de contexte placée avant une spécification de paquetage s’appliqueaussi bien à cette spécification qu’au corps de ce paquetage. Par contre, une telleclause placée avant le corps n’est valable que pour ce corps.

10.5.2 Clauses use

Lors de l’utilisation des déclarations exportées, il peut être fastidieux de répétersystématiquement le nom du paquetage les définissant; de plus, l’appel defonctions-opérateurs exportées devrait s’effectuer sous la forme fonctionnelle(sect. 4.9).

Comme déjà mentionné (§ 2.6.9), il existe une clause permettant d’éviter depréfixer par le nom du paquetage et d’utiliser les fonctions-opérateurs comme desopérateurs habituels. Cette clause a la même syntaxe qu’une clause de contexte, ensubstituant à with le mot réservé use. Elle doit se trouver après la clause decontexte mentionnant le même paquetage, avant le programme principal ou l’unitéde bibliothèque, ou dans une partie déclarative. Dès sa mention, les préfixes ne sontplus nécessaires (mais toujours autorisés), les identificateurs exportés sont alorsrendus directement visibles (sect. 4.4). L’exemple 10.4 reprend l’exemple 10.3sans la mention des préfixes, rendus inutiles par la présence d’une clause use.

Exemple 10.4 Utilisation du paquetage Nombres_Rationnels et d’une clause use.

with Nombres_Rationnels;use Nombres_Rationnels;

-- ...procedure Exemple_10_4 is

-- La clause use permet l'utilisation directe, sans prefixe,-- des declarations de la specification du paquetage

Page 210: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE 210

-- Nombres_Rationnels

Une_Demi : constant T_Rationnel := (1, 2);Nombre_1 : T_Rationnel;

-- Le prefixe est cependant toujours possibleNombre_2 : Nombres_Rationnels.T_Rationnel;

begin -- Exemple_10_4

-- AffectationsNombre_1 := Une_Demi;Nombre_2 := Zero;

-- Addition de deux nombres rationnelsNombre_2 := Nombre_1 + (3, 12);

-- Division de deux nombres rationnelsNombre_1 := Nombre_1 / Nombre_2;

-- Puissance d’un nombre rationnelNombre_2 := Puissance ( Nombre_2, 2 );

-- L'acces aux champs (comme d'habitude) reste possibleNombre_2.Numerateur := 5;...

Une question surgit lorsque plusieurs paquetages sont utilisés et que lepréfixage est rendu inutile via une ou plusieurs clauses use. Que se passe-t-il si unidentificateur exporté par deux paquetages (au moins) est écrit sans préfixe? Plusgénéralement, que devient la visibilité des identificateurs en présence de clausesuse? Deux règles précisent la situation [BAR 97]:

• un identificateur déclaré dans une spécification de paquetage est rendudirectement visible (sect. 4.4) par une clause use pourvu que, d’une part,le même identificateur ne figure pas dans un autre paquetage mentionnéavec une clause use, et que, d’autre part, cet identificateur ne soit pas déjàdirectement visible; sinon le préfixage est indispensable (exemple 10.5);

• si tous les identificateurs en présence sont des sous-programmes ou desvaleurs énumérées (§ 5.2.2), alors ils se surchargent (exemple 10.5) etdeviennent tous directement visibles; il est donc possible que des cas d’am-biguïté surviennent, qu’il faudra éliminer par exemple en préfixant chaqueidentificateur ambigu par un nom de paquetage.

Exemple 10.5 Visibilité et clauses use.

with Nombres_Rationnels;use Nombres_Rationnels;

-- ...procedure Exemple_10_5 is

-- La declaration de la constante Zero ci-dessous cache (sect.

Page 211: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE 211

-- 4.4) celle de la specification de Nombres_Rationnels

Zero : constant Natural := 0;

-- Il faut donc utiliser le nom developpe pour acceder a la-- constante Zero du paquetage

Nombre_1 : T_Rationnel := Nombres_Rationnels.Zero;

-------------------------------------------------------------- Surcharge de la fonction-operateur de multiplication du-- paquetage Nombres_Rationnels

function "*" ( X : Integer;Y : T_Rationnel)return T_Rationnel is ... end "*";

------------------------------------------------------------

begin -- Exemple_10_5

-- Zero est la constante declaree ci-dessus, la premiere -- multiplication l'est aussi, la seconde provient du paquetage-- Nombres_RationnelsNombre_1 := Zero * (Nombres_Rationnels.Zero * Nombre_1);...

10.5.3 Clauses use type

L’utilisation systématique de clauses use aboutit à des codes sources contenanttrès peu de noms développés; cela constitue une avantage appréciable pour leprogrammeur qui n’a ainsi pas besoin de préfixer un grand nombre d’iden-tificateurs. Par contre, la compréhension de tels codes source peut rapidementdevenir difficile par le fait que la provenance des identificateurs est rendue obscurepar l’absence de tout préfixe.

A contrario, renoncer à l’utilisation des clauses use conduit parfois à unediminution de la lisibilité, voire à une certaine lourdeur du code du fait de laprésence de tous les préfixes nécessaires à la visibilité des identificateurs ou desfonctions-opérateurs. En particulier, ces dernières doivent être utilisées sous leurforme fonctionnelle et non simplement et facilement comme des opérateurs.Heureusement, ce dernier inconvénient peut être facilement supprimé par l’uti-lisation d’une ou plusieurs clauses use type tout en évitant un usage immodérédes clauses use.

Page 212: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE 212

Figure 10.5 Syntaxe d’une clause use type.

L’utilisation d’une clause use type pour un type T rend directement visiblesles fonctions-opérateurs déclarées dans la même spécification que T, sous réservede la première règle applicable aux clauses use (§ 10.5.2) valable également pourles clauses use type. Par contre, le préfixage reste toujours indispensable pourtous les identificateurs exportés (exemple 10.6).

Exemple 10.6 Utilisation du paquetage Nombres_Rationnels et d’une clause use type.

with Nombres_Rationnels;

use type Nombres_Rationnels.T_Rationnel;

-- La clause use type permet l'utilisation directe, sans prefixe,-- des fonctions-operateurs associees au type T_Rationnel

-- ...procedure Exemple_10_6 is

-- Trois declarationsUne_Demi : constant Nombres_Rationnels.T_Rationnel := (1, 2);

Nombre_1 : Nombres_Rationnels.T_Rationnel;Nombre_2 : Nombres_Rationnels.T_Rationnel;

begin -- Exemple_10_6

-- AffectationsNombre_1 := Une_Demi;Nombre_2 := Nombres_Rationnels.Zero;

-- Addition de deux nombres rationnelsNombre_2 := Nombre_1 + (3, 12);

-- Division de deux nombres rationnels

Nombre_1 := Nombre_1 / Nombre_2;

-- Puissance d’un nombre rationnelNombre_2 := Nombres_Rationnels.Puissance ( Nombre_2, 2 );...

Clause use type

use nom de type

,

;type

Page 213: Cours Ada de A à Z

UNITÉS DE COMPILATION ET UNITÉS DE BIBLIOTHÈQUE 213

10.6 UNITÉS DE COMPILATION ET UNITÉS DE BIBLIOTHÈQUE

10.6.1 Généralités

Comme déjà relevé, les paquetages forment les briques de base des (grands)programmes Ada. D’un point de vue pratique, comment de tels programmes sont-ils compilés? La question n’est pas dénuée d’intérêt lorsque leur taille atteintplusieurs dizaines ou centaines de milliers de lignes en code source! Leurcompilation en un seul bloc nécessiterait plusieurs heures, même avec la rapiditédes processeurs actuels. Il faut donc absolument disposer de facilités decompilation séparée (separate compilation) dans le but de soumettre au compi-lateur des parties de code compilables pour elles-mêmes appelées unités de com-pilation (compilation units). Les programmes principaux, les spécifications despaquetages et les corps des paquetages constituent des unités de compilation demême que les sous-programmes dans certains cas particuliers.

L’ensemble des unités compilées pour elles-mêmes forment une bibliothèque(library) et sont alors appelées unités de bibliothèque (library units). Dans lapratique, la bibliothèque peut être constituée simplement par un répertoire parti-culier du système d’exploitation regroupant les unités de bibliothèque, ou encoregrâce à un fichier spécial souvent appelé projet et contenant entre autres des liensvers lesdites unités de bibliothèque.

Toute unité de compilation peut être précédée d’une ou plusieurs clauses decontexte afin de permettre sa compilation propre. Une telle clause présente avantla spécification d’un paquetage est valable également pour son corps, mais il estpossible et conseillé de la placer avant le corps uniquement, si elle n’est pas requisedans la spécification, ceci afin de respecter le principe de localité des déclarations(sect. 4.5). Une clause de contexte va donc servir à mentionner toutes les unités debibliothèque utilisées dans l’unité de compilation qui suit.

L’ordre de compilation des constituants d’un programme doit respecter le faitqu’une unité de bibliothèque ne peut pas être compilée tant que toutes les unitésénumérées dans la ou les clauses de contexte de l’unité ne sont pas présentes dansla bibliothèque.

10.6.2 Sous-unités

Lorsque les unités de compilation atteignent une taille importante, il existe unepossibilité supplémentaire de compilation séparée en procédant à l’extraction descorps présents dans l’unité de compilation afin de les compiler pour eux-mêmes. Ala place des corps, ou plus exactement à la place de tout le code commençant paris et se terminant au end final, il faut indiquer au compilateur, par le mot réservéseparate, que ce code va se compiler séparément (exemple 10.7). Cettedéclaration particulière s’appelle corps souche (body stub).

Page 214: Cours Ada de A à Z

UNITÉS DE COMPILATION ET UNITÉS DE BIBLIOTHÈQUE 214

Exemple 10.7 Corps souche Puissance dans le corps du paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage body Nombres_Rationnels is

----------------------------------------------------------------- Addition de deux nombres rationnelsfunction "+" (X, Y : T_Rationnel )

return T_Rationnel is ... end "+";

----------------------------------------------------------------- Soustraction de deux nombres rationnelsfunction "–" (X, Y : T_Rationnel )

return T_Rationnel is ... end "–";

----------------------------------------------------------------- Multiplication de deux nombres rationnelsfunction "*" (X, Y : T_Rationnel )

return T_Rationnel is ... end "*";

----------------------------------------------------------------- Division de deux nombres rationnelsfunction "/" (X, Y : T_Rationnel )

return T_Rationnel is ... end "/";

----------------------------------------------------------------- Puissance d'un nombre rationnel sous forme de corps souchefunction Puissance ( X : T_Rationnel;

Exposant:Natural)return T_Rationnel is separate;

---------------------------------------------------------------

end Nombres_Rationnels;

Le corps extrait est nommé sous-unité (subunit). Il se présente sous la forme ducorps complet, précédé d’une clause separate indiquant de quelle unité decompilation, appelée unité parent (parent unit), provient ce corps. Cette sous-unitéreprésente également une unité de compilation. Elle va ainsi pouvoir être compiléepour elle-même (c’est l’objectif visé) et devenir une unité de bibliothèque.

Figure 10.6 Syntaxe d’une clause separate.

Clause separate

separate nom de l’unité parent( )

Page 215: Cours Ada de A à Z

UNITÉS DE COMPILATION ET UNITÉS DE BIBLIOTHÈQUE 215

Exemple 10.8 Sous-unité Puissance de l’unité parent Nombres_Rationnels.

separate (Nombres_Rationnels) -- Pas de point-virgule ici!

function Puissance ( X : T_Rationnel;Exposant : Natural) return

T_Rationnel isbegin -- Puissance

return ( X.Numerateur ** Exposant,X.Denominateur ** Exposant );

end Puissance;

La fonction Puissance est la seule qui peut devenir une sous-unité (exemple10.8) dans le corps du paquetage Nombres_Rationnels. En effet, les fonctions-opérateurs ne peuvent pas être des unités de bibliothèque.

NOTE 10.2 Visibilité des identificateurs dans une sous-unité.

Les identificateurs visibles dans une sous-unité sont ceux qui sont visibles là où le corps souche estplacé, et ceux rendus visibles par les clauses de contexte mentionnées pour la sous-unité.

La note 10.2 précise les règles de visibilité applicables à une sous-unité. Enfin,si une sous-unité nécessite une ou plusieurs clauses de contexte, celles-ci se placentavant la clause separate.

Page 216: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 216

10.7 CONCEPTION D’UN PAQUETAGE

La conception et la réalisation d’un bon paquetage pose plus de difficultés queles exemples simples présentés. Le risque de créer une structure fourre-tout quioffre tout et «n’importe quoi» est réel, en particulier pour le concepteur débutant.L’ébauche du paquetage Nombre_Rationnels (exemples 10.1 et 10.2) va êtrecomplétée pour illustrer les notions conduisant à la création d’un paquetagecohérent et fournissant les outils de base pour le calcul avec les nombres rationnels.

Un paquetage bien conçu doit satisfaire les critères généraux suivants:

• il doit fournir une solution d’un problème bien défini et, si possible, defaible complexité;

• il doit constituer une brique réutilisable, voire facilement extensible;• la spécification doit être cohérente, former un tout;• la spécification doit être simple, et faciliter la tâche de l’utilisateur du

paquetage et non celle de son concepteur;• le corps doit réaliser complètement et précisément la spécification.

Le fait de limiter le paquetage au calcul avec des nombres rationnels respecteles deux premiers critères. La spécification de l’exemple 10.9 répond aux deuxsuivants. Mais il faut attendre le corps pour voir si le dernier critère est satisfait.

Exemple 10.9 Spécification cohérente du paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnels. Tous-- les nombres traites sont irreductiblespackage Nombres_Rationnels is

type T_Rationnel is -- Le type d'un nombre rationnelrecord

Numerateur : Integer;Denominateur : Positive;-- Le signe est au numerateur

end record;

-- Le nombre rationnel 0Zero : constant T_Rationnel := (0, 1);-------------------------------------------------------------- Construction d'un nombre rationnelfunction "/" ( Numerateur : Integer;

Denominateur : Positive ) return T_Rationnel;

-------------------------------------------------------------- Addition de deux nombres rationnelsfunction "+" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Soustraction de deux nombres rationnelsfunction "–" ( X, Y : T_Rationnel ) return T_Rationnel;

------------------------------------------------------------

Page 217: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 217

-- Multiplication de deux nombres rationnelsfunction "*" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Division de deux nombres rationnelsfunction "/" ( X, Y : T_Rationnel ) return T_Rationnel;

-------------------------------------------------------------- Puissance d'un nombre rationnelfunction "**" ( X : T_Rationnel;

Exposant : Natural) return T_Rationnel;

-------------------------------------------------------------- Comparaisons de deux nombres rationnelsfunction "=" ( X, Y : T_Rationnel ) return Boolean;function "<" ( X, Y : T_Rationnel ) return Boolean;function "<=" ( X, Y : T_Rationnel ) return Boolean;function ">" ( X, Y : T_Rationnel ) return Boolean;function ">=" ( X, Y : T_Rationnel ) return Boolean;------------------------------------------------------------

end Nombres_Rationnels;

Cette spécification est cohérente parce qu’elle ne traite que des nombres ration-nels, parce qu’un seul type permet de déclarer de tels nombres et que les fonctionsfournies constituent la base indispensable pour le calcul avec eux. Elle est simpleet facilite son utilisation par le choix uniforme des en-têtes des opérations. Ladéclaration systématique de fonctions-opérateurs permet, en introduisant uneclause use type (§ 10.5.3), l’écriture de nombres et d’expressions sous la formefractionnaire habituelle.

L’exemple 10.10 présente le corps du paquetage Nombres_Rationnels. Ilimplémente les corps de toutes les opérations appartenant à la spécification ainsique trois fonctions nécessaires à ces opérations, les fonctions P_P_M_C, P_G_C_Det Irreductible. Ces dernières sont invisibles à l’extérieur du paquetage puis-qu’elles sont déclarées uniquement dans le corps.

Exemple 10.10 Corps complet du paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnels. Tous-- les nombres traites sont irreductibles. A relever la presence-- des trois fonctions locales P_P_M_C, P_G_C_D et Irreductible.

package body Nombres_Rationnels is

-------------------------------------------------------------- Construction d'un nombre rationnelfunction "/" ( Numerateur : Integer;

Denominateur : Positive ) return T_Rationnel is

Page 218: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 218

begin -- "/"

return ( Numerateur, Denominateur );

end "/";

-------------------------------------------------------------- Pour l'addition et la soustraction afin de rendre les-- numerateur et denominateur les plus petits possiblesfunction P_P_M_C ( X, Y : Positive ) return Positive is

Multiple_X : Positive := X; -- Pour les multiplesMultiple_Y : Positive := Y;

begin -- P_P_M_C

while Multiple_X /= Multiple_Y loop -- PPMC trouve?

if Multiple_X < Multiple_Y then

Multiple_X := Multiple_X + X; -- Multiple suivant

else

Multiple_Y := Multiple_Y + Y; -- Multiple suivant

end if;

end loop;

return Multiple_X; -- C'est le PPMC

end P_P_M_C;

-- Addition de deux nombres rationnelsfunction "+" ( X, Y : T_Rationnel ) return T_Rationnel is

Le_P_P_M_C : Positive := P_P_M_C ( X.Denominateur,Y.Denominateur );

begin -- "+"

return ( X.Numerateur * ( Le_P_P_M_C/X.Denominateur ) +Y.Numerateur * ( Le_P_P_M_C/Y.Denominateur ),Le_P_P_M_C );

end "+";

-------------------------------------------------------------- Soustraction de deux nombres rationnelsfunction "–" ( X, Y : T_Rationnel ) return T_Rationnel is

Le_P_P_M_C : Positive := P_P_M_C ( X.Denominateur,Y.Denominateur );

begin -- "–"

return ( X.Numerateur * ( Le_P_P_M_C/X.Denominateur ) –Y.Numerateur * ( Le_P_P_M_C/Y.Denominateur ),Le_P_P_M_C );

end "–";

-------------------------------------------------------------- Pour la reduction en un nombre rationnel irreductible par-- la fonction Irreductiblefunction P_G_C_D ( X, Y : Positive ) return Positive is

Diviseur_X : Positive := X; -- Pour les soustractions

Page 219: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 219

Diviseur_Y : Positive := Y;

begin -- P_G_C_D

while Diviseur_X /= Diviseur_Y loop -- PGCD trouve?

if Diviseur_X > Diviseur_Y then

Diviseur_X := Diviseur_X – Diviseur_Y;

else

Diviseur_Y := Diviseur_Y – Diviseur_X;

end if;

end loop;

return Diviseur_X; -- C'est le PGCD

end P_G_C_D;

-------------------------------------------------------------- Rendre un nombre rationnel irreductible apres-- multiplication et division de deux nombres rationnelsfunction Irreductible ( X : T_Rationnel) return T_Rationnel is

Le_P_G_C_D : Positive;

begin -- Irreductible

if X.Numerateur = 0 then

return (0, 1);

else

Le_P_G_C_D := P_G_C_D (abs X.Numerateur, X.Denominateur);

return (X.Numerateur / Le_P_G_C_D,X.Denominateur / Le_P_G_C_D );

end if;

end Irreductible;

-------------------------------------------------------------- Multiplication de deux nombres rationnels. Le resultat est-- un nombre rationnel irreductiblefunction "*" ( X, Y : T_Rationnel ) return T_Rationnel is

begin -- "*"

return Irreductible (( X.Numerateur * Y.Numerateur,X.Denominateur * Y.Denominateur ));

end "*";

-------------------------------------------------------------- Division de deux nombres rationnels. Le resultat est un-- nombre rationnel irreductiblefunction "/" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "/"

if Y.Numerateur > 0 then -- Diviseur positif

return Irreductible(( X.Numerateur * Y.Denominateur,X.Denominateur * Y.Numerateur ));

elsif Y.Numerateur < 0 then -- Changer de signe

return Irreductible((– X.Numerateur * Y.Denominateur,

Page 220: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 220

X.Denominateur * abs Y.Numerateur));

else -- Division par zero!

... -- Lever une exception (sect. 13.3)

end if;

end "/";

-------------------------------------------------------------- Puissance d'un nombre rationnelfunction "**" (X : T_Rationnel; Exposant : Natural)

return T_Rationnel isbegin -- "**"

return ( X.Numerateur ** Exposant,X.Denominateur ** Exposant );

end "**";

-------------------------------------------------------------- Comparaisons entre deux nombres rationnels: egalitefunction "=" ( X, Y : T_Rationnel ) return Boolean isbegin -- "="

return X.Numerateur * Y.Denominateur =X.Denominateur * Y.Numerateur;

end "=";

-- Comparaisons entre deux nombres rationnels: inferieurfunction "<" ( X, Y : T_Rationnel ) return Boolean isbegin -- "<"

return X.Numerateur * Y.Denominateur <X.Denominateur * Y.Numerateur;

end "<";

-------------------------------------------------------------- Comparaisons entre deux nombres rationnels: inferieur ou-- egalfunction "<=" ( X, Y : T_Rationnel ) return Boolean isbegin -- "<="

return X.Numerateur * Y.Denominateur <=X.Denominateur * Y.Numerateur;

end "<=";

-------------------------------------------------------------- Comparaisons entre deux nombres rationnels: superieurfunction ">" ( X, Y : T_Rationnel ) return Boolean isbegin -- ">"

return X.Numerateur * Y.Denominateur >X.Denominateur * Y.Numerateur;

end ">";

-------------------------------------------------------------- Comparaisons entre deux nombres rationnels: superieur ou-- egal

Page 221: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE 221

function ">=" ( X, Y : T_Rationnel ) return Boolean isbegin -- ">="

return X.Numerateur * Y.Denominateur >=X.Denominateur * Y.Numerateur;

end ">=";------------------------------------------------------------

end Nombres_Rationnels;

Les opérations de comparaison pourraient générer des erreurs de débordement(Constraint_Error) provoquées par l’utilisation des produits croisés, et ladivision par un nombre rationnel nul lèvera une exception (sect. 13.3). Tous les casoù une exception peut être levée doivent être précisés (note 10.3).

NOTE 10.3 Documentation accompagnant un paquetage.

Un mode d’emploi doit accompagner tout paquetage. Il contiendra en particulier les règlesd’utilisation des opérations exportées par la spécification ainsi que les sources possibles des erreursgénérées lors de l’exécution de tout sous-programme du paquetage.

Il faut enfin relever un point fondamental qui justifiera une présentationdétaillée des types privés (sect. 16.2). La transparence du type T_Rationnel,c’est-à-dire la connaissance de sa structure hors du paquetage, peut conduire à uneutilisation erronée d’objets de ce type. Par exemple, si R et S sont deux variablesrationnelles, rien n’interdit au programmeur d’écrire l’agrégat logiquement faux(R.Numerateur + S.Numerateur, R.Denominateur + S.Denominateur)qui ne correspond à aucune opération sur des nombres rationnels. Ces erreurs,involontaires, doivent être détectées si possible à la compilation. Les types privés(sect. 16.2) empêchent de telles erreurs et augmentent la fiabilité (sect. 1.5) d’unpaquetage qui doit naturellement être maximale.

Page 222: Cours Ada de A à Z

PAQUETAGES ENFANTS 222

10.8 PAQUETAGES ENFANTS

10.8.1 Généralités

Soit un paquetage P correctement conçu, et même déjà compilé. Il est doncutilisable par n’importe quelle unité de bibliothèque. L’évolution de ces unités aucours du temps (corrections, modifications, extensions, etc.) peut nécessiter desadaptations du paquetage P, aussi bien de sa spécification que de son corps. Enparticulier, l’adjonction de nouvelles fonctionnalités à P nécessitera la recom-pilation de P et de toutes les unités qui l’importent, même si ses nouvellesfonctionnalités ne sont pas utiles pour certaines d’entre elles.

Pour étendre le paquetage P d’origine sans les inconvénients cités ci-dessus, ilexiste la notion de paquetage enfant (child package). Il s’agit de laisser lepaquetage P tel quel et le compléter par un paquetage supplémentaire dont laspécification et le corps complètent la spécification du paquetage P. Cette manièrede procéder implique que les unités utilisant uniquement P, donc pas le nouveaupaquetage enfant, n’auront pas besoin d’être recompilées! Naturellement celles quiimportent le paquetage enfant devront l’être. Dans ce contexte, le paquetage P estappelé paquetage parent (parent package).

Le nom d’un enfant est obligatoirement un nom développé (sect. 4.4) composédu nom du paquetage parent comme préfixe et d’un identificateur adéquat carac-térisant l’enfant. De tels noms mentionnés précédemment (sect. 10.3) désignentdonc des paquetages enfants!

Exemple 10.11 Spécification d’un paquetage enfant Nombres_Rationnels.Utilitaires.

-- Ce paquetage complete le calcul avec les nombres rationnelspackage Nombres_Rationnels.Utilitaires is

-------------------------------------------------------------- Valeur absolue d'un nombre rationnelfunction "abs" ( X : T_Rationnel ) return T_Rationnel;

-- Multiplication d'un nombre rationnel par un nombre entierfunction "*" ( N : Integer;

X : T_Rationnel ) return T_Rationnel;

function "*" ( X : T_Rationnel;N : Integer ) return T_Rationnel;

-------------------------------------------------------------- Division d'un nombre rationnel par un nombre entierfunction "/" ( X : T_Rationnel;

N : Integer ) return T_Rationnel;------------------------------------------------------------

end Nombres_Rationnels.Utilitaires;

Page 223: Cours Ada de A à Z

PAQUETAGES ENFANTS 223

L’exemple 10.11 montre la définition de la spécification du paquetage enfantNombres_Rationnels.Utilitaires. Celle-ci met à disposition quatre fonc-tions-opérateurs pour des calculs complémentaires sur les nombres rationnels.Quant à l’exemple 10.12, il présente, lui, le corps de ce paquetage enfant.

Exemple 10.12 Corps du paquetage enfant Nombres_Rationnels.Utilitaires.

-- Ce paquetage complete le calcul avec les nombres rationnelspackage body Nombres_Rationnels.Utilitaires is

-------------------------------------------------------------- Valeur absolue d'un nombre rationnelfunction "abs" ( X : T_Rationnel ) return T_Rationnel isbegin -- "abs"

return ( abs X.Numerateur, X.Denominateur );

end "abs";

-------------------------------------------------------------- Multiplication d'un nombre rationnel par un nombre entierfunction "*" ( N : Integer;

X : T_Rationnel ) return T_Rationnel is

begin -- "*"

return ( N * X.Numerateur, X.Denominateur );

end "*";

------------------------------------------------------------function "*" ( X : T_Rationnel;

N : Integer ) return T_Rationnel isbegin -- "*"

return ( N * X.Numerateur, X.Denominateur );

end "*";

-------------------------------------------------------------- Division d'un nombre rationnel par un nombre entierfunction "/" ( X : T_Rationnel;

N : Integer ) return T_Rationnel isbegin -- "/"

if N > 0 then -- Diviseur positifreturn ( X.Numerateur, N * X.Denominateur );

elsif N < 0 then -- Diviseur negatif, le signe est au-- numerateur

return ( – X.Numerateur, abs N * X.Denominateur );

else -- Division par zero!... -- Lever une exception (sect. 13.3)

end if;

end "/";

Page 224: Cours Ada de A à Z

PAQUETAGES ENFANTS 224

------------------------------------------------------------end Nombres_Rationnels.Utilitaires;

Les multiplications et la division par un nombre entier retournent des nombresrationnels non irréductibles! Il faudrait les réduire par l’utilisation de la fonctionIrreductible, soit placée dans la partie visible de Nombres_Rationnels oualors déclarée une deuxième fois dans le corps du paquetage enfantNombres_Rationnels.Utilitaires, solution insatisfaisante dans les deux cascar cette opération est évidemment purement interne. Une solution élégante à ceproblème passe par l’utilisation d’un enfant privé (sect. 16.7).

10.8.2 Visibilité entre paquetages parents et enfants

Les éléments de la spécification d’un paquetage parent sont directementvisibles (sect. 4.4) dans la spécification et le corps d’un paquetage enfant. Leséléments du corps du paquetage parent ne sont jamais visibles pour le paquetageenfant. On peut donc se représenter le paquetage enfant comme s’il était déclaré entrela spécification et le corps du paquetage parent (fig. 10.7 et 16.2).

Figure 10.7 Visibilité entre paquetages parent et enfant.

Un paquetage enfant peut être parent d’un autre paquetage enfant et ainsi desuite. Un paquetage parent peut avoir plusieurs paquetages enfants. On obtientalors une hiérarchie de paquetages qui devraient illustrer la décompositionnaturelle des fonctionnalités.

Du point de vue de la visibilité, les éléments de la spécification d’un paquetageparent sont directement visibles dans la spécification et le corps de tous sespaquetages enfants, petits-enfants, etc.

10.8.3 Utilisation d’un paquetage enfant

package body Parent.Enfant is...

end Parent.Enfant;

package Parent.Enfant is...

end Parent.Enfant;

Paquetage parentpackage Parent is

...end Parent;

package body Parent is...

end Parent;

Paquetage enfant

Page 225: Cours Ada de A à Z

PAQUETAGES ENFANTS 225

Comme pour un paquetage parent, toute unité de bibliothèque doit mentionnerle paquetage enfant dans une clause de contexte (§ 10.5.1) afin d’accéder à saspécification. Une clause use peut également s’appliquer au nom d’un paquetageenfant pour éviter de préfixer les éléments de sa spécification.

Une particularité très pratique consiste en le fait qu’une clause de contexte pourl’utilisation d’un paquetage enfant comprend implicitement une telle clause pourson paquetage parent ainsi que pour tous ses ancêtres! Par contre, cette règle nes’applique pas aux clauses use. L’exemple 10.13 présente quelques cas d’uti-lisation d’un paquetage enfant.

Exemple 10.13 Paquetages parent, enfant, clauses de contexte et clauses use.

-- Premier exemple

with Nombres_Rationnels.Utilitaires;-- Implique automatiquement with Nombres_Rationnels

procedure Exemple_10_13 is

use Nombres_Rationnels.Utilitaires;

... -- Visibilite directe sur les elements de Utilitaires

----------------------------------------------------------------- Deuxieme exemple

with Nombres_Rationnels.Utilitaires;use Nombres_Rationnels;

procedure Exemple_10_13 is

use Utilitaires;

... -- Visibilite directe sur les elements des deux-- specifications

----------------------------------------------------------------- Troisieme exemple

with Nombres_Rationnels.Utilitaires;

package body Nombres_Rationnels is

-- La specification d'un enfant peut s'utiliser dans le corps du-- parent

...-- Quatrieme exemple

with Nombres_Rationnels.Utilitaires;

package Nombres_Rationnels.Autre_Enfant is

-- La specification d'un enfant peut s'utiliser dans la-- specification d'un autre enfant

...----------------------------------------------------------------- Cinquieme exemple

with Nombres_Rationnels.Utilitaires;

package body Nombres_Rationnels.Autre_Enfant is

Page 226: Cours Ada de A à Z

PAQUETAGES ENFANTS 226

-- La specification d'un enfant peut s'utiliser dans le corps-- d'un autre enfant

...

10.8.4 Remarques

Fort de la connaissance de la notion de paquetage enfant et pour obtenir uneexcellente structuration, le paquetage Nombres_Rationnels devrait être disjointen un paquetage parent plus concis (le type T_Rationnel, la fonction deconstruction et les quatre opérations arithmétiques de base) et un nouveaupaquetage enfant contenant les autres éléments (la constante Zero et les opérationsde comparaison).

La compilation d’un paquetage enfant ne peut se faire que lorsque laspécification de son parent a été introduite dans la bibliothèque. Bien entendu laspécification d’un paquetage enfant se compile avant le corps.

Il existe des règles particulières pour les paquetages enfants appelés publics ouprivés (sect. 16.6 et 16.7). Cette distinction deviendra nécessaire lorsque lesnotions de types privés et de parties privées (sect. 16.2) auront été présentées.

Pour terminer, il faut mentionner que la norme Ada comporte de nombreuxpaquetages prédéfinis ou offerts dans les annexes du langage (sect. 19.3).

Page 227: Cours Ada de A à Z

227

10.9 EXERCICES

10.9.1 Spécification de paquetagesEcrire la spécification de paquetages de gestion de:

• nombres complexes avec les opérations de base (exercice 7.4.4);• vecteurs avec les opérations de base (exercice 8.6.5);• matrices carrées avec les opérations de base (exercice 8.6.6);• fiches personnelles (nom, prénom, état civil, sexe, etc.) avec les opérations

de création, d’affichage et de destruction d’une fiche.

10.9.2 Corps de paquetages

Ecrire le corps des paquetages de l’exercice 10.9.1.

10.9.3 Utilisation de paquetagesRéaliser des programmes de test des opérations fournies par les paquetages des

exercices 10.9.1 et 10.9.2.

10.9.4 Paquetage avec variable rémanente

Ecrire la spécification et le corps d’un paquetage de génération des nombres deFibonacci. L’unique opération exportée sera Prochain_Nombre. Ces nombres secalculent par la formule avec et .

10.9.5 Paquetage avec variable rémanente

Ecrire la spécification et le corps d’un paquetage de génération de nombresentiers pseudo-aléatoires. Les opérations exportées seront Initialiser etProchain_Nombre. Voici un algorithme de génération connu:

Test := 16807 * (Seed rem 127773) – 2836 * (Seed/127773)si Test > 0 alors Seed := Testsinon Seed := Test + M fin siNouveau_Nombre := Réel(Seed)/Réel(M)

M vaut 2**31–1 et Seed est initialisé à 117. Tous les nombres sont des entiersdans l’intervalle –2**31..2**31–1, la conversion finale en nombres réels estnécessaire pour normaliser entre 0.0 et 1.0.

10.9.6 Paquetages enfantsEcrire la spécification et le corps de paquetages enfants dont les parents sont les

paquetages des exercices 10.9.1 et 10.9.2. Plus précisément, les opérations mises àdisposition par les enfants sont:

• la puissance, l’obtention de la partie réelle et celle de la partie imaginaire

Fn Fn 1– Fn 2–+= F0 0= F1 1=

Page 228: Cours Ada de A à Z

228

pour les nombres complexes;• la norme et le produit scalaire pour les vecteurs;• la puissance et la multiplication par un nombre réel pour les matrices;• l’obtention du nombre total de fiches et la recherche d’une fiche en fonction

d’un nom particulier pour le paquetage de gestion de fiches personnelles;un algorithme de recherche peut être imaginé ou alors repris du para-graphe 14.4.8 et adapté au problème.

10.9.7 Utilisation de paquetages enfants

Réaliser des programmes de test des opérations fournies par les paquetagesenfants de l’exercice 10.9.6.

Page 229: Cours Ada de A à Z

POINTS À RELEVER 229

10.10 POINTS À RELEVER

10.10.1 En général

• Les paquetages et leurs équivalents dans d’autres langages de program-mation forment, comme les sous-programmes, un constituant de base des(grands) programmes.

• La réalisation d’un bon paquetage, ou de leurs équivalents dans d’autreslangages de programmation, doit respecter des règles de conception.

10.10.2 En Ada

• La spécification fournit les éléments exportés utilisables hors du paquetage.

• Le corps contient les éléments propres au paquetage, invisibles à l’ex-térieur.

• La spécification des sous-programmes exportés se déclare dans la spéci-fication des paquetages, leur corps dans le corps des paquetages.

• Les éléments d’un paquetage, en particulier les variables, ont la mêmedurée de vie que le paquetage.

• Le code d’initialisation est exécuté avant toute utilisation d’un paquetage.

• Une clause de contexte rend visibles, utilisables, un ou plusieurs paque-tages.

• Une clause use rend directement visibles les éléments exportés d’un pa-quetage.

• Une clause use type appliquée à un type T rend directement visibles lesfonctions-opérateurs associées à T et déclarées dans la même spécificationque T.

• Une unité de compilation est une structure compilable pour elle-même.

• Une unité de bibliothèque est une unité de compilation déjà compilée.

• Une sous-unité est un corps extrait d’une unité de compilation, transforméen une unité de compilation pour elle-même. Le corps-souche est ce quireste dans l’unité de compilation originale après l’extraction.

• Un paquetage enfant est une extension, un complément d’un paquetageappelé paquetage parent.

• Les clauses de contexte possèdent quelques particularités pour des paque-tages enfants.

• Les annexes du langage Ada définissent de nombreux paquetages pré-définis.

Page 230: Cours Ada de A à Z

POINTS À RELEVER 230

Page 231: Cours Ada de A à Z

231

C H A P I T R E 1 1

A R T I C L E S À

Page 232: Cours Ada de A à Z

232 ARTICLES À DISCRIMINANTS

Page 233: Cours Ada de A à Z

MOTIVATION 233

11.1 MOTIVATION

Les types articles vus précédemment (chap. 7) ont tous une structure statique:leurs champs sont définis à la déclaration du type et n’ont aucune dépendance entreeux en dehors de la volonté du concepteur de les associer en un seul type. D’autrepart, si un tableau constitue l’un de ces champs, il doit avoir ses intervallesd’indices déterminés à la compilation. Or les données regroupées dans un articlepeuvent comporter des dépendances naturelles. Par exemple, l’informationconcernant la fiche personnelle d’un individu verra sa forme dépendre partiel-lement du sexe de la personne.

Les discriminants sont des champs qui influencent la structure des articles danslesquels ils sont utilisés. A ce titre ils constituent une sorte de paramètres des typesarticles. Mentionnons également que la notion de discriminant se rencontreégalement dans d’autres contextes (types tâches et types protégés) non présentésdans cet ouvrage.

Page 234: Cours Ada de A à Z

GÉNÉRALITÉS 234

11.2 GÉNÉRALITÉS

11.2.1 Types articles à discriminants

Un discriminant est un champ (§ 7.2.1) délimité par une paire de parenthèseset localisé entre l’identificateur d’un type article et le mot réservé is. Cetteparticularité de placement définit à elle seule qu’un tel champ est un discriminant.De plus, le type d’un discriminant doit être discret ou accès [ARM 3.2] mais cettedernière possibilité ne sera pas développée ici.

Il est possible de placer plus d’un discriminant dans la paire de parenthèses lesdélimitant. Dans ce cas, chaque déclaration est séparée de la suivante par un point-virgule.

A l’intérieur du type article, un ou plusieurs champs (éventuellement aucun...)formeront le contenu (exemple 11.1). Parmi ceux-ci, certains peuvent dépendred’un discriminant: ce sont les tableaux dont l’une des bornes au moins est donnéepar le discriminant seul (une expression est ici interdite). Notons qu’une autre for-me de dépendance est fournie dans les types articles à parties variantes (sect. 11.4).

Exemple 11.1 Types articles à discriminant et déclarations d’articles.

type Intervalle is range 0 .. 100;

type T_Exemple (Discriminant : Intervalle) isrecord -- Exemple symbolique

Champ_1 : Integer;Champ_2 : String (1..Discriminant); -- Sect. 9.2Champ_3 : Float;

end record;type T_Degre is range 0 .. 100;

type T_Coefficients is array (T_Degre range <>) of Integer;

type T_Polynome (Degre : T_Degre) isrecord -- Pour traiter des polynomes

-- Coefficients des termesCoefficients : T_Coefficients (T_Degre'First..Degre);

end record;

Exemple : T_Exemple (5); -- Contient un tableau de 5 elementsQuadratique : T_Polynome (2); -- Un polynome de degre 2

-- Pour afficher n'importe quel polynomeprocedure Afficher ( Polynome : in T_Polynome );

En l’absence d’une valeur par défaut pour un discriminant (sect. 11.3), ladéclaration d’un article (constante ou variable) à discriminant doit comporter unevaleur pour chacun de ses discriminants (exemple 11.1), valeur donnée entreparenthèses ou par une valeur initiale (agrégat). Un tel article est dit contraint (par

Page 235: Cours Ada de A à Z

GÉNÉRALITÉS 235

le discriminant), par analogie avec la déclaration d’un tableau.

Le type T_Polynome va être utilisé pour illustrer le travail avec des articlesdont un champ est un tableau dépendant d’un discriminant [BAR 97], [MOL 92].Par convention, un polynôme classique comme

P(x) = a0 + a1x + a2x2 + ... + anxn avec an /= 0 si n /= 0

va être réalisé sous la forme d’un article de type T_Polynome dont le discriminantreprésentera le degré du polynôme et les éléments du tableau Coefficients im-plémenteront les coefficients a0...an selon la convention Coefficient(i) = ai.

11.2.2 Sous-types articles à discriminants

Il est possible de déclarer des sous-types d’un type (de base) article à discrimi-nants (exemple 11.2). La forme générale d’une telle déclaration est la suivante:

subtype identificateur is id_type_article (valeur_discr_1, ..., valeur_discr_N);

où• identificateur est le nom du sous-type;• id_type_article est le nom d’un type article à discriminant;• valeur_discr_i fixe la valeur du ième discriminant.

Exemple 11.2 Sous-types et articles à discriminants.

-- Pour utiliser des polynomes de degres 2 et 5subtype T_Polynome_2 is T_Polynome (2);subtype T_Polynome_5 is T_Polynome (5);

Quadratique : T_Polynome_2; -- Un polynome de degre 2Polynome_Degre_5 : T_Polynome_5; -- Un polynome de degre 5

11.2.3 Agrégats, expressions et opérations

Les agrégats d’article à discriminants (exemple 11.3) sont semblables à ceuxdes articles simples (§ 7.2.2). Les valeurs attribuées aux discriminants se situent audébut de l’agrégat si la notation par position est utilisée. La seule subtilité résidedans les agrégats dynamiques, où la valeur d’un discriminant et une borne dutableau (au moins) sont donnés par une variable.

Exemple 11.3 Déclarations de polynômes et agrégats.

-- Le polynome 3 – 2x + 5x2

Polynome_1: T_Polynome (2) := (2, (3, –2, 5) );

-- Le polynome x + 7x4 declare de deux manieres equivalentes

Page 236: Cours Ada de A à Z

GÉNÉRALITÉS 236

Polynome_2: T_Polynome (4) := (4, (0, 1, 0, 0, 7) );Polynome_3: T_Polynome := (4, (0, 1, 0, 0, 7) );

-- Le polynome constant –6Polynome_4: constant T_Polynome (0) :=

( Degre => 0,Coefficients => (0 => –6) );

-- Le polynome 1+x+x2+...+xn

Polynome_5: T_Polynome := (N, (0..N => 1) );-- N de type T_Degre-- et voir § 8.2.5

Il faut rappeler qu’il est toujours possible de qualifier l’agrégat, c’est-à-dire depréciser son type en le préfixant par le nom du type suivi d’une apostrophe.

Les expressions d’un type article à discriminants sont réduites aux constantes,variables et paramètres de ce type.

Les opérations possibles sur les articles (en plus de l’affectation et du passageen paramètre) sont l’égalité = et l’inégalité /=.

11.2.4 Affectation

L’affectation se fait de manière habituelle, mais en précisant que la valeur d’undiscriminant donnée à la déclaration d’une variable ne peut plus être modifiée parla suite. L’expression et la variable présentes dans une opération d’affectationdevront donc comporter les mêmes valeurs pour tous les discriminants corres-pondants, sinon l’exception Constraint_Error sera levée. L’exemple 11.4illustre l’affectation et le passage en paramètres d’articles à discriminants.

Exemple 11.4 Affectation de valeurs d’un type article à discriminants.

-- ...procedure Exemple_11_4 is

type T_Degre is range 0 .. 100;

type T_Coefficients is array (T_Degre range <>) of Integer;

type T_Polynome (Degre : T_Degre) isrecord -- Pour traiter des polynomes

-- Coefficients des termesCoefficients : T_Coefficients (T_Degre'First..Degre);

end record;

-- Une constante et deux variables de ce typeDeux_Plus_X : constant T_Polynome := (1, (2, 1));

Polynome_1 : T_Polynome (1);Polynome_2 : T_Polynome := ( Degre => 2,

Coefficients => (2, –4, 1));

------------------------------------------------------------

Page 237: Cours Ada de A à Z

GÉNÉRALITÉS 237

-- Pour afficher n'importe quel polynomeprocedure Afficher ( Polynome : in T_Polynome ) is

...end Afficher;------------------------------------------------------------

begin -- Exemple_11_4

-- Trois affectations correctes car meme valeur de discriminantPolynome_1 := Deux_Plus_X;Polynome_2 := (Polynome_2.Degre, (1, –2, 1));Polynome_2 := T_Polynome'(2, (1, –2, 1));

Afficher ( Deux_Plus_X ); -- Affichage de deux polynomesAfficher ( Polynome_2 );

Polynome_2 := Polynome_1; -- Provoque Constraint_Error... -- (discriminants differents)

11.2.5 Compléments sur les articles à discriminants

Comme déjà mentionné, une expression, et non seulement une constante, peutdonner la valeur d’un discriminant (exemple 11.5). Par contre, un discriminant seulpeut être utilisé comme borne d’un champ tableau.

Comme suggéré dans l’exemple 11.4, l’accès à un champ discriminants’effectue comme pour un champ habituel, en préfixant le nom du discriminant parle nom de l’article et en séparant les deux identificateurs par un point.

Il existe des attributs applicables à un article ou un type article à discriminants.Le plus utile est l’attribut Constrained (§ 11.3.3).

Lors d’un passage en paramètre, la valeur du discriminant d’un paramètreformel comme Polynome (exemple 11.4) est obtenue de celle du paramètre effectifcorrespondant, quel que soit le mode de passage (in, out, in out) du paramètre.Si le paramètre formel est d’un type ou sous-type contraint, les valeurs desdiscriminants des paramètres formel et effectif doivent être identiques, sinonl’exception Constraint_Error sera levée à l’appel du sous-programme.

Une fonction peut retourner un article à discriminant comme résultat (exemple11.5).

Exemple 11.5 Illustration de quelques-unes des remarques ci-dessus.

-- Expression comme valeur de discriminantPolynome_1 : T_Polynome (3 * 4 + N) -- N entier----------------------------------------------------------------- Fonction a resultat de type T_Polynomefunction Polynome_Nul return T_Polynome isbegin -- Polynome_Nul

Page 238: Cours Ada de A à Z

GÉNÉRALITÉS 238

return (0, (0 => 0) );

end Polynome_Nul;----------------------------------------------------------------- Correspondance entre valeurs de discriminants, N entier egal-- a 3 sinon Constraint_ErrorPolynome_2 : T_Polynome (3) := (N, (1..N => 1) );

Page 239: Cours Ada de A à Z

VALEURS PAR DÉFAUT DES DISCRIMINANTS 239

11.3 VALEURS PAR DÉFAUT DES DISCRIMINANTS

11.3.1 Motivation

L’inconvénient majeur des articles à discriminants examinés jusqu’ici résidedans l’impossibilité de modifier la contrainte de discriminant, c’est-à-dire la valeurdonnée lors de la déclaration de tels articles. En introduisant une expression(valeur) par défaut pour un discriminant lors de la déclaration du type article, cetype devient non contraint et la liberté existe de contraindre ou non un article de cetype lors d’une déclaration de variable, en mentionnant explicitement une valeurou, au contraire, en laissant la valeur de l’expression par défaut donner la valeur dudiscriminant. Le fait d’utiliser la valeur de l’expression par défaut conduit à créerun article non contraint dont les champs, y compris les discriminants, peuvent êtremodifiés par la suite. La modification de la valeur d’un discriminant d’un articlenon contraint n’est cependant possible que par une affectation globale de l’article,au moyen d’un agrégat par exemple (exemple 11.6). Cette manière de faire assurele maintien de la cohérence entre la valeur du discriminant et les champs qui endépendent.

Exemple 11.6 Discriminants avec valeur par défaut.

-- ...procedure Exemple_11_6 is

type T_Degre is range 0 .. 100;

type T_Coefficients is array (T_Degre range <>) of Integer;

type T_Polynome (Degre : T_Degre:= 0) is-- 0 est la valeur-- par defaut

recordCoefficients : T_Coefficients (T_Degre'First..Degre);

end record;

-- Une constante et une variable contrainte de ce typeDeux_Plus_X : constant T_Polynome := (1, (2, 1));Polynome_1 : T_Polynome (1);

-- Deux variables de ce type, non contraintesPolynome_2 : T_Polynome := ( Degre => 2,

Coefficients => (2, –4, 1));Polynome_3 : T_Polynome;

begin -- Exemple_11_6

-- Toutes les affectations suivantes sont correctesPolynome_1 := Deux_Plus_X;

Polynome_2 := (Polynome_2.Degre, (1, –2, 1));Polynome_2 := T_Polynome'(2, (1, –2, 1));

Polynome_3 := Polynome_1;Polynome_3 := (Polynome_2.Degre – 1, Polynome_2.Coefficients

(0..Polynome_2.Degre –

Page 240: Cours Ada de A à Z

VALEURS PAR DÉFAUT DES DISCRIMINANTS 240

1));...

Si un article comprend plus d’un discriminant, alors il doit être complètementcontraint (aucune expression par défaut) ou non contraint (tous les discriminantsont une expression par défaut). Finalement un paramètre formel d’entrée-sortie oude sortie d’un type article à discriminants est contraint si le paramètre effectif l’est;un paramètre formel d’entrée est de toute manière toujours constant.

11.3.2 Remarque importante

Quel serait l’effet de remplacer le sous-type T_Degre par Natural dans ladéclaration du type T_Polynome avec expression par défaut (§ 11.3.1)? Uncompilateur Ada réservera en général une place mémoire permettant d’implanter letableau Coefficients, quelle que soit la valeur par défaut. Le nombre d’octetsnécessaires sera donc de Natural'Last multiplié par la taille d’un entier, ce quireprésente beaucoup (trop) de mémoire et provoquera donc très vraisemblablementl’exception Storage_Error, exception qui est levée lorsque la place mémoiredevient insuffisante lors de l’exécution d’un programme.

11.3.3 Attribut Constrained

L’attribut Constrained qui s’applique entre autres à un article à discriminantsrend une valeur booléenne vraie si l’article est contraint, fausse sinon. En faisantréférence à l’exemple 11.6, on obtiendrait Polynome_1'Constrained = True,Polynome_2'Constrained = False et Polynome_3'Constrained = False.

Exemple 11.7 Utilisation de l’attribut Constrained.

-- Cette procedure normalise le polynome P, c'est-a-dire qu'elle-- assure que le polynome rendu dans P, de degre n, a son terme

-- anxn non nul.

-- Il faut cependant que le parametre effectif soit non contraint.procedure Normaliser ( P : in out T_Polynome ) is

Degre_Polynome : Natural := P.Degre; -- Degre du polynome

begin -- Normaliser

-- Normalisation possible?if not P'Constrained then

-- Chercher le plus grand coefficient non nulwhile Degre_Polynome > 0 and then

P.Coefficients (Degre_Polynome) = 0 loop

Degre_Polynome := Degre_Polynome – 1;end loop;

Page 241: Cours Ada de A à Z

VALEURS PAR DÉFAUT DES DISCRIMINANTS 241

P := ( Degre_Polynome,P.Coefficients (T_Degre'First..Degre_Polynome) );

end if;

end Normaliser;

Page 242: Cours Ada de A à Z

ARTICLES À PARTIES VARIANTES 242

11.4 ARTICLES À PARTIES VARIANTES

11.4.1 Motivation

Il n’est pas rare qu’un groupe d’informations formant un tout cohérent,typiquement le contenu d’un article, comporte une partie commune à tous lesexemplaires de ces groupes mais que ces exemplaires se distinguent les uns desautres par la présence ou l’absence de certaines informations n’appartenant pas àcette partie commune. Par exemple, les informations nécessaires pour décrire unepersonne comportent des indications indépendantes du sexe (taille, poids, couleurdes cheveux...) mais d’autres ne sont opportunes que pour l’un des deux sexes(présence ou absence de barbe, femme enceinte ou non...).

Les fenêtres des interfaces utilisateurs actuelles en sont un autre exemple.Parmi les informations communes, l’on trouvera les dimensions et la position, maisles fenêtres se classent en plusieurs catégories: celles qui possèdent un titre, lesfenêtres graphiques ou textuelles, celles prévues pour un dialogue modal, lesavertissements, etc. Une telle fenêtre s’implémentera directement par un article àdiscriminants avec partie variantes.

11.4.2 Généralités

Une partie variantes est une structure permettant de déclarer les champs par-ticuliers de certains articles et ressemblant fortement à la syntaxe de l’instructioncase.

La forme générale d’une partie variantes est:case discriminant iswhen choix_1 => suite_de_decl_de_champs_1;-- Premiere branchewhen choix_2 => suite_de_decl_de_champs_2;-- Deuxieme branchewhen choix_3 => suite_de_decl_de_champs_3;-- Troisieme branche...when others => autre_suite_de_decl_de_champs;-- Derniere brancheend case;

avec• discriminant d’un type discret et déclaré comme discriminant d’article;• les choix_n statiques (sect. 4.10), formés de valeurs ou d’intervalles

séparés par des barres verticales, valeurs et bornes d’intervalle du type dudiscriminant;

• les suite_de_decl_de_champs_n composées d’une ou de plusieursdéclarations de champs (éventuellement aucune);

• le mot réservé others qui représente toutes les autres valeurs possibles dudiscriminant.

Un type article à discriminant peut donc comporter maintenant, commedernière déclaration, une partie variantes qui permettra de déclarer des objets de ce

Page 243: Cours Ada de A à Z

ARTICLES À PARTIES VARIANTES 243

type (exemple 11.8) et dont la structure variera en fonction de la valeur dudiscriminant.

Exemple 11.8 Types articles à partie variantes et déclarations d’articles.

Max : constant := 80; -- Longueur maximum d'une ligne

type T_Genre_Fenetre is ( Confirmation, De_Taille_Variable,Avertissement);

type T_Fenetre ( Genre : T_Genre_Fenetre:= De_Taille_Variable ) isrecord

Abscisse : Natural; -- Position de la fenetreOrdonnee : Natural;Longueur : Natural; -- Dimensions de la fenetre en

pixelsLargeur : Natural;

case Genre is -- Partie variantes

when Confirmation =>Texte_Affiche : String ( 1..Max );-- Selon le texte,Reponse_OK : Boolean; -- confirmer ou non

when De_Taille_Variable =>Variation_X : Integer := 0; -- Variations par rapportVariation_Y : Integer := 0; -- aux dimensions d’origine

when Avertissement => Texte_Avertissement : String ( 1..Max );

end case;

end record;

Edition : T_Fenetre ( De_Taille_Variable ); -- Article contraintGraphique : T_Fenetre; -- Article non contraintErreur_NT : T_Fenetre ( Avertissement ); -- Article contraint

11.4.3 Expressions, agrégats et opérations sur les articles à parties variantes

Les expressions, agrégats et opérations sur les articles à parties variantes sontsemblables à ce qui existe pour les articles à discriminants sans partie variantes. Laseule différence concerne les agrégats: les valeurs des discriminants doivent y êtrestatiques puisqu’un agrégat joue le rôle d’une constante (exemple 11.9). Il est bienclair que seules des valeurs pour la branche correspondant à la valeur du dis-criminant seront mentionnées dans l’agrégat.

Exemple 11.9 Déclarations d’articles à partie variantes et d’agrégats.

-- Edition est un article contraint

Page 244: Cours Ada de A à Z

ARTICLES À PARTIES VARIANTES 244

Edition : T_Fenetre ( De_Taille_Variable ) :=(De_Taille_Variable, 0, 0, 600, 400, 0, 0);

-- Graphique est non contraint malgre la valeur initialeGraphique : T_Fenetre :=

(De_Taille_Variable, 0, 0, 600, 400, 10, 20);

-- Agregat tableau (§ 8.2.5) pour le texte d'avertissementErreur_NT : T_Fenetre ( Avertissement ) :=

(Avertissement, 0, 0, 600, 400,"Profil inconnu" & (15..80=>' '));

11.4.4 Affectation

L’affectation se fait de manière habituelle, en rappelant que la valeur d’undiscriminant donnée à la déclaration d’une variable (et non par une valeur initiale)ne peut plus être modifiée par la suite. L’expression et la variable présentes dansune opération d’affectation doivent donc comporter les mêmes valeurs pour tousles discriminants correspondants, sinon l’exception Constraint_Error seralevée. Si les discriminants comportent une valeur par défaut et que cette valeur estutilisée lors de la déclaration d’un article (comme pour Graphique dans l’exemple11.8), la valeur de tout l’article, y compris les discriminants, est modifiable glo-balement.

11.4.5 Compléments sur les articles à parties variantes

Un choix (représentant une valeur possible du discriminant) ne peut apparaîtrequ’une seule fois.

Si le discriminant reçoit une valeur ne correspondant à aucun des choixmentionnés explicitement, la branche commençant par when others, qui doit êtrela dernière, est choisie.

Si les choix mentionnés explicitement (autres que others) couvrent toutes lesvaleurs possibles de l’expression, alors la branche commençant par when othersest optionnelle.

Le mot réservé null doit être utilisé dans une branche où aucune déclarationn’est effectuée.

L’accès aux champs d’une branche se fait comme pour un champ habituel (sect.7.3). Mais seuls les champs de la branche correspondant à la valeur du discriminantsont utilisables. Une tentative d’accès à un champ d’une autre branche lèveraConstraint_Error.

Les identificateurs de tous les champs d’un article (avec ou sans partievariantes) doivent être différents.

Une partie variantes peut être emboîtée dans une autre pourvu qu’elle se situe

Page 245: Cours Ada de A à Z

ARTICLES À PARTIES VARIANTES 245

à la fin de la branche qui l’englobe.

Page 246: Cours Ada de A à Z

246

11.5 EXERCICES

11.5.1 Chaînes de caractères de longueur variableDéclarer un type article contraint à discriminants T_Chaine_Contraint pour

représenter des chaînes de caractères de longueur variable mais d’au maximum 256caractères. En faire de même mais avec un type article à discriminantsT_Chaine_Non_Contraint cette fois non contraint.

11.5.2 Chaînes de caractères de longueur variable

Déclarer une variable du type T_Chaine_Contraint et une autre du typeT_Chaine_Non_Contraint (exercice 11.5.1) puis leur affecter successivement àchacune les chaînes "Bonjour" et "Au revoir".

11.5.3 Paquetage de chaînes de caractères de longueur variableEcrire un paquetage de gestion de chaînes de caractères du type

T_Chaine_Contraint et un autre pour le type T_Chaine_Non_Contraint(exercice 11.5.1). Les opérations à disposition seront la lecture au clavier,l’affichage à l’écran, la concaténation, l’obtention de la longueur et les opérateursde comparaison.

11.5.4 Matrices carrées

Reprendre le paquetage de gestion de matrices carrées créé dans les exercices10.9.1 et 10.9.2, et transformer le type tableau T_Matrice en un type article àdiscriminants où le discriminant représente l’ordre de la matrice. Modifier enconséquence le reste (corps) du paquetage, ainsi que le programme de test del’exercice 10.9.3.

11.5.5 Fiches personnellesReprendre le paquetage de gestion de fiches personnelles créé dans les

exercices 10.9.1 et 10.9.2, et transformer à votre guise le type des fiches en un typearticle à discriminants avec partie variantes (les champs sexe et état civil sont debons candidats pour devenir des discriminants). Modifier en conséquence le reste(corps) du paquetage, ainsi que le programme de test de l’exercice 10.9.3.

Page 247: Cours Ada de A à Z

POINTS À RELEVER 247

11.6 POINTS À RELEVER

11.6.1 En Ada

• Les types articles à discriminants permettent d’obtenir des articles de mêmetype mais de constitution variable.

• Un discriminant est un champ jouant un rôle particulier.

• Un discriminant peut servir à préciser la valeur initiale d’un champ, uneborne d’un champ tableau, ou à introduire une partie variantes.

• Un type article à discriminant peut être contraint ou non.

• Un article à discriminant d’un type non contraint peut être contraint ou non.

• La valeur d’un discriminant ne peut être modifiée que par une modificationglobale de tous les champs de l’article.

• Seuls les champs définis peuvent être accédés, sinon Constraint_Errorsera levée.

Page 248: Cours Ada de A à Z

248

C H A P I T R E 1 2

F I C H I E R

Page 249: Cours Ada de A à Z

249 FICHIERS

Page 250: Cours Ada de A à Z

MOTIVATION 250

12.1 MOTIVATION

Les structures de données que nous avons déjà vues ont un point commun: ellesrésident toutes dans la mémoire principale de l’ordinateur. Ceci signifie quel’effacement (volontaire ou non!) de la mémoire provoque la destruction de cesstructures et de leur contenu, ainsi d’ailleurs que celle du programme les utilisant.De plus, il peut être nécessaire de conserver des données à la fin de l’applicationqui les a créées, ceci en prévision d’une utilisation future. Par exemple, l’utilisationd’un traitement de texte conduit à la manipulation de contenus lus et écrits sursupports magnétiques ou autres. Ces quelques considérations nous amènent àintroduire la notion de fichier.

Page 251: Cours Ada de A à Z

NOTION DE FICHIER 251

12.2 NOTION DE FICHIER

Hors du monde informatique un fichier est une collection de fiches; chacun denous a peut-être manipulé un fichier dans une bibliothèque, une administration, etc.Ces fichiers se caractérisent par un nombre quelconque de fiches en général toutesde même aspect. Pour trouver une fiche particulière, il faut les parcourir une à uneou utiliser une clé d’accès si le fichier est trié selon cette clé (ordre alphabétique,ordre de cotation de livres...).

En informatique, on définit qu’un fichier (file) est une structure composée dedonnées dont le nombre n’est pas connu a priori et qui réside en mémoiresecondaire. L’accès à un élément (à une donnée) du fichier peut se faire

• séquentiellement (sequential access), c’est-à-dire en parcourant le fichierélément par élément depuis le début jusqu’au moment où, par exemple, unélément cherché est trouvé;

• directement (direct access), en donnant la position d’un élément;• selon une clé (access by key), chaque valeur de la clé désignant un élément

particulier et permettant donc d’obtenir l’élément désiré; mais le traitementde ce dernier type d’accès dépasse les objectifs de cet ouvrage.

Comme les fichiers sont conservés en mémoire secondaire (disques et bandesmagnétiques, disquettes, cassettes, disques compacts, DVD...), ils subsistent tantque cette mémoire n’est pas effacée ou endommagée. Chaque fichier est désignépar un nom et possède des attributs tels que date de création, taille, icône, exten-sion... Ils se répartissent en deux catégories:

• les fichiers (de) texte (text files, appelés aussi imprimables) contenant descaractères et susceptibles d’être lus par un être humain, édités, imprimés...(sect. 12.3);

• les fichiers binaires (binary files), qui contiennent du code binaire repré-sentant chaque élément (sect. 12.4); ces fichiers ne doivent être manipulésque par des programmes!

La différence entre les deux catégories peut être illustrée par un exemplesimple. Un fichier binaire contenant la valeur 75 comportera un mot (supposons 2octets) formé des bits 00000000 01001011 représentant l’entier 75, alors que dansun fichier texte ce même nombre sera écrit comme les deux caractères '7' et '5'accolés, implémentés par les codes (LATIN-1, § 3.8.1) 00110111 et 00110101.

En Ada comme dans d’autres langages, on distingue les termes fichier (fileobject) et fichier externe (external file). Un fichier est une variable d’un pro-gramme destinée à désigner un fichier externe. Un fichier externe est un objetextérieur au programme, conservé en mémoire secondaire et désigné par un nom.Il est fréquent, dans le langage parlé, d’appeler indistinctement fichier les deuxtypes d’entités.

Page 252: Cours Ada de A à Z

FICHIERS TEXTE 252

12.3 FICHIERS TEXTE

12.3.1 Généralités

Les fichiers texte constituent un cas particulier des fichiers séquentiels car ilssont formés d’éléments bien connus: les caractères. Chacun a déjà manipulé de telsfichiers: un programme source (Ada ou autre) est en fait un fichier texte! Les carac-tères contenus dans un fichier texte sont organisés en lignes, chacune terminée parune fin de ligne. Après la dernière ligne, le fichier se termine; cet endroit particulierest appelé fin de fichier et représente une limite à ne jamais dépasser lors de lalecture des caractères sous peine de voir levée l’exception End_Error (sect. 12.5).

En Ada, le traitement des fichiers texte s’effectue grâce au paquetage prédéfiniAda.Text_IO (sect. 19.3) dans lequel sont définis les types, sous-types,procédures, fonctions et exceptions nécessaires. Les notions de base pour lamanipulation d’un fichier seront donc présentées en utilisant Ada.Text_IO. Unfichier texte se déclare comme une variable habituelle (exemple 12.1), en utilisantle type File_Type qui est particulier dans le sens où sa structure est cachée àl’utilisateur. De tels types (privés) feront l’objet d’une présentation ultérieure (sect.16.2). Il suffit d’indiquer ici qu’ils permettent essentiellement la déclaration devariables et de paramètres.

La seule opération permise sur les fichiers est le passage en paramètre.L’affectation est en effet interdite entre des fichiers (car ils sont d’un type limité,sect. 16.9). Par conséquent il est impossible de déclarer une constante d’un typefichier.

Les expressions se réduisent aux variables d’un type fichier. Comme dans le casdes tableaux, l’intérêt principal des fichiers réside en l’utilisation de leurs éléments(§ 12.3.3).

Il faut encore préciser que les deux paragraphes précédents s’appliquent à tousles fichiers et pas seulement aux fichiers texte.

Exemple 12.1 Déclarations de fichiers comme variables et paramètres.

with Ada.Text_IO;

-- ...procedure Exemple_12_1 is

Original : Ada.Text_IO.File_Type; -- Deux variables fichiersCopie : Ada.Text_IO.File_Type; -- texte

use Ada.Text_IO; -- Pour eviter de prefixer le type File_Type

-------------------------------------------------------------- Cree une copie de Source, copie designee par Destinationprocedure Dupliquer ( Source: in File_Type;

Page 253: Cours Ada de A à Z

FICHIERS TEXTE 253

Destination: in File_Type) isbegin -- Dupliquer

...end Dupliquer;------------------------------------------------------------

begin -- Exemple_12_1...

12.3.2 Création, ouverture et fermeture d’un fichier texte

En Ada un fichier texte peut être lu, écrit ou complété à la fin. On dit que lefichier est utilisé en mode lecture, en écriture ou en adjonction. La lecture (read)consiste à consulter le contenu sans le changer, alors que l’écriture (write) permetde créer un nouveau contenu (en effaçant l’ancien s’il existait). Finalementl’adjonction (append) laisse le contenu présent tel quel mais permet de rajouter desinformations à la fin du fichier.

L’ouverture (open) d’un fichier consiste entre autres à associer une variablefichier à un fichier externe et à choisir le mode d’utilisation (lecture, écriture,adjonction) du fichier. Ceci se fait par la procédure Create si le fichier est nouveauou par Open s’il existe déjà. Ces deux procédures ont les en-têtes (semblables)suivantes:

procedure Create ( File : in out File_Type;Mode : in File_Mode := Out_File;Name : in String := "";Form : in String := "" );

procedure Open ( File : in out File_Type;Mode : in File_Mode;Name : in String;Form : in String := "" );

avec• File le fichier créé s’il est nouveau ou simplement ouvert s’il existait déjà;• Mode le mode d’utilisation (d’accès) du fichier, par défaut en écriture lors

de la création;• File_Mode un type énumératif déclaré dans Ada.Text_IO et fournissant

les valeurs énumérées In_File (lecture), Out_File (écriture) etAppend_File (adjonction);

• Name le nom du fichier externe désigné par File;• Form un paramètre permettant de fixer des propriétés du fichier externe

comme par exemple les droits d’accès.

La valeur par défaut du paramètre Name de la procédure Create permet lacréation d’un fichier temporaire qui sera automatiquement effacé à la fin du

Page 254: Cours Ada de A à Z

FICHIERS TEXTE 254

programme. Par ailleurs, la valeur par défaut du paramètre Form permet l’utili-sation des options courantes de l’implémentation, souvent celles du systèmed’exploitation utilisé.

La fermeture (close) d’un fichier consiste en la suppression de l’association(réalisée à l’ouverture) entre un fichier et un fichier externe. Elle est effectuée parla procédure Close:

procedure Close ( File : in out File_Type );

avec• File le fichier qui doit être fermé.

C’est au plus tard lors de la fermeture d’un fichier que son contenu est modifiéen fonction des opérations réalisées sur le fichier.

12.3.3 Accès aux éléments d’un fichier texte

Les procédures Put et Get, utilisées pour la lecture et l’écriture de donnéesd’un type scalaire [ARM 3.2], de même que celles fournissant d’autres servicescomme Put_Line, Get_Line, New_Line ou Skip_Line sont utilisables avecn’importe quel fichier texte. Il suffit de rajouter le nom de la variable fichiercomme premier paramètre pour que l’opération s’effectue sur le fichier (exemple12.2). Comme déjà mentionné il faut cependant disposer des paquetages d’entrées-sorties adéquats.

Exemple 12.2 Lecture et écriture dans un fichier texte.

with Ada.Text_IO; use Ada.Text_IO;with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;with Ada.Float_Text_IO; use Ada.Float_Text_IO;

-- ...procedure Exemple_12_2 is

Fichier_Traite : File_Type; -- Une variable fichier texte

Titre : String ( 1..10 ); -- Une variable chaine de-- caracteres

Lettre : Character; -- Une variable caractereNb_Entier : Integer; -- Une variable entiereNb_Reel : Float; -- Une variable reelle

L : Natural; -- Pour un appel correct a Get_Line

begin -- Exemple_12_2

-- Creation et ouverture du fichier a ecrire appele texte.txtCreate ( File => Fichier_Traite, Name => "texte.txt" );

Put_Line ( Fichier_Traite, "Titre" ); -- Ecriture sur une ligne

Put ( Fichier_Traite, 'O' ); -- Ecriture d'un caracterePut ( Fichier_Traite, 123 ); -- Ecriture d'un entierPut ( Fichier_Traite, 'N' ); -- Ecriture d'un caractere

Page 255: Cours Ada de A à Z

FICHIERS TEXTE 255

New_Line ( Fichier_Traite ); -- Passage a la ligne

Put ( Fichier_Traite, 3.14 ); -- Ecriture d'un reelNew_Line ( Fichier_Traite ); -- Passage a la ligne

Close ( Fichier_Traite ); -- Fermeture du fichier

-- Ouverture du meme fichier, cette fois-ci en lectureOpen ( File => Fichier_Traite, Mode => In_File,

Name => "texte.txt" );

-- Lecture du titre (L vaut 5)Get_Line ( Fichier_Traite, Titre, L);

Get ( Fichier_Traite, Lettre ); -- Lecture d'un caractere-- (O)

Get ( Fichier_Traite, Nb_Entier ); -- Lecture d'un entier-- (123)

Get ( Fichier_Traite, Lettre ); -- Lecture d'un caractere-- (N)

Skip_Line ( Fichier_Traite ); -- Passage a la ligne

Get ( Fichier_Traite, Nb_Reel ); -- Lecture d'un reel (3.14)Skip_Line ( Fichier_Traite ); -- Passage a la ligne

Close ( Fichier_Traite ); -- Fermeture du fichier...

L’exemple 12.2 est naïvement écrit car le traitement des fichiers texte est plusstructuré que cela (§ 12.3.4). Son seul but est d’illustrer l’utilisation des procéduresd’entrées-sorties de base lorsqu’elles s’appliquent à un fichier texte.

Figure 12.1 Le fichier texte.txt créé par le programme de l’exemple 12.2.

TitreO123N3.14000E+00

12.3.4 Traitement d’un fichier texte

Le traitement d’un fichier texte au sens le plus général consiste en la lecture,l’utilisation du contenu et l’écriture d’un (nouveau) fichier texte, la lecture et l’é-criture s’effectuant séquentiellement, un élément après l’autre sans saut ni retouren arrière. Il faut rappeler que ce contenu est formé de lignes de caractères(terminées par une fin de ligne) et, qu’après la dernière ligne, le fichier se termine(fin du fichier). Le traitement des caractères consiste en fait à les lire ou à les écrirecomme des caractères individuels, comme des groupes constituant une ou plusieurschaînes de caractères ou encore à les interpréter comme des mots ou des nombres.

Page 256: Cours Ada de A à Z

FICHIERS TEXTE 256

Par exemple, la suite –12E3 arbitrairement choisie forme une séquence de cinqcaractères individuels ou une chaîne de cinq caractères, mais elle peut aussi êtreinterprétée différemment comme le nombre entier –12 suivi d’une lettre etfinalement d’un deuxième nombre entier, ou encore former un seul nombre entiernégatif. C’est la signification que l’on désire donner au contenu qui déterminera lamanière de lire (ou d’écrire) l’information d’un fichier texte.

Habituellement une lecture correcte doit tenir compte des fins de lignes (sicelles-ci jouent un rôle particulier) et de la fin du fichier afin de ne pas essayer delire au-delà, ce qui provoquerait inévitablement une erreur. Pour cela leprogrammeur peut utiliser les deux fonctions booléennes End_Of_Line etEnd_Of_File définies dans Ada.Text_IO. Elles ont les en-têtes:

function End_Of_Line ( File : in File_Type ) return Boolean;function End_Of_File ( File : in File_Type ) return Boolean;

avec• File le fichier traité, ouvert en lecture.

La fonction End_Of_Line retourne la valeur True si une fin de ligne précèdele prochain caractère à lire, False dans tous les autres cas. La fonctionEnd_Of_File retourne la valeur True si la fin de fichier est atteinte, False sinon.Pour illustrer l’utilisation de ces fonctions, l’exemple 12.3 présente un algorithmetypique de lecture caractère à caractère d’un fichier texte.

Exemple 12.3 Lecture caractère à caractère d’un fichier texte.

with Ada.Text_IO; use Ada.Text_IO;

-- ...procedure Exemple_12_3 is

Fichier_Traite : File_Type; -- Une variable fichier texteLettre : Character; -- Une variable caractere

begin -- Exemple_13_3

-- Ouverture du fichier a lireOpen ( File => Fichier_Traite, Mode => In_File,

Name => "texte.txt" );-- Fin de fichier?while not End_Of_File ( Fichier_Traite ) loop

-- Fin de ligne?while not End_Of_Line ( Fichier_Traite ) loop

Get ( Fichier_Traite, Lettre ); -- Lecture d'un caractere

...; -- Traitement du caractere lu

end loop;

Skip_Line ( Fichier_Traite ); -- Passage a la ligne

end loop;

Page 257: Cours Ada de A à Z

FICHIERS TEXTE 257

Close ( Fichier_Traite ); -- Fermeture du fichier...

La suite des caractères lus et traités est donnée par la figure 12.2. Lors del’écriture dans un fichier texte, c’est naturellement le passage à la ligne qui termineune ligne et la fermeture du fichier qui provoque la fin du fichier par l’intermédiairedu système d’exploitation.

Figure 12.2 Caractères lus par le programme de l’exemple 12.3 (<sp> représente un espace).

T i t r e O<sp><sp><sp><sp><sp><sp><sp><sp>1 2 3 N<sp>3 . 1 4 0 0 0 E +0 0

12.3.5 Remarques sur l’utilisation de Ada.Text_IO

Le paquetage Ada.Text_IO offre quelques facilités pour des applicationsinteractives. Il s’agit des procédures Get_Immediate et Look_Ahead.

La procédure Get_Immediate permet de lire le caractère suivantimmédiatement, contrairement à Get qui nécessite l’introduction d’une fin de ligneau clavier pour fournir le caractère. Elle possède deux formes:

procedure Get_Immediate ( Item: out Character );procedure Get_Immediate ( Item: out Character;

Available: out Boolean );

avec• Item le caractère lu immédiatement;• Available qui indique s’il existe un caractère à lire immédiatement.

La première forme fait attendre le programme si aucun caractère n’est dis-ponible. La seconde retourne la valeur False dans Available si aucun caractèren’est disponible et l’exécution du programme se poursuit.

La procédure Look_Ahead permet d’obtenir une copie du caractère suivantsans éliminer l’original de la suite de caractères à lire. Un tel caractère sera doncencore présent pour une future lecture. Elle a l’en-tête:

procedure Look_Ahead ( Item : out Character;End_Of_Line : out Boolean );

avec• Item le caractère obtenu si End_Of_Line est faux;• End_Of_Line qui est vrai si la fin de ligne est atteinte. Dans ce cas Item

n’a pas de valeur définie.

Page 258: Cours Ada de A à Z

FICHIERS TEXTE 258

Finalement, il faut encore effectuer les remarques suivantes:

• Il existe deux fichiers courants, l’un ouvert en lecture et l’autre en écriture.En fait ces deux fichiers représentent par défaut le clavier (lecture) etl’écran (écriture). Des opérations comme Put et Get, sans paramètrefichier, s’appliquent à ces fichiers.

• Les entrées-sorties textuelles s’effectuent de la même manière sur desfichiers qu’avec le clavier et l’écran. Des formatages (§ 2.6.7) sont parconséquent aussi possibles sur des valeurs écrites dans les fichiers texte.

• Il existe les notions de numéros de ligne et de colonne qui ne seront pasdéveloppées ici.

• Plusieurs exceptions prédéfinies peuvent être générées lors de l’utilisationde fichiers textes ou binaires. Elles sont décrites plus loin (sect. 12.5).

Page 259: Cours Ada de A à Z

FICHIERS BINAIRES 259

12.4 FICHIERS BINAIRES

12.4.1 Généralités

Comme déjà mentionné (sect. 12.2), les fichiers binaires comprennent tous lesfichiers non textuels. Leur contenu peut être vu comme une suite de bits constituantles éléments du fichier placés les uns après les autres. Comme pour les fichierstexte, la fin du fichier se situe après le dernier élément. En Ada, le traitement desfichiers binaires s’effectue grâce aux paquetages génériques prédéfinisAda.Sequential_IO (sect. 19.3) pour un accès séquentiel (sect. 12.2) etAda.Direct_IO (sect. 19.3) pour un accès direct (sect. 12.2), paquetages danslesquels sont définis les types, sous-types, procédures, fonctions et exceptionsnécessaires. Une fois encore les notions de base pour la manipulation d’un fichierbinaire seront présentées de façon intuitive.

Un fichier se déclare comme une variable, en utilisant le type File_Type dontla structure est cachée à l’utilisateur. De tels types (privés) feront l’objet d’uneprésentation ultérieure (§ 16.2.1). Il suffit de rappeler ici qu’ils permettent essen-tiellement la déclaration de variables et de paramètres. Tout ceci est identique à lasituation rencontrée pour les fichiers texte.

Mais la manipulation d’un fichier binaire nécessite la connaissance du type deséléments du fichier. Celle-ci est obtenue grâce au fait que les deux paquetagesAda.Sequential_IO et Ada.Direct_IO sont génériques (sect. 17.2). Ils per-mettent de créer des paquetages non génériques par des déclarations mentionnantle type des éléments (exemple 12.4) et semblables à celles nécessaires pour dispo-ser des entrées-sorties sur des types numériques (sect. 6.2) ou énumératifs (§ 5.2.5).Pour un fichier séquentiel, les éléments peuvent être de n’importe quel type alorsqu’il existe une restriction pour ceux des fichiers à accès direct (§ 12.4.7).

Exemple 12.4 Déclarations de fichiers comme variables et paramètres.

with Ada.Sequential_IO; -- Pas de use sur un paquetage quiwith Ada.Direct_IO; -- est generique (sect. 17.2)-- ...procedure Exemple_12_4 is

type T_Mois_De_L_Annee is (Janvier, Fevrier, Mars, Avril, Mai,Mai, Juin, Juillet, Aout,

Septembre,Octobre, Novembre, Decembre);

type T_Jour is range 1..31;type T_Date is

recordJour : T_Jour;Mois : T_Mois_De_L_Annee;Annee : Integer;

Page 260: Cours Ada de A à Z

FICHIERS BINAIRES 260

end record;

-- Pour utiliser des fichiers binaires sequentiels d'entierspackage Entiers_Seq_IO is new Ada.Sequential_IO ( Integer );use Entiers_Seq_IO;

-- Pour utiliser des fichiers binaires sequentiels de datespackage Dates_Dir_IO is new Ada.Direct_IO ( T_Date );use Dates_Dir_IO;

-- Deux variables fichiers (prefixe necessaire, § 10.5.2)Fichier_Entiers : Entiers_Seq_IO.File_Type;Fichier_Dates : Dates_Dir_IO.File_Type;

-- Lit le fichier d'entiers Mesuresprocedure Lire (Mesures : in Entiers_Seq_IO.File_Type) isbegin

...end Lire;

-- Ecrit le fichier de dates Datesprocedure Ecrire (Dates : in Dates_Dir_IO.File_Type) isbegin

...end Ecrire;

begin -- Exemple_12_4...

La seule opération permise sur les fichiers binaires est le passage en paramètre.L’affectation est en effet interdite entre ces fichiers (car ils sont d’un type limité,sect. 16.9). Par conséquent, il est impossible de déclarer une constante d’un typefichier.

Les expressions se réduisent aux variables d’un type fichier. Comme dans le casdes tableaux l’intérêt principal des fichiers binaires réside en l’utilisation de leurséléments (§ 12.4.3 et 12.4.5).

12.4.2 Création, ouverture et fermeture d’un fichier binaire

En Ada, un fichier binaire peut toujours être lu ou écrit. Il peut encore êtrecomplété à la fin s’il est séquentiel ou à la fois lu et écrit s’il est en accès direct. Ondit que le fichier est ouvert respectivement en mode lecture, en écriture, en ad-jonction ou en lecture-écriture. La lecture (read) consiste à consulter le contenusans le changer, alors que l’écriture (write) permet de créer un nouveau contenu(en effaçant l’ancien s’il existait). L’adjonction (append) laisse le contenu présenttel quel mais permet de rajouter des informations à la fin du fichier. Finalement lalecture-écriture (read-write) permet de lire, de modifier ou d’ajouter desinformations n’importe où dans le fichier. Comme expliqué pour les fichiers texte,l’ouverture (open) d’un fichier binaire consiste entre autres à associer une variablefichier à un fichier externe et à choisir le mode d’utilisation (accès séquentiel ou

Page 261: Cours Ada de A à Z

FICHIERS BINAIRES 261

direct, lecture, écriture, adjonction, lecture-écriture). Ceci se fait par la procédureCreate si le fichier est nouveau ou par Open s’il existe déjà. Ces deux procéduresont les en-têtes (semblables):

procedure Create ( File : in out File_Type;Mode : in File_Mode := ...;Name : in String := "";Form : in String := "" );

procedure Open ( File : in out File_Type;Mode : in File_Mode;Name : in String;Form : in String := "" );

avec• File le fichier créé s’il est nouveau ou simplement ouvert s’il existait déjà;• Mode le mode d’utilisation du fichier; la valeur par défaut est Out_File

pour Ada.Sequential_IO et Inout_File pour Ada.Direct_IO lorsde la création;

• File_Mode un type énumératif fournissant les valeurs In_File (lecture),Out_File (écriture) et Append_File (adjonction) s’il est déclaré dansAda.Sequential_IO et In_File (lecture), Inout_File (lecture-écriture) et Out_File (écriture) s’il est déclaré dans Ada.Direct_IO;

• Name le nom du fichier externe désigné par File;• Form un paramètre permettant de fixer des propriétés du fichier externe.

La valeur par défaut du paramètre Name de Create permet la création d’unfichier temporaire qui sera automatiquement effacé à la fin du programme alors quecelle du paramètre Form permet l’utilisation des options courantes de l’implé-mentation, souvent celles du système d’exploitation utilisé.

La fermeture (close) d’un fichier binaire, comme pour un fichier texte, consisteen la suppression de l’association (réalisée à l’ouverture) entre un fichier et unfichier externe. Elle est effectuée par la procédure Close:

procedure Close ( File : in out File_Type );

avec• File le fichier qui doit être fermé.

C’est au plus tard lors de la fermeture d’un fichier que son contenu est modifiéen fonction des opérations réalisées sur le fichier.

12.4.3 Accès aux éléments d’un fichier binaire séquentiel

Les procédures Read et Write sont déclarées dans Ada.Sequential_IO ets’utilisent pour la lecture et l’écriture séquentielles de données du type deséléments du fichier. Elles ont les en-têtes:

procedure Read ( File : in File_Type; Item : out Element_Type );

Page 262: Cours Ada de A à Z

FICHIERS BINAIRES 262

procedure Write ( File : in File_Type; Item : in Element_Type );

avec• File le fichier séquentiel;• Item l’élément lu ou écrit.

Exemple 12.5 Lecture et écriture dans un fichier binaire séquentiel.

with Ada.Sequential_IO;

-- ...procedure Exemple_12_5 is

-- Pour utiliser des fichiers binaires sequentiels formes de-- dates, les types sont declares comme dans l'exemple 12.4package Dates_Seq_IO is new Ada.Sequential_IO ( T_Date );use Dates_Seq_IO;

Fichier_Dates : File_Type; -- Une variable fichier binaire

Date : T_Date; -- Une date reelle

begin -- Exemple_12_5

-- Creation et ouverture du fichier a ecrireCreate ( File => Fichier_Dates, Name => "dates.dat" );

Date := (1, Avril, 1958);Write ( Fichier_Dates, Date ); -- Ecriture de quelques datesWrite ( Fichier_Dates, (26, Decembre, 1964) );Write ( Fichier_Dates, (7, Septembre, 1991) );Write ( Fichier_Dates, (28, Juillet, 1998) );

Close ( Fichier_Dates ); -- Fermeture du fichier

-- Ouverture du même fichier pour le relireOpen ( File => Fichier_Dates, Mode => In_File,

Name => "dates.dat" );

Read ( Fichier_Dates, Date ); -- Lecture des quatre datesRead ( Fichier_Dates, Date ); -- dans l'ordre selon lequelRead ( Fichier_Dates, Date ); -- elles viennent d'etreRead ( Fichier_Dates, Date ); -- ecrites

Close ( Fichier_Dates ); -- Fermeture du fichier...

L’exemple 12.5 illustre simplement l’utilisation des procédures Read et Write.Un traitement moins trivial d’un fichier binaire séquentiel est donné ci-après.

12.4.4 Traitement d’un fichier binaire séquentiel

Le traitement d’un fichier binaire séquentiel au sens le plus général consiste enla lecture, l’utilisation du contenu et l’écriture d’un (nouveau) fichier binaire. Lefichier se termine ici aussi après le dernier élément (fin du fichier). Une lecturecorrecte doit tenir compte de la fin du fichier afin de ne pas essayer de lire au-delà,

Page 263: Cours Ada de A à Z

FICHIERS BINAIRES 263

ce qui provoquerait une erreur. Pour cela le programmeur peut utiliser la fonctionbooléenne End_Of_File définie dans Ada.Sequential_IO. Elle a l’en-tête:

function End_Of_File ( File : in File_Type ) return Boolean;

avec• File le fichier traité, ouvert en lecture.

La fonction End_Of_File retourne la valeur True si la fin de fichier estatteinte, False sinon. Pour illustrer l’utilisation de cette fonction, l’exemple 12.6présente un algorithme typique de traitement de fichiers binaires séquentiels et lafigure 12.3 montre le résultat de cet algorithme.

Exemple 12.6 Copie d’un fichier binaire séquentiel.

with Ada.Sequential_IO;-- ...procedure Exemple_12_6 is

-- Pour utiliser des fichiers binaires sequentiels formes de-- dates, les types sont declares comme dans l'exemple 12.4package Dates_Seq_IO is new Ada.Sequential_IO ( T_Date );use Dates_Seq_IO;

Original : File_Type; -- Deux variables fichiers binairesCopie : File_Type; -- sequentiels

Date : T_Date; -- Une variable pour la copie d'un element

begin -- Exemple_12_6

-- Ouverture du fichier a lire et creation de la copieOpen ( File => Original, Mode => In_File,

Name => "dates.dat" );Create ( File => Copie, Name => "copie de dates.dat" );

while not End_Of_File ( Original ) loop -- Fin de fichier?Read ( Original, Date ); -- Lecture d'une dateWrite ( Copie, Date ); -- Ecriture d'une date

end loop;

Close ( Original ); -- Fermeture des fichiersClose ( Copie );...

Figure 12.3 Suite des dates copiées par le programme de l’exemple 12.6.

1 Avril 195826 Decembre 19647 Septembre 1991

Page 264: Cours Ada de A à Z

FICHIERS BINAIRES 264

28 Juillet 1998

Lors de l’écriture dans un fichier binaire, c’est naturellement la fermeture dufichier qui provoque la fin du fichier par l’intermédiaire du système d’exploitation.

12.4.5 Accès aux éléments d’un fichier binaire à accès direct

Des procédures Read et Write sont aussi déclarées dans Ada.Direct_IO ets’utilisent pour la lecture et l’écriture de données du type des éléments du fichier.Ces opérations peuvent être séquentielles mais aussi, et c’est l’intérêt de cet accès,s’effectuer en fonction de la valeur d’un index. Cet index représente un nombreentier entre 1 et une borne maximale imposée par l’implémentation; il est du sous-type Positive_Count construit sur le type de base Count. Toutes ces déclara-tions appartiennent à Ada.Direct_IO et sont définies ainsi:

type Count is range 0 .. implementation_defined;subtype Positive_Count is Count range 1 .. Count'Last;

procedure Read ( File : in File_Type; Item : out Element_Type );procedure Read ( File : in File_Type; Item : out Element_Type;

From : in Positive_Count );procedure Write ( File : in File_Type; Item : in Element_Type

);procedure Write ( File : in File_Type; Item : in Element_Type;

To : in Positive_Count );

avec• File le fichier à accès direct;• Item l’élément lu ou écrit;• From la valeur de l’index, c’est-à-dire la position de l’élément à lire;• To la valeur de l’index, c’est-à-dire la position d’écriture de l’élément.

Dans tous les cas, l’index est augmenté de 1 après une lecture ou une écriture.Comme la valeur de l’index est arbitraire, il est possible d’effectuer des lectures oudes écritures n’importe où dans un fichier à accès direct! De plus, l’index peut êtremodifié sans passer par Read ou Write en utilisant la procédure Set_Index ouconsulté par la fonction Index. Elles ont les en-têtes:

procedure Set_Index ( File : in File_Type;To : in Positive_Count );

function Index ( File : in File_Type ) return Positive_Count;

avec• File le fichier à accès direct;• To la nouvelle valeur de l’index. Cette valeur peut dépasser la position du

dernier élément du fichier (§ 12.4.7)!

Page 265: Cours Ada de A à Z

FICHIERS BINAIRES 265

12.4.6 Traitement d’un fichier binaire à accès direct

Toutes les notions présentées pour le traitement des fichiers binaires séquentielssont identiques pour les fichiers à accès direct. L’exemple 12.7 et la figure 12.4 lesillustrent en présentant l’algorithme de copie de l’exemple 12.6 modifié de manièreà ce que la copie contienne les éléments de l’original mais dans l’ordre inverse. Lafonction Size (§ 12.4.7) qui donne le nombre d’éléments d’un fichier est utiliséedans cet exemple.

Exemple 12.7 Copie d’un fichier binaire à accès direct en inversant l’ordre des éléments.

with Ada.Direct_IO;-- ...

procedure Exemple_12_7 is

-- Pour utiliser des fichiers binaires a acces direct formes-- de dates, les types sont declares comme dans l'exemple 12.4package Dates_Dir_IO is new Ada.Direct_IO ( T_Date );use Dates_Dir_IO;

-- Deux variables fichiers a acces directOriginal : File_Type;Copie : File_Type;

Date : T_Date; -- Une variable pour la copie d'un element

begin -- Exemple_12_7

-- Ouverture du fichier a lire et creation de la copieOpen ( File => Original, Mode => In_File,

Name => "dates.dat" );

Create ( File => Copie, Name => "copie de dates.dat" );

-- Copier tous les elementsfor No_Element_Courant in reverse 1 .. Size( Original ) loop

-- Placer l'index sur l'element No_Element_CourantSet_Index ( Original, No_Element_Courant );

Read ( Original, Date ); -- Lecture d'une dateWrite ( Copie, Date ); -- Ecriture d'une date

end loop;

Close ( Original ); -- Fermeture des fichiersClose ( Copie );...

Page 266: Cours Ada de A à Z

FICHIERS BINAIRES 266

Figure 12.4 Lecture et écriture des dates par le programme de l’exemple 12.7.

La fin de la copie se produit lorsque le premier élément du fichier Original aété lu. Il faut noter que la lecture s'effectue en fonction de l'index alors que l'écritureest purement séquentielle.

12.4.7 Compléments sur l’utilisation des fichiers binaires à accès direct

Lorsqu’une déclaration telle quepackage Dates_Dir_IO is new Ada.Direct_IO ( T_Dates );

est effectuée, il faut que le type mentionné, ici T_Dates, ne soit pas un type tableaunon contraint (§ 8.2.3) ni un type article à discriminants sans valeurs par défaut(sect. 11.3). En fait, cette restriction est la même que pour le type des éléments d’un

Après l’ouverture de dates.dat et la création de copie de dates.dat:1 avril 195826 decembre 19647 septembre 199128 juillet 1998

Index de dates.datIndex de copie de dates.dat

Après la première itération:

Index de dates.dIndex de copie de dates.dat

28 juillet 1998

Après la deuxième itération:

28 juillet 19987 septembre 1991

Et ainsi de suite pour les deux dernières itérations.

1 avril 195826 decembre 19647 septembre 199128 juillet 1998

1 avril 195826 decembre 19647 septembre 199128 juillet 1998

Après la première instruction Set_Index( Original, No_Element_Couran

Index de dates.dat

1 avril 195826 decembre 19647 septembre 199128 juillet 1998

Index de dates.dat

Index de copie de dates.dat

Page 267: Cours Ada de A à Z

FICHIERS BINAIRES 267

tableau (§ 8.2.8). De plus, il existe la fonction Size qui a l’en-tête:function Size ( File : in File_Type ) return Count;

Elle retourne le nombre d’éléments du fichier à accès direct File.

Comme le positionnement de l’index après la fin du fichier est possible, à cetendroit une lecture lèvera l’exception End_Error (sect. 12.5) alors qu’une écriturecréera un élément bien constitué. De plus, entre celui-ci et l’élément qui était ledernier, le fichier est complété par des éléments de contenu non défini. Le fichierne peut de ce fait jamais contenir de trous. Un fichier à accès direct ressemble doncà un tableau dont on peut augmenter la taille.

12.4.8 Compléments sur l’utilisation des fichiers

Pour n’importe laquelle des trois catégories de fichiers, le paquetagecorrespondant founit d’autres outils comme les procédures Delete et Reset ou lesfonctions Is_Open, Mode et Name. La procédure Delete ferme le fichier etl’efface de la mémoire secondaire. Elle possède l’en-tête:

procedure Delete ( File : in out File_Type );

avec• File le fichier à effacer.

La procédure Reset positionne («rembobine») le fichier de manière à ce que lalecture recommence au premier élément pour les modes lecture et lecture-écriture,ou que l’écriture se fasse au début pour les modes lecture-écriture et écriture, oufinalement que l’écriture reprenne après le dernier élément pour le modeadjonction. Elle possède deux formes:

procedure Reset ( File : in out File_Type );

procedure Reset ( File : in out File_Type; Mode : in File_Mode);

avec• File le fichier à repositionner;• Mode le nouveau mode du fichier.

La deuxième forme permet de changer le mode du fichier, par exemple pourrelire les éléments d’un fichier qui viennent d’être écrits. Elle aurait donc pu êtreutilisée dans les exemples 12.2 et 12.5. Par ailleurs, pour un fichier à accès direct,Reset a aussi pour effet de positionner l’index à la valeur 1.

La fonction Is_Open est vraie si le fichier File est ouvert, fausse sinon. Ellea l’en-tête:

function Is_Open ( File : in File_Type ) return Boolean;

La fonction Mode redonne le mode d’ouverture du fichier File. Elle possèdel’en-tête:

function Mode ( File : in File_Type ) return File_Mode;

Page 268: Cours Ada de A à Z

FICHIERS BINAIRES 268

La fonction Name fournit le nom du fichier externe connecté au fichier File.Elle a l’en-tête:

function Name ( File : in File_Type ) return String;

Il existe encore d’autres opérations qui ne seront pas abordées dans cet ouvrage.

Page 269: Cours Ada de A à Z

EXCEPTIONS LORS DE L’UTILISATION DES FICHIERS 269

12.5 EXCEPTIONS LORS DE L’UTILISATION DES FICHIERS

Toutes les opérations de traitement des fichiers ou de leurs éléments peuventgénérer des exceptions. Celles-ci sont toutes déclarées dans le paquetageAda.IO_Exceptions et surnommées (sect. 19.1) dans tous les paquetagesd’entrées-sorties. Cela signifie que leurs identificateurs sont utilisables de la mêmemanière que tous ceux déclarés dans Ada.Text_IO, Ada.Sequential_IO ouencore Ada.Direct_IO. Le bref résumé qui suit [BAR 97] donne une idéegénérale des circonstances provoquant la levée de ces exceptions:

Status_Errorle fichier est ouvert alors qu’il devrait être fermé ou vice-versa;Mode_Errorle fichier n’a pas le bon mode, par exemple In_File au lieu deOut_File;Name_Errorune erreur est apparue dans le nom externe de fichier passé enparamètre de Create ou Open;Use_Errordiverses raisons (!); paramètre Form inacceptable, ordred’impression sur un périphérique d’entrée, etc.;Device_Errorle dispositif physique est en panne ou n’est pas connecté;End_Errortentative de lecture au-delà de la fin de fichier;Data_ErrorRead ou Get ne peut pas interpréter les données comme valeursdu type désiré;Layout_Errorun problème de formatage dans Ada.Text_IO, ou bien Putécrit trop de caractères dans une chaîne.

Le manuel de référence [ARM A.6-A.13] donne, pour chaque opérationd’entrée-sortie, les exceptions levées en fonction de l’erreur commise.

Page 270: Cours Ada de A à Z

270

12.6 AUTRES PAQUETAGES D’ENTRÉES-SORTIES

Il faut encore mentionner, à titre informatif et sans approfondissement,l’existence de paquetages très spécifiques comme Ada.Streams.Stream_IO etAda.Storage_IO. Le paquetage Ada.Streams.Stream_IO permet de traiterséquentiellement des fichiers hétérogènes considérés comme des flots, c’est-à-direcomme des suites d’octets. Ada.Storage_IO permet, lui, les lectures et écrituresdirectes en mémoire.

Page 271: Cours Ada de A à Z

EXERCICES 271

12.7 EXERCICES

12.7.1 Traitement d’un fichier texte

Reprendre les exercices 9.4.2 à 9.4.4 et adapter les solutions de manière à lireles lignes dans un fichier texte.

12.7.2 Majuscules et minuscules

Modifier (si nécessaire) la casse des caractères d’un fichier texte de manière àrespecter les règles habituelles en présence de symboles de ponctuation. Onsuppose qu’il n’existe pas de noms propres dans le texte.

12.7.3 Dates et fichier texte

Soit un fichier texte formé de lignes contenant chacune une date (numéro dejour, nom du mois, année). Récrire le fichier en faisant précéder chaque date par lenom du jour (on peut partiellement utiliser la solution de l’exercice 8.6.9).

12.7.4 Justification d’un fichier texte

Reprendre l’exercice 9.4.5 et adapter la solution de manière à justifier toutes leslignes d’un fichier texte.

12.7.5 Fiches personnelles et fichier binaire

Adapter la solution de l’exercice 11.5.5 (ou des exercices 10.9.1 et 10.9.2) demanière à utiliser un fichier binaire (séquentiel ou à accès direct) pour lire et écrireles fiches.

12.7.6 Existence d’un fichier

Ecrire une fonction Is_Created qui sera vraie si le fichier externe (dont lenom est passé en paramètre) existe, fausse sinon. Indication: ouvrir le fichier ettraiter l’exception Name_Error.

Page 272: Cours Ada de A à Z

POINTS À RELEVER 272

12.8 POINTS À RELEVER

12.8.1 En général

• Les fichiers servent à conserver l’information, essentiellement sur supportexterne.

• Les fichiers se répartissent en deux catégories: les fichiers binaires et lesfichiers texte (appelés aussi fichiers ASCII).

• Les accès séquentiel, direct et selon une clé sont les accès aux fichiers lesplus courants.

• La création, l’ouverture, la lecture, l’écriture et la fermeture sont desopérations communes à tous les fichiers.

12.8.2 En Ada

• L’accès aux fichiers texte s’effectue par le paquetage Ada.Text_IO.

• Il ne faut jamais dépasser la fin d’un fichier texte sinon l’exceptionEnd_Error sera levée.

• L’accès aux fichiers binaires s’effectue par les paquetagesAda.Sequential_IO et Ada.Direct_IO mais cet accès nécessite uneou plusieurs déclarations spéciales.

• L’adjonction est une opération supplémentaire pour les fichiers binairesséquentiels.

• La lecture-écriture est une opération supplémentaire pour les fichiersbinaires à accès direct.

• Le paquetage Ada.IO_Exceptions regroupe les exceptions en relationavec le traitement des fichiers.

Page 273: Cours Ada de A à Z

273

C H A P I T R E 1 3

C R É A T I ON E T T R A I T E M

Page 274: Cours Ada de A à Z

274 CRÉATION ET TRAITEMENT D’EXCEPTIONS

Page 275: Cours Ada de A à Z

RAPPELS ET MOTIVATION 275

13.1 RAPPELS ET MOTIVATION

La notion d’exception a été abordée relativement tôt (sect. 6.3) afin d’expliquerles phénomènes issus d’erreurs lors de l’exécution d’un programme. Les conceptsde levée (automatique), de propagation et de traitement d’exceptions ont étéprésentés en détails. On rappellera simplement que:

• une quelconque erreur d’exécution provoquera la levée d’une exception quiinterrompra le cours normal du déroulement du programme;

• une exception peut être traitée dans un traite-exception placé après ladernière instruction d’un sous-programme, d’un bloc, d’un code d’initia-lisation de paquetage, d’une tâche, d’un corps d’entrée ou d’une instruc-tion accept; ces trois derniers cas sont mentionnés à titre indicatif puis-que n’appartenant pas à la matière traitée dans cet ouvrage;

• si une exception n’est pas traitée et supprimée par un traite-exception, elleest propagée à l’appelant et levée à nouveau au point d’appel.

L’intérêt de cette notion réside en la possibilité de prévoir et de programmer destraitements aboutissant au rétablissement de la situation en cas d’erreur ou, aumoins, explicitant les causes de l’apparition de telles erreurs.

Or, compter sur des exceptions prédéfinies (§ 6.3.2 et sect. 12.5) pour détecteret réagir à des situations exceptionnelles mais connues, est une mauvaise pratiqueen programmation. En effet, comment s’assurer que la levée d’une telle exceptionest provoquée par une situation prévue et non par autre chose qui s’est mal passé?

Une bonne habitude consiste à traiter les situations exceptionnelles prévisiblesen créant ses propres exceptions et en les levant lorsque c’est nécessaire. Enécrivant des traite-exceptions appropriés pour ces exceptions, il est alors possiblede réagir précisément et de manière adéquate en fonction de l’erreur survenue.

Page 276: Cours Ada de A à Z

DÉCLARATION D’UNE EXCEPTION 276

13.2 DÉCLARATION D’UNE EXCEPTION

Une exception peut être déclarée (exemple 13.1) dans n’importe quelle partiedéclarative sous la forme générale suivante:

suite_d_identificateurs : exception;

avec• la suite_d_identificateurs formée d’un ou de plusieurs identi-

ficateurs d’exceptions séparés par des virgules;• exception le mot réservé utilisé ici pour donner la nature de l’identifi-

cateur.

Les règles de visibilité sont les mêmes pour une exception que pour n’importequelle constante ou variable.

Exemple 13.1 Déclaration d’une exception.

-- Ce paquetage permet le calcul avec les nombres rationnels.-- La specification est completee par la declaration d'une-- exception

package Nombres_Rationnels is

type T_Rationnel is -- Le type d'un nombre rationnelrecord

Numerateur : Integer;Denominateur : Positive; -- Le signe est au numerateur

end record;

-- Le nombre rationnel 0Zero : constant T_Rationnel := (0, 1);

-- Exception levee si division par 0Division_Par_0 : exception;

------------------------------------------------------------

... -- Autres declarations (sect. 10.7)

end Nombres_Rationnels;

Page 277: Cours Ada de A à Z

LEVÉE ET PROPAGATION D’UNE EXCEPTION 277

13.3 LEVÉE ET PROPAGATION D’UNE EXCEPTION

Si la propagation (§ 6.3.3) s’effectue de manière identique pour n’importequelle exception (prédéfinie ou non), la levée d’une exception non prédéfinie nepeut pas avoir lieu automatiquement mais doit être effectuée par une instructionparticulière, l’instruction raise. Sa forme générale est:

raise nom_d_exception;

où• nom_d_exception désigne l’exception levée.

L’exécution de cette instruction termine la suite d’instructions en cours etprovoque la levée de l’exception mentionnée. N’importe quelle exception visiblepeut être levée par l’instruction raise. L’emploi de cette instruction est illustrédans l’exemple 13.2.

Exemple 13.2 Levée explicite d’une exception par l’instruction raise.

-- Ce paquetage permet le calcul avec les nombres rationnels. Le-- corps de l'operateur de division est complete pour lever une-- exception en cas de division par 0

package body Nombres_Rationnels is

... -- Premiers operateurs (sect. 10.7)

-- Division de deux nombres rationnels. Le resultat est un-- nombre rationnel irreductiblefunction "/" ( X, Y : T_Rationnel ) return T_Rationnel isbegin -- "/"

if Y.Numerateur > 0 then -- Diviseur positif

return Irreductible( X.Numerateur * Y.Denominateur,X.Denominateur * Y.Numerateur );

elsif Y.Numerateur < 0 then -- Changer de signe

return Irreductible( X.Numerateur * (–Y.Denominateur),

X.Denominateur * (–Y.Numerateur) );

else -- Division par zero!

raise Division_Par_0; -- Lever l'exception

end if;

end "/";

------------------------------------------------------------

... -- Autres operations (sect. 10.7)

end Nombres_Rationnels;

-- Ce paquetage complete le calcul avec les nombres rationnels. -- Le corps de l'operateur de division est complete pour lever une

Page 278: Cours Ada de A à Z

LEVÉE ET PROPAGATION D’UNE EXCEPTION 278

-- exception en cas de division par 0package body Nombres_Rationnels.Utilitaires is

... -- Premiers operateurs (§ 10.8.1)

-------------------------------------------------------------- Division d'un nombre rationnel par un nombre entierfunction "/" ( X : T_Rationnel;

N : Integer ) return T_Rationnel isbegin

if N > 0 then -- Diviseur positif

return ( X.Numerateur, N * X.Denominateur );

elsif N < 0 then -- Le signe au numerateur

return ( – X.Numerateur, abs N * X.Denominateur );

else -- Division par zero!

raise Division_Par_0; -- Lever l'exception

end if;

end "/";

------------------------------------------------------------

... -- Autres operations (§ 10.8.1)

end Nombres_Rationnels.Utilitaires;

L’exemple 13.2 illustre parfaitement l’utilité des exceptions: que doit retournerune fonction de division si le diviseur est nul? L’exception levée dans une tellefonction ne peut être traitée que par l’appelant. C’est parfaitement cohérent puisquec’est l’appelant qui a transmis le diviseur nul à la fonction, à lui de rétablir lasituation s’il le peut!

Un cas intéressant est représenté par la propagation d’une exception hors de saportée (sect. 4.4), par exemple si elle est propagée hors d’une procédure où elleétait (maladroitement) déclarée. Lors de la sortie de sa portée, l’exception continued’exister mais perd son identificateur et devient anonyme; la seule façon de latraiter est d’utiliser le choix others dans un traite-exception! Inutile de préciserque ce genre de cas est à éviter absolument.

Page 279: Cours Ada de A à Z

TRAITEMENT D’UNE EXCEPTION 279

13.4 TRAITEMENT D’UNE EXCEPTION

Le traitement d’une exception est identique pour n’importe quelle exception.Mais la présentation des notions de base (§ 6.3.4) va être complétée par quelquesaspects pratiques.

Tout d’abord il existe la forme simplifiée raise; sans mention explicited’exception. Cette forme ne peut être utilisée que dans un traite-exception et sert àlever à nouveau l’exception dont le traitement est en cours. Cela permet de passerpar le traite-exception, par exemple pour afficher un message ou pour quitter leplus correctement possible la structure actuelle, et de laisser l’exception sepropager. L’exemple 13.3 présente cette situation.

Exemple 13.3 Utilisation de l’instruction raise sans mention explicite d’exception.

...

exception

when Constraint_Error =>

... -- Preparer la sortie de la structure actuelle

raise; -- Lever Constraint_Error a nouveau pour la propager

-- On suppose que E1 et E2 sont deux exceptionswhen E1 | E2 =>

Put_Line ("Exception levee dans cette structure");

raise; -- Lever l'exception qui a provoque l'arrivee dans le-- traite-exception, c'est-a-dire E1 ou E2

end ...;

Le paquetage Ada.Exceptions [ARM 11.4.1], à ne pas confondre avecAda.IO_Exceptions, permet d’obtenir des précisions concernant l’exception encours grâce à trois fonctions qui retournent chacune une chaîne de caractère de typeString.

Ces trois fonctions sont les suivantes:

• Exception_Name, qui rend le nom de l’exception courante;• Exception_Message, qui produit un message d’une ligne en relation

avec l’exception;• Exception_Information, qui fournit le nom de l’exception, le message

et d’autres informations.

Le message et les autres informations dépendent de l’implémentation et doiventservir à identifier la cause et l’endroit de la levée de l’exception. Ces trois fonctionsnécessitent chacune un paramètre permettant d’accéder à l’exception. Sans expli-quer complètement le mécanisme, il suffit de dire qu’une branche d’un traite-

Page 280: Cours Ada de A à Z

TRAITEMENT D’UNE EXCEPTION 280

exception peut comporter une sorte de paramètre qui contiendra l’identité del’exception survenue et qui pourra être transmis aux fonctions. De cette manière,un traite-exception peut produire des traces (exemple 13.4), c’est-à-dire desinformations textuelles que le programmeur va utiliser pour comprendre les raisonsde l’exception. Une telle branche va toujours comporter un choix (§ 6.3.4) maiscomplété de la manière suivante:

when identificateur : choix => traitement;-- branche avec parametrewhen identificateur : others => traitement;-- branche others avec parametre

L’identificateur représente le paramètre et contiendra l’identité de l’exceptionqui a provoqué l’arrivée dans la branche. Sa portée se limite à sa branche dedéclaration.

Exemple 13.4 Production de traces dans un traite-exception

with Ada.Exceptions;...exception

when Constraint_Error => -- Comme pour l'exemple 13.3

... -- Preparer la sortie de la structure actuelle

raise; -- Lever Constraint_Error a nouveau pour la propager

-- On suppose que E1 et E2 sont deux exceptionswhen Erreur : E1 | E2 => -- Erreur est implicitement

-- declaree ici

-- Utilisation de la fonction Exception_NamePut ("Exception ");Put ( Ada.Exceptions.Exception_Name(Erreur) );Put_Line (" levee dans cette structure");

-- Utilisation de la fonction Exception_MessagePut_Line ("Informations supplementaires:");Put_Line ( Ada.Exceptions.Exception_Message(Erreur) );

-- Utilisation de la fonction Exception_InformationPut_Line ("Informations completes:");Put_Line ( Ada.Exceptions.Exception_Information(Erreur) );

raise; -- Lever l'exception qui a provoque l'arrivee dans le-- traite-exception, c'est-a-dire E1 ou E2

end ...;

Page 281: Cours Ada de A à Z

281

13.5 COMPLÉMENTS

Le code d’initialisation d’un paquetage peut comporter un traite-exception.Celui-ci pourra traiter les exceptions levées dans ce code, mais en aucun cas cellesprovoquées par l’un des corps contenus dans le corps du paquetage.

Une exception déclarée dans un sous-programme récursif n’est pas recréée àchaque appel récursif mais existe en un seul exemplaire.

Les exceptions sont des entités spéciales car il est impossible de les manipulercomme les autres objets d’un programme Ada. Les seules opérations possibles sontcelles décrites dans ce chapitre et à la section 6.3.

Page 282: Cours Ada de A à Z

EXERCICES 282

13.6 EXERCICES

13.6.1 Paramètre incorrectReprendre le sous-programme de l’exercice 8.6.4 et lever une exception dans le

cas où le tableau contenant les nombres réels et passé en paramètre est vide. Faut-il déclarer l’exception dans le sous-programme ou à l’extérieur de celui-ci? Uneexception prédéfinie fait-elle l’affaire?

13.6.2 Carrés latins

Reprendre le sous-programme de l’exercice 8.6.7 et lever une exception pour lecas où une ligne ou une colonne contient un nombre hors de l’intervalle 1..n. Faut-il déclarer l’exception dans le sous-programme ou à l’extérieur de celui-ci? Uneexception prédéfinie fait-elle l’affaire?

13.6.3 Division par zéroReprendre le paquetage de gestion de nombres complexes (exercices 10.9.1 et

10.9.2) et introduire une exception pour le cas de division par zéro. Introduire cecas dans le programme de test correspondant (exercice 10.9.3).

13.6.4 Multiplication par zéroReprendre le paquetage enfant de gestion de vecteurs (exercice 10.9.6) et

introduire une exception pour le cas de produit scalaire avec un vecteur nul.Introduire ce cas dans le programme de test correspondant (exercice 10.9.3).

Page 283: Cours Ada de A à Z

POINTS À RELEVER 283

13.7 POINTS À RELEVER

13.7.1 En général

• La déclaration et le traitement d’exceptions se retrouvent dans d’autreslangages de programmation, par exemple C++ et Java.

13.7.2 En Ada

• Le programmeur peut déclarer et traiter ses propres exceptions.

• L’instruction raise permet de lever explicitement n’importe quelle ex-ception.

• Un traite-exception peut comprendre l’utilisation de fonctions exportées dupaquetage Ada.Exceptions afin de produire de l’information sur l’ex-ception en cours.

Page 284: Cours Ada de A à Z

284

C H A P I T R E 1 4

S T R U C T UR E S L I N É A I RE S

Page 285: Cours Ada de A à Z

285 STRUCTURES LINÉAIRES SIMPLES ET STATIQUES

Page 286: Cours Ada de A à Z

MOTIVATION 286

14.1 MOTIVATION

L’étude des structures linéaires simples comme les files, listes, piles et queues,appartient traditionnellement aux bases de l’analyse et de la programmation et cecipour de multiples raisons parmi lesquelles:

• elles font partie des structures de données (data structures) simples;• elles impliquent l’étude d’algorithmes de recherche (searching);• sous leur forme dynamique, elles impliquent l’utilisation des pointeurs

(pointers).

On appelle structure de données un support organisé permettant la repré-sentation en mémoire de données simples ou complexes. L’étude de ces structuresainsi que des algorithmes associés (recherche, tri, parcours, etc.) forme l’une desmatières les plus fondamentales dans la formation en programmation. A ce titre, cechapitre se veut également une introduction à ces notions. Les articles et lestableaux sont des structures de données linéaires simples. Ils peuvent d’ailleursaussi servir à implémenter certaines autres structures linéaires dans leur formestatique (§ 14.4.1 et 14.5.1).

La présence de ce chapitre se justifie comme préparation au chapitre suivantconsacré aux pointeurs. Même s’il n’apporte aucun nouveau mécanisme Ada, ilcontient des situations typiques d’utilisation des tableaux et permet de comprendreles notions de listes, queues et piles indépendamment des pointeurs. Le lecteur,après présentation de ces concepts, peut parfaitement sauter ce chapitre dès lasection 14.3 et continuer sa lecture au chapitre 15. Cependant, la définition d’unepile statique (§ 14.5.2) sera utilisée à la section 17.3 et celle d’une queue statique(§ 14.4.2) dans le chapitre 18.

Page 287: Cours Ada de A à Z

LISTES, FILES 287

14.2 LISTES, FILES

14.2.1 Généralités

Les listes (lists) ou files (les deux termes sont synonymes) sont formées d’élé-ments successifs, classés ou non mais toujours ordonnés, dont le nombre peut êtreborné ou illimité. Une file d’attente devant un guichet ou la liste de passage à unexamen en sont de bons exemples. Pour la suite le terme liste sera utilisé car c’estle plus répandu des deux. En informatique, une liste sert principalement à représen-ter une collection d’informations qui peuvent ou doivent se suivre. Des adressespostales peuvent constituer une liste si on les considère une par une; ces mêmesadresses classées par ordre alphabétique du nom de famille forment une liste ditetriée. Si le nombre maximal d’adresses est connu, cette liste pourra être implantéesous une forme statique par un tableau. Sinon l’usage des pointeurs conduira à uneversion dynamique. Les termes statique, respectivement dynamique dénotent uneentité connue à la compilation, respectivement seulement à l’exécution (sect. 3.10).

On appelle tête (head), respectivement queue (tail) le premier, respectivementle dernier élément d’une liste. Comme les éléments sont toujours ordonnés,l’élément suivant (next element) et l’élément précédent (previous element) d’unélément donné sont définis (sauf pour la tête et la queue). L’élément considéré(traité) à un moment donné est appelé élément courant (current element).

Comme pour les tableaux le nombre d’éléments s’appelle longueur (length) dela liste. Finalement, une liste vide (empty) est une liste ne comportant aucunélément. Cette dernière définition n’est pas sans importance car une liste vide doitsouvent être traitée de manière particulière pour éviter l’apparition d’erreurs àl’exécution des programmes!

Manipuler des listes consiste plus précisément à:

• insérer (insert) une information, c’est-à-dire la rajouter à la liste;• supprimer (delete) ou extraire (extract) une information, l’éliminer de la

liste;• modifier (modify) un élément, changer l’information présente;• consulter (get) un élément, c’est-à-dire lire l’information présente;• savoir si la liste est vide (empty);• rechercher (search) une information particulière, savoir si elle fait partie

de la liste;• parcourir (traverse) la liste, c’est-à-dire effectuer un traitement sur chacun

de ses éléments.

Ces opérations sont les plus simples et forment la base du traitement des listesmême si cette énumération est loin d’être exhaustive. A noter que l’endroit où unélément sera rajouté ou supprimé joue un rôle important dans la manipulation decertaines listes.

Page 288: Cours Ada de A à Z

LISTES, FILES 288

14.2.2 Gestion, manipulations de base

Comment réaliser les opérations sur les listes? La recherche et le parcours neposent pas de problèmes particuliers; la recherche consiste à retrouver un élément(éventuellement plusieurs) dont on connaît une partie de l’information et leparcours effectue un traitement sur tous les éléments. Par contre, l’insertion, lasuppression, la modification et la consultation nécessitent la mention de la positionoù va s’effectuer l’opération. Or rien n’est défini dans le cas général d’une liste;l’insertion par exemple pourrait avoir lieu en tête, en queue, au «milieu», selon uncritère comme l’ordre lexicographique sur une partie de l’information, etc.

Le choix systématique de la même position particulière pour chacune de cesquatre opérations conduit à la gestion de listes caractérisées par un nom typique.En effet:

• si l’insertion se passe en queue et la suppression en tête (ou inversement),alors la liste s’appelle une queue (queue); cela conduit au fait que lepremier élément mis en queue en sera le premier extrait; cette gestion estconnue sous l’adage «premier arrivé, premier servi» (first in, first outabrégé FIFO) et possède donc la propriété de conserver l’ordre deséléments placés et retirés d’une queue;

• si l’insertion et la suppression ont lieu toutes les deux en tête (ou en queue),alors la liste s’appelle une pile (stack); le premier élément empilé sera ledernier désempilé; l’adage «dernier arrivé, premier servi» (last in, first outabrégé LIFO) s’applique à ce type de gestion;

• si l’insertion s’effectue selon un critère d’ordre et la suppression en tête,alors la liste s’appelle une queue de priorité (priority queue).

La queue correspond donc à la situation d’attente devant un guichet; lapersonne située en tête de la queue la quitte après avoir dialogué avec l’employé(e)au guichet; les personnes qui arrivent se placent à la fin de la queue. Le terme queuepossède maintenant deux significations: il peut désigner une liste gérée de cettemanière ou simplement le dernier élément d’une liste quelconque.

Une pile se manipule comme une pile d’assiettes qui sont prises et remisestoujours en tête, tout en haut des assiettes restantes. C’est pour cette raison quel’accès à une pile se fait au même endroit appelé communément sommet (top). Laqueue d’une pile ne joue aucun rôle spécial dans ce type de liste.

La gestion d’une file d’impression sur une imprimante s’effectue très souventselon une queue de priorité où les fichiers à imprimer sont classés par ordrecroissant de taille. Le fichier imprimé puis éliminé est toujours pris en tête (c’est leplus petit) alors qu’un nouveau fichier est placé dans la file en fonction de sa taille.

Il existe naturellement d’autres manières de gérer des listes. Seuls les deuxpremiers cas seront développés dans ce chapitre. La structure de paquetage va être

Page 289: Cours Ada de A à Z

LISTES, FILES 289

utilisée pour réaliser des solutions de gestion de queues et piles.

Page 290: Cours Ada de A à Z

LISTES STATIQUES 290

14.3 LISTES STATIQUES

Une liste statique est donc une structure de données statique (puisque sa lon-gueur maximale est connue à la compilation) et prête à contenir une suited’informations. Comment cette structure peut-elle être définie? Une bonne façonde faire est de déclarer un article comme dans l’exemple 14.1 où les informationssont supposées du type T_Info.

Le type T_Liste_Statique permet la déclaration (création) d’une ou deplusieurs listes formées des informations (dans le tableau Contenu), de leurLongueur et de deux indicateurs désignant leur Tete et leur Queue. Lesinformations sont numérotées par T_Numerotation. Ces listes seront toutesinitialement vides car leur longueur vaut 0 à la déclaration.

Exemple 14.1 Définition d’une liste statique.

Longueur_Max : constant := 100; -- Longueur maximum d'une liste

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Liste_Statique is -- Pour une liste statiquerecord

Contenu : T_Contenu; -- Contenu de la liste

-- Longueur de la listeLongueur : T_Longueur := T_Longueur'First;

Tete : T_Numerotation; -- Tete et Queue de la listeQueue : T_Numerotation;

end record;

Page 291: Cours Ada de A à Z

QUEUES STATIQUES 291

14.4 QUEUES STATIQUES

14.4.1 Définition et création

Une queue statique voit donc sa longueur maximale connue à la compilation.La définition de cette structure (exemple 14.2) est presque identique à celle del’exemple 14.1 pour les listes quelconques, les informations sont toujourssupposées du type T_Info.

Exemple 14.2 Définition d’une queue statique.

Longueur_Max : constant := 100; -- Longueur maximum d'une queue

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Indic is Integer range 1..Longueur_Max + 1;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Queue_Statique is -- Pour une queue statiquerecord

-- Contenu de la queueContenu : T_Contenu;

-- Longueur de la queueLongueur : T_Longueur := T_Longueur'First;

Tete : T_Pos_Indic := T_Pos_Indic'First;-- Tete et queueQueue : T_Pos_Indic := T_Pos_Indic'First;-- de la queue

end record;

Le type T_Queue_Statique défini dans l’exemple 14.2 permet la déclaration(création) d’une ou de plusieurs queues formées des informations existantes (dansle tableau Contenu), de sa Longueur et de deux indicateurs désignant leur Teteet leur Queue. Leurs informations sont numérotées par T_Numerotation.

Ces queues seront toutes initialement vides car leur longueur vaut 0 à ladéclaration et la première information sera placée dans le premier élément. De pluset pour des raisons pratiques, Queue désignera le prochain élément de tableau àutiliser pour une insertion alors que Tete indiquera la prochaine information àextraire. Ceci explique les valeurs initiales pour ces deux indicateurs (fig. 14.1)ainsi que l’intervalle T_Pos_Indic des valeurs possibles.

Page 292: Cours Ada de A à Z

QUEUES STATIQUES 292

Figure 14.1 Définition schématique d’une queue statique initialement vide.

Une queue statique peut maintenant être créée par une simple déclaration devariable de type T_Queue_Statique.

14.4.2 Manipulations de base

Les opérations de base pour une queue (sect. 14.2) s’implémentent sansdifficulté particulière sauf l’insertion (§ 14.4.3). La spécification d’un paquetagede gestion de queues statiques est donné dans l’exemple 14.3. Le corps de cepaquetage Queues_Statiques contiendra uniquement les corps des opérationsénumérées dans la spécification, corps présentés dans les paragraphes qui suivent.

Exemple 14.3 Spécification d’un paquetage de gestion de queues statiques.

-- Bases de gestion de queues statiquespackage Queues_Statiques is

type T_Info is ...; -- Depend de l'application

Longueur_Max : constant :=100; -- Longueur maximum d'une queue

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Indic is Integer range 1..Longueur_Max + 1;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Queue_Statique is -- Pour une queue statiquerecord

-- Contenu de la queueContenu : T_Contenu;

-- Longueur de la queueLongueur : T_Longueur := T_Longueur'First;

Tete : T_Pos_Indic := T_Pos_Indic'First;-- Tete et queueQueue : T_Pos_Indic := T_Pos_Indic'First;-- de la queue

end record;

Queue_Vide : exception; -- Levee si la queue est videQueue_Pleine : exception; -- Levee si la queue est pleine

Queue statique

Queue

1 Longueur_Max65432 ...

Tete

Longueur = 0

Contenu

Page 293: Cours Ada de A à Z

QUEUES STATIQUES 293

-------------------------------------------------------------- Insere Info en queue de La_Queue. Leve Queue_Pleine si-- l'insertion est impossible (la queue contient deja-- Longueur_Max elements)procedure Inserer ( La_Queue : in out T_Queue_Statique;

Info : in T_Info );-------------------------------------------------------------- Supprime l'information (en tete de La_Queue) qui est rendue -- dans Info. Leve Queue_Vide si la suppression est impossible-- (la queue est vide)procedure Supprimer ( La_Queue : in out T_Queue_Statique;

Info : out T_Info );-------------------------------------------------------------- Change l'information en tete de Queue. Leve Queue_Vide si la-- modification est impossible (la queue est vide)procedure Modifier ( La_Queue : in out T_Queue_Statique;

Info : in T_Info );-------------------------------------------------------------- Retourne l'information en tete de La_Queue. Leve Queue_Vide-- si la consultation est impossible (la queue est vide)function Tete ( La_Queue : T_Queue_Statique ) return T_Info;-------------------------------------------------------------- Retourne True si La_Queue est vide, False sinon------------------------------------------------------------function Vide ( La_Queue : T_Queue_Statique ) return Boolean;-- Retourne True si l'information Info est presente dans la-- queue, False sinon------------------------------------------------------------function Recherche ( La_Queue : T_Queue_Statique;

Info : T_Info ) return Boolean;-------------------------------------------------------------- Effectue un traitement sur tous les elements de la queueprocedure Parcourir ( La_Queue : in out T_Queue_Statique );------------------------------------------------------------

end Queues_Statiques;

14.4.3 Insertion

Pour l’insertion, les informations sont placées les unes à la suite des autres, encommençant par occuper le premier élément (fig. 14.2).

Page 294: Cours Ada de A à Z

QUEUES STATIQUES 294

Figure 14.2 Queue statique après une, puis cinq insertions.

Que faire lorsque le dernier élément du Contenu est utilisé? Si le Contenu estcomplet, toute tentative d’insertion doit lever l’exception Queue_Pleine pour enavertir l’auteur (fig. 14.3).

Figure 14.3 Tentative d’insertion après déjà Longueur_Max insertions.

Si la queue n’est pas pleine parce que des suppressions ont eu lieu, et sans parlerencore de gestion circulaire (§ 14.4.11), l’utilisation d’une structure statique néces-site parfois le déplacement des informations présentes, par exemple de manière àce que l’information de tête soit placée dans le premier élément (fig. 14.4). Laprocédure d’insertion (exemple 14.4) est donc quelque peu compliquée du fait de

Queue statique après une insertion

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 5

Contenu

Info 1 Info 2 Info 3 Info 4 Info 5

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 1

Contenu

Info 1

Queue statique après cinq insertions

Queue statique

Tete

1 Longueur_Max65432 ...

Queue

Longueur = Longueur_Max

Contenu

Info 1 Info 2 Info 3 Info 4 Info 5 Info 6 Info 100

Q u e u e _ P l e i n e

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

Page 295: Cours Ada de A à Z

QUEUES STATIQUES 295

ces déplacements, nécessaires lorsque le dernier élément est occupé par uneinformation.

Figure 14.4 Insertion après Longueur_Max insertions et trois suppressions.

Exemple 14.4 Procédure d’insertion d’une information dans une queue statique.

-- Insere Info en queue de La_Queue. Leve Queue_Pleine si l'inser--- tion est impossible (la queue contient deja Longueur_Max

Queue statique avant l’insertion

Queue

1 Longueur_Max65432 ...

Tete

Longueur = Longueur_Max - 3

Contenu

Info 4 Info 5 Info 6 ...

Queue statique avant l’insertion mais après déplacement

Queue

1 Longueur_Max65432 ...

Tete

Longueur = Longueur_Max - 3

Contenu

Info 4 Info 5 Info 6 ...

Info 100

Info 100

Queue statique après l’insertion

Queue

1 Longueur_Max65432 ...

Tete

Longueur = Longueur_Max - 2

Contenu

Info 4 Info 5 Info 6 ... Info 100 Info 101

Page 296: Cours Ada de A à Z

QUEUES STATIQUES 296

-- elements)

procedure Inserer ( La_Queue : in out T_Queue_Statique;Info : in T_Info ) is

begin -- Inserer

-- Cas si la queue est pleineif La_Queue.Longueur = Longueur_Max then

raise Queue_Pleine;

end if;

-- Queue pas pleine => faut-il deplacer les informations et-- les indicateurs? Oui si le dernier element est utilise

if La_Queue.Queue = T_Pos_Indic'Last then

-- Deplacer les informationsLa_Queue.Contenu ( La_Queue.Contenu'First ..

La_Queue.Contenu'First + La_Queue.Longueur–1 ) :=La_Queue.Contenu (La_Queue.Tete..La_Queue.Queue–1);-- 1

-- Remettre les indicateurs au bon endroitLa_Queue.Queue := T_Pos_Indic'First + La_Queue.Longueur;La_Queue.Tete := T_Pos_Indic'First;

end if;

-- Inserer l'information en queueLa_Queue.Contenu ( La_Queue.Queue ) := Info;

-- L'element courant est maintenant occupeLa_Queue.Queue := La_Queue.Queue + 1;

-- Une information de plus dans la queueLa_Queue.Longueur := La_Queue.Longueur + 1;

end Inserer;

Il peut arriver qu’il n’y ait pas d’information à déplacer et que l’indicateurQueue désigne la position après le dernier élément. Les deux indicateurs sont alorsremis à leur valeur initiale et l’instruction 1 effectue une affectation de tranches detableau vides (§ 8.5.1).

14.4.4 Suppression

Pour la suppression, les informations sont extraites les unes après les autres, encommençant par l’information de tête (fig. 14.5). Lorsque la dernière informationdu Contenu a été extraite, toute tentative de suppression doit lever l’exceptionQueue_Vide pour en avertir l’auteur (fig. 14.6). La réalisation de la procédure desuppression est alors facile et illustrée dans l’exemple 14.5.

Page 297: Cours Ada de A à Z

QUEUES STATIQUES 297

Figure 14.5 Queue statique après cinq insertions et deux suppressions.

Figure 14.6 Tentative de suppression alors que la queue est vide.

Exemple 14.5 Procédure de suppression d’une information dans une queue statique.

-- Supprime l'information (en tete de La_Queue) qui est rendue-- dans Info. Leve Queue_Vide si la suppression est impossible (la-- queue est vide)

procedure Supprimer ( La_Queue : in out T_Queue_Statique;Info : out T_Info ) is

begin -- Supprimer

-- Cas si la queue est videif La_Queue.Longueur = T_Longueur'First then

raise Queue_Vide;

end if;

-- Recuperer l'information en teteInfo := La_Queue.Contenu ( La_Queue.Tete );

-- L'element courant est maintenant libreLa_Queue.Tete := La_Queue.Tete + 1;

Queue statique

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 3

Contenu

Info 3 Info 4 Info 5

Queue statique

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 0

Contenu

Q u e u e _ Vide

Page 298: Cours Ada de A à Z

QUEUES STATIQUES 298

-- Une information de moins dans la queueLa_Queue.Longueur := La_Queue.Longueur – 1;

end Supprimer;

14.4.5 Modification

La modification d’une information n’est possible qu’en tête de la queue, pardéfinition de celle-ci (fig. 14.7). Lorsque la queue est vide, toute tentative de modi-fication doit lever l’exception Queue_Vide pour en avertir l’auteur (fig. 14.8).Finalement, la réalisation de la procédure de modification est facile et illustrée dansl’exemple 14.6.

Figure 14.7 Modification de l’information de tête dans une queue statique.

Figure 14.8 Tentative de modification alors que la queue est vide.

Queue statique

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 3

Contenu

Info 3 Info 4 Info 5

Information à modifier

Queue statique

Tete

1 Longueur_Max65432 ...

Queue

Longueur = 0

Contenu

Q u e u e _ Vide

Page 299: Cours Ada de A à Z

QUEUES STATIQUES 299

Exemple 14.6 Procédure de modification d’une information dans une queue statique.

-- Change l'information en tete de La_Queue. Leve Queue_Vide si la-- modification est impossible (la queue est vide)procedure Modifier ( La_Queue : in out T_Queue_Statique;

Info : in T_Info ) isbegin -- Modifier

-- Cas si la queue est videif La_Queue.Longueur = T_Longueur'First then

raise Queue_Vide;

end if;

-- Modifier l'information en teteLa_Queue.Contenu ( La_Queue.Tete ) := Info;

end Modifier;

14.4.6 Consultation

La consultation d’une information n’est possible qu’en tête de la queue, pardéfinition de celle-ci. Cette opération est donc très proche de la modification, cequi permet de donner directement la fonction adéquate dans l’exemple 14.7.

Exemple 14.7 Fonction de consultation d’une information dans une queue statique.

-- Retourne l'information en tete de La_Queue. Leve Queue_Vide si-- la consultation est impossible (la queue est vide)function Tete ( La_Queue : T_Queue_Statique ) return T_Info isbegin -- Tete

-- Cas si la queue est videif La_Queue.Longueur = T_Longueur'First then

raise Queue_Vide;

end if;

-- Retourner l'information en tetereturn La_Queue.Contenu ( La_Queue.Tete );

end Tete;

14.4.7 Queue vide

Le fait qu’une queue soit vide implique que tous les éléments insérés ont étéextraits, ou éventuellement que la queue n’a pas encore servi. Cette information esttrès souvent utilisée dans les algorithmes qui mettent en œuvre une ou plusieursqueues. La fonction correspondante est présentée dans l’exemple 14.8.

Page 300: Cours Ada de A à Z

QUEUES STATIQUES 300

Exemple 14.8 Fonction retournant l’état (vide/non vide) d’une queue statique.

-- Retourne True si La_Queue est vide, False sinonfunction Vide ( La_Queue : T_Queue_Statique ) return Boolean isbegin -- Vide

-- La queue est vide si sa longueur vaut 0return La_Queue.Longueur = T_Longueur'First;

end Vide;

14.4.8 Recherche

Une recherche est très différente des opérations précédentes. S’il existe denombreuses méthodes de recherche, la recherche séquentielle (sequential search)va seule être réalisée. Une recherche séquentielle consiste à examiner les informa-tions les unes après les autres en commençant par la tête de la queue et à stopper:

• soit parce que l’information cherchée a été trouvée dans la queue;• soit parce que toutes les informations ont été examinées sans succès.

Une recherche donnera donc comme résultat une valeur binaire (informationtrouvée ou non). Parfois la position de l’information trouvée est aussi donnéecomme résultat. L’algorithme de recherche séquentielle consiste donc essentiel-lement en une boucle avec les deux conditions de sortie mentionnées ci-dessus.L’exemple 14.9 donne la fonction réalisant cette opération.

Exemple 14.9 Fonction de recherche d’une information dans une queue statique.

-- Retourne True si l'information Info est presente dans la queue,-- False sinonfunction Recherche ( La_Queue : T_Queue_Statique;

Info : T_Info ) return Boolean isPosition : T_Pos_Indic := La_Queue.Tete;-- Pour acceder a

-- chaque informationbegin -- Recherche

-- Tant que la queue n'est pas atteinte et l'information pas-- trouveewhile Position < La_Queue.Queue and then

Info /= La_Queue.Contenu ( Position ) loopPosition := Position + 1; -- Passer a l'information suivante

end loop;

-- Si la queue n'est pas atteinte, l'information a ete trouveereturn Position < La_Queue.Queue;

end Recherche;

Page 301: Cours Ada de A à Z

QUEUES STATIQUES 301

La présence de la forme de contrôle en raccourci and then (§ 3.4.3) est le seulpoint délicat de cet exemple; elle est nécessaire pour éviter de consulter leContenu si la Position est hors de celui-ci. A noter que le cas d’une queue videne provoque cette fois aucune exception; la recherche d’une information dans unequeue vide aboutit immédiatement et simplement à la valeur False (informationintrouvable), ce qui est conforme à la logique et à la pratique courante.

14.4.9 Parcours

Le parcours (traverse) d’une structure consiste à appliquer un même traitementà tous les éléments de cette structure. Le résultat de l’application du traitement àchaque élément forme le résultat du parcours. Selon le traitement, les éléments oula structure peuvent être modifiés ou non.

Le parcours d’une queue semble facile à réaliser. Mais puisque le traitementpeut être différent à chaque fois qu’un parcours s’effectue, comment réaliser ceschangements dans une seule procédure de parcours? Il faut connaître la notion degénéricité (chap. 17 et 18) ou de type accès à sous-programme (§ 15.8.2) pourobtenir une réponse satisfaisante à ce problème. L’exemple 14.10 présente uneprocédure réalisant un parcours où l’on suppose qu’une procédure Traitereffectue le traitement voulu.

Exemple 14.10 Procédure de parcours d’une queue statique.

-- Effectue un traitement sur tous les elements de la queue. Le-- traitement est suppose fait dans la procedure interne Traiterprocedure Parcourir ( La_Queue : in out T_Queue_Statique ) is

procedure Traiter ( Info : in out T_Info ) is ... end Traiter;

begin -- Parcourir

-- Prendre les elements les uns apres les autresfor Position in La_Queue.Tete .. La_Queue.Queue – 1 loop

Traiter ( La_Queue.Contenu (Position) ); -- Traitementend loop;

end Parcourir;

Comme pour la recherche et pour les mêmes raisons, le cas d’une queue videne provoque aucune exception.

14.4.10 Exemple d’utilisation d’une queue statique

Soit un bar à boissons qui suit les festivals musicaux d’été (jazz à Montreux,Paléo à Nyon, etc.). Les festivaliers assoiffés doivent d’abord payer leur boisson àune caisse unique et reçoivent un ticket numéroté attestant la somme payée. Lacaisse et le bar proprement dit sont reliés par un petit système informatique qui,

Page 302: Cours Ada de A à Z

QUEUES STATIQUES 302

entre autres, permet aux barmen de préparer aussitôt les boissons payéesapparaissant à l’écran, accompagnées des numéros des tickets. Les barmen serventles boissons selon l’ordre d’apparition des numéros afin de ne provoquer aucunmécontentement.

Pour la programmation du logiciel du système informatique, une queue statiquea été utilisée. Elle contient à tout moment les boissons payées mais non encoredistribuées avec les numéros correspondants. L’information est donc constituée dutype de boisson, de la contenance et du numéro associé (exemple 14.11).

Exemple 14.11 Le type T_Info pour le bar à boissons.

type T_Info is

recordBoisson : Integer; -- Pour simplifierContenance : Float; -- En litresNumero : Integer; -- Numero du ticket correspondant

end record;

On suppose que ce type T_Info a complété la spécification de l’exemple 14.3.Les caissiers et les barmen utilisent le logiciel pour gérer leurs boissons. Pourvendre une boisson, la procédure Vendre_Boisson est utilisée alors que pourdistribuer une boisson, c’est la procédure Distribuer_Boisson qui s’exécute.La queue et les corps de ces procédures sont décrites dans l’exemple 14.12.

Exemple 14.12 Gestion rudimentaire des boissons du bar.

with Queues_Statiques; -- But de l'exemple

procedure Systeme_Gestion is-- Squelette pour presenter la queue et les deux procedures

-- La queue des boissons vendues mais pas encore distribueesQueue_Boissons : Queues_Statiques.T_Queue_Statique;...-------------------------------------------------------------- Permet d'enregistrer une boisson vendueprocedure Vendre_Boisson is

-- Type , contenance et numero de la boisson vendueInfo_Boisson : Queues_Statiques.T_Info;

begin -- Vendre_Boisson

... -- Accueillir le client, lui demander ce qu'il veut

-- Enregistrer sa commandeQueues_Statiques.Inserer ( Queue_Boissons,

(Boisson, Contenance, Numero) );end Vendre_Boisson;

Page 303: Cours Ada de A à Z

QUEUES STATIQUES 303

-- Pour la distribution d'une boissonprocedure Distribuer_Boisson is

-- Type , contenance et numero de la boisson distribueeInfo_Boisson : Queues_Statiques.T_Info;

begin -- Distribuer_Boisson

-- Connaitre la boisson du prochain clientQueues_Statiques.Supprimer (Queue_Boissons, Info_Boisson);

-- Preparer la boisson, appeler le client grace au numero...

end Distribuer_Boisson;------------------------------------------------------------...

begin -- Systeme_Gestion...

end Systeme_Gestion;

Il faut que les barmen distribuent les boissons suffisamment vite pour éviter quela queue devienne pleine et que la procédure Inserer lève l’exceptionQueue_Pleine.

14.4.11 Queues statiques gérées circulairement

L’insertion dans une queue statique peut être considérablement simplifiée si laqueue est gérée circulairement, c’est-à-dire si le tableau Contenu est pris commeun anneau: le premier élément est le successeur du dernier. Il n’y a alors plus besoinde décaler les informations puisque le concept de dernier élément n’existe plus!

Les autres opérations sont très similaires à celles présentées. Ce cas particulierde gestion circulaire, très utilisé dans la pratique, ne sera pas plus détaillé ici.

Page 304: Cours Ada de A à Z

PILES STATIQUES 304

14.5 PILES STATIQUES

14.5.1 Définition et création

Une pile statique voit donc sa longueur maximale connue à la compilation. Ladéfinition de cette structure est presque identique à celle d’une queue (§ 14.4.1),les informations sont toujours supposées du type T_Info.

Exemple 14.13 Définition d’une pile statique.

Longueur_Max : constant := 100; -- Longueur maximum d'une pile

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Sommet is Integer range 0..Longueur_Max;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Pile_Statique is -- Pour une pile statique

record

-- Contenu de la pileContenu : T_Contenu;

-- Longueur de la pileLongueur : T_Longueur := T_Longueur'First;

-- Sommet de la pileSommet : T_Pos_Sommet := T_Pos_Sommet'First;

end record;

Le type T_Pile_Statique défini dans l’exemple 14.13 permet la déclaration(création) d’une ou de plusieurs piles formées des informations existantes (dans letableau Contenu), de sa Longueur et d’un seul indicateur désignant leur Sommet.Leurs informations sont numérotées par T_Numerotation.

Ces piles seront toutes initialement vides car leur longueur vaut 0 à ladéclaration. Par choix arbitraire, la première information sera placée dans lepremier élément. De plus et pour des raisons pratiques, Sommet désigneral’élément de tableau contenant l’information située au sommet de la pile (§ 14.2.2).Ceci explique la valeur initiale pour cet indicateur ainsi que l’intervalleT_Pos_Sommet des valeurs possibles (fig. 14.9).

Page 305: Cours Ada de A à Z

PILES STATIQUES 305

Figure 14.9 Définition schématique d’une pile statique initialement vide.

Une pile statique peut être représentée horizontalement ou verticalement. Pourdes raisons de composition des pages de cet ouvrage, la forme horizontale sera engénéral celle choisie.

Une pile statique peut maintenant être créée par une simple déclaration devariable de type T_Pile_Statique.

14.5.2 Manipulations de base

Les opérations de base pour une pile (sect. 14.2) s’implémentent sans difficultéparticulière et, sauf l’insertion, sont très semblables à celles d’une queue. La spéci-fication d’un paquetage de gestion de piles statiques est donné dans l’exemple14.14.

Exemple 14.14 Spécification d’un paquetage de gestion de piles statiques.

-- Bases de gestion de piles statiquespackage Piles_Statiques is

type T_Info is ...; -- Depend de l'application

Longueur_Max : constant := 100; -- Longueur maximum d'une pile

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Sommet is Integer range 0..Longueur_Max;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Pile_Statique is -- Pour une pile statique

record

Contenu : T_Contenu; -- Contenu de la pile

-- Longueur de la pileLongueur : T_Longueur := T_Longueur'First;

-- Sommet de la pile

Pile statique

Sommet

1 Longueur_Max65432 ...

Longueur = 0

Contenu

Page 306: Cours Ada de A à Z

PILES STATIQUES 306

Sommet : T_Pos_Sommet := T_Pos_Sommet'First;

end record;

Pile_Vide : exception; -- Levee si la pile est videPile_Pleine : exception; -- Levee si la pile est pleine

-------------------------------------------------------------- Insere Info au sommet de Pile. Leve Pile_Pleine si l'inser--- tion est impossible (la pile contient deja Longueur_Max-- elements)procedure Empiler ( Pile : in out T_Pile_Statique;

Info : in T_Info );

-------------------------------------------------------------- Supprime l'information (au sommet de Pile) qui est rendue-- dans Info. Leve Pile_Vide si la suppression est impossible-- (la pile est vide)procedure Desempiler ( Pile : in out T_Pile_Statique;

Info : out T_Info );

-------------------------------------------------------------- Change l'information du sommet de Pile. Leve Pile_Vide si la-- modification est impossible (la pile est vide)procedure Modifier ( Pile : in out T_Pile_Statique;

Info : in T_Info );-- Retourne l'information du sommet de Pile. Leve Pile_Vide si-- la consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile_Statique) return T_Info;

-------------------------------------------------------------- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile_Statique) return Boolean;

-------------------------------------------------------------- Retourne True si l'information Info est presente dans la-- pile, False sinonfunction Recherche ( Pile : T_Pile_Statique;

Info : T_Info) return Boolean;-------------------------------------------------------------- Effectue un traitement sur tous les elements de la pileprocedure Parcourir ( Pile : in out T_Pile_Statique );------------------------------------------------------------

end Piles_Statiques;

Le corps de ce paquetage Piles_Statiques contiendra uniquement les corpsdes opérations énumérées dans la spécification, corps présentés dans les para-graphes qui suivent.

14.5.3 Insertion

Les informations sont placées à la suite les unes des autres, en commençant paroccuper le premier élément (fig. 14.10). Insérer une information est communément

Page 307: Cours Ada de A à Z

PILES STATIQUES 307

appelé empiler (push).

Figure 14.10 Pile statique après cinq insertions.

Lorsque le dernier élément du Contenu est utilisé, toute tentative d’insertiondoit naturellement lever l’exception Pile_Pleine pour en avertir l’auteur (fig.14.11). La réalisation de la procédure d’insertion est alors facile et illustrée dansl’exemple 14.15.

Figure 14.11 Tentative d’insertion après déjà Longueur_Max insertions.

Exemple 14.15 Procédure d’insertion d’une information dans une pile statique.

-- Insere Info au sommet de Pile. Leve Pile_Pleine si l'insertion-- est impossible (la pile contient deja Longueur_Max elements)procedure Empiler ( Pile : in out T_Pile_Statique;

Info : in T_Info ) isbegin -- Empiler

-- Cas si la pile est pleineif Pile.Longueur = Longueur_Max then

Pile statique

1 Longueur_Max65432 ...

Sommet

Longueur = 5

Contenu

Info 1 Info 2 Info 3 Info 4 Info 5

Pile statique

1 Longueur_Max65432 ...

Sommet

Longueur = Longueur_Max

Contenu

Info 1 Info 2 Info 3 Info 4 Info 5 Info 6 ... Info 100

P i l e _ P l e i n e

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

Page 308: Cours Ada de A à Z

PILES STATIQUES 308

raise Pile_Pleine;

end if;

-- L'element suivant va etre occupePile.Sommet := Pile.Sommet + 1;

-- Inserer l'information au sommetPile.Contenu ( Pile.Sommet ) := Info;

-- Une information de plus dans la pilePile.Longueur := Pile.Longueur + 1;

end Empiler;

14.5.4 Suppression

Pour la suppression, les informations sont extraites les unes après les autres, encommençant par l’information au sommet (fig. 14.12). Supprimer une informationest communément appelé désempiler (pop).

Lorsque la dernière information du Contenu a été désempilée, toute tentativede suppression doit lever l’exception Pile_Vide pour en avertir l’auteur (fig.14.13). La réalisation de la procédure de suppression est alors facile et illustréedans l’exemple 14.16.

Figure 14.12 Pile statique après cinq insertions et deux suppressions.

Pile statique

1 Longueur_Max65432 ...

Sommet

Longueur = 3

Contenu

Info 1 Info 2 Info 3

Page 309: Cours Ada de A à Z

PILES STATIQUES 309

Figure 14.13 Tentative de suppression alors que la pile est vide.

Exemple 14.16 Procédure de suppression d’une information dans une pile statique.

-- Supprime l'information (au sommet de Pile) qui est rendue dans-- Info. Leve Pile_Vide si la suppression est impossible (la pile-- est vide)

procedure Desempiler (Pile : in out T_Pile_Statique;Info : out T_Info ) is

begin -- Desempiler

-- Cas si la pile est videif Pile.Longueur = T_Longueur'First then

raise Pile_Vide;

end if;

-- Supprimer l'information au sommetInfo := Pile.Contenu ( Pile.Sommet );

-- Le sommet devient l'element precedentPile.Sommet := Pile.Sommet – 1;

-- Une information de moins dans la pilePile.Longueur := Pile.Longueur – 1;

end Desempiler;

14.5.5 Modification

La modification d’une information n’est possible qu’au sommet de la pile, pardéfinition de celle-ci (fig. 14.14).

Pile statique

1 Longueur_Max65432 ...

Sommet

Longueur = 0

Contenu

P i l e _ Vide

Page 310: Cours Ada de A à Z

PILES STATIQUES 310

Figure 14.14 Modification de l’information au sommet dans une pile statique.

Lorsque la pile est vide, toute tentative de modification doit lever l’exceptionPile_Vide pour en avertir l’auteur (fig. 14.15).

Figure 14.15 Tentative de modification alors que la pile est vide.

La réalisation de la procédure de modification est aussi facile et elle est illustréedans l’exemple 14.17.

Exemple 14.17 Procédure de modification d’une information dans une pile statique.

-- Change l'information du sommet de Pile. Leve Pile_Vide si la-- modification est impossible (la pile est vide)procedure Modifier ( Pile : in out T_Pile_Statique;

Info : in T_Info ) isbegin -- Modifier

-- Cas si la pile est videif Pile.Longueur = T_Longueur'First then

raise Pile_Vide;

end if;

-- Modifier l'information du sommet

Pile statique

Sommet

1 Longueur_Max65432 ...

Longueur = 3

Contenu

Info 1 Info 2 Info 3

Information à modifier

Pile statique

Sommet

1 Longueur_Max65432 ...

Longueur = 0

Contenu

P i l e _ Vide

Page 311: Cours Ada de A à Z

PILES STATIQUES 311

Pile.Contenu ( Pile.Sommet ) := Info;

end Modifier;

14.5.6 Consultation

La consultation d’une information n’est possible qu’au sommet de la pile, pardéfinition de celle-ci. Cette opération est donc très proche de la modification, cequi permet de donner directement la fonction adéquate dans l’exemple 14.18.

Exemple 14.18 Fonction de consultation d’une information dans une pile statique.

-- Retourne l'information du sommet de Pile. Leve Pile_Vide si la-- consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile_Statique ) return T_Info isbegin -- Sommet

-- Cas si la pile est videif Pile.Longueur = T_Longueur'First then

raise Pile_Vide;

end if;

-- Retourner l'information du sommetreturn Pile.Contenu ( Pile.Sommet );

end Sommet;

14.5.7 Pile vide

Le fait qu’une pile soit vide implique que tous les éléments insérés ont étéextraits, ou éventuellement que la pile n’a pas encore servi. Cette information esttrès souvent utilisée dans les algorithmes qui mettent en œuvre une ou plusieurspiles. La fonction correspondante est présentée dans l’exemple 14.19.

Exemple 14.19 Fonction retournant l’état (vide/non vide) d’une pile statique.

-- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile_Statique ) return Boolean isbegin -- Vide

-- La pile est vide si sa longueur vaut 0return Pile.Longueur = T_Longueur'First;

end Vide;

Page 312: Cours Ada de A à Z

PILES STATIQUES 312

14.5.8 Recherche

La recherche séquentielle dans une pile est semblable à celle appliquée pourune queue (§ 14.4.8) en commençant par le sommet de la pile. L’exemple 14.20donne la fonction réalisant cette opération.

Exemple 14.20 Fonction de recherche d’une information dans une pile statique.

-- Retourne True si l'information Info est presente dans la pile,-- False sinon

function Recherche ( Pile : T_Pile_Statique;Info : T_Info ) return Boolean is

Position : T_Pos_Sommet := Pile.Sommet; -- Pour acceder a-- chaque information

begin -- Recherche

-- Tant qu'il reste des elements et l'information pas trouveewhile Position >= Pile.Contenu'First and then

Info /= Pile.Contenu ( Position ) loop

-- Passer a l'information suivantePosition := Position – 1;

end loop;

-- L'information a ete trouvee si Position designe un element-- de la pilereturn Position >= Pile.Contenu'First;

end Recherche;

La présence de la forme de contrôle en raccourci and then (§ 3.4.3) est le seulpoint délicat de cet exemple; elle est nécessaire pour éviter de consulter leContenu si la Position est hors de celui-ci. A noter que le cas d’une pile vide neprovoque cette fois aucune exception; la recherche d’une information dans une pilevide aboutit immédiatement et simplement à la valeur False (informationintrouvable), ce qui est conforme à la logique et à la pratique courante.

14.5.9 Parcours

Le parcours d’une pile est semblable à celui appliqué à une queue (§ 14.4.9) encommençant par le sommet de la pile, avec la même remarque concernant letraitement à effectuer. L’exemple 14.21 présente une procédure réalisant unparcours où l’on suppose qu’une procédure Traiter effectue le traitement voulu.

Exemple 14.21 Procédure de parcours d’une pile statique.

-- Effectue un traitement sur tous les elements de la pile. Le-- traitement est suppose fait dans la procedure interne Traiter

Page 313: Cours Ada de A à Z

PILES STATIQUES 313

procedure Parcourir ( Pile : in out T_Pile_Statique ) is

procedure Traiter ( Info : in out T_Info ) is ... end Traiter;

begin -- Parcourir

-- Prendre les elements les uns apres les autresfor Position in reverse Pile.Contenu'First .. Pile.Sommet loop

Traiter ( Pile.Contenu (Position) ); -- Traitement

end loop;

end Parcourir;

Comme pour la recherche et pour les mêmes raisons, le cas d’une pile vide neprovoque aucune exception.

14.5.10 Exemple d’utilisation d’une pile statique

Parmi toutes les applications de l’utilisation d’une pile statique, l’empilementdes objets locaux lors de l’appel d’un sous-programme est l’une des plusintéressantes. En simplifiant la réalité qui est assez complexe, il s’agit de gérer unepile dont les éléments sont des blocs de mémoire formés de paramètres et devariables locales, avec un bloc par sous-programme en cours d’exécution. Achaque appel de sous-programme, un nouveau bloc est empilé alors qu’à chaquesortie, le bloc du sous-programme quitté est désempilé. Sans donner plusd’explications, cette façon de faire facilite la gestion de la mémoire et l’accès auxobjets locaux. L’exemple 14.22 montre une suite d’appels de procédures et lesfigures 14.16 et 14.17 les empilements correspondants.

Exemple 14.22 Suite d’appels de procédures.

procedure P1 ( I : in Integer ) isJ : Integer;

begin -- P1...

end P1;

procedure P2 isA, B: Float;

begin -- P2...P1;...

end P2;

Page 314: Cours Ada de A à Z

PILES STATIQUES 314

Figure 14.16 Empilement du bloc correspondant à l’appel de P2 (exemple 14.22).

Figure 14.17 Empilement du bloc correspondant à l’appel de P1 (exemple 14.22).

Appel de P2:

emplacement de B

emplacement de A

blocs des appels des sous-programmes précédant

place libre dans la pile

sommet de la pile

bloc de P2}l’appel de P2

Appel de P1:

emplacement de B

emplacement de A

blocs des appels des sous-programmes précédant

place libre dans la pile

sommet de la pile

bloc de P2}

emplacement de J

emplacement de Ibloc de P1}

l’appel de P2

Page 315: Cours Ada de A à Z

315

14.6 EXERCICES

14.6.1 Déclaration d’une queue statiquePourquoi la borne supérieure du sous-type T_Pos_Indic (exemple 14.2) vaut-

elle Longueur_Max + 1?

14.6.2 Queue statique circulaireRécrire les déclarations de l’exemple 14.2 pour une queue statique circulaire et

adapter la procédure d’insertion (§ 14.4.3).

14.6.3 Paquetage enfant pour les queues statiquesEcrire un paquetage enfant du parent Queues_Statiques exportant les opéra-

tions suivantes: copier une queue, appondre une (copie d’une) queue à une autre,savoir si une queue est pleine, et supprimer tous les éléments d’une queue.

14.6.4 Paquetage enfant pour les piles statiquesEcrire un paquetage enfant du parent Piles_Statiques similaire à celui

demandé dans l’exercice 14.6.3.

Page 316: Cours Ada de A à Z

POINTS À RELEVER 316

14.7 POINTS À RELEVER

14.7.1 En général

• Une structure de données est un support organisé permettant lareprésentation en mémoire de données simples ou complexes.

• Une liste est une structure de données linéaire, statique ou dynamique. Lesqueues et les piles sont des cas simples de listes.

• Insérer, supprimer, modifier, consulter, rechercher, parcourir constituentles opérations de base sur les listes.

• Les queues sont gérées selon la politique FIFO, les piles selon la politiqueLIFO.

• Il faut faire attention lorsqu’une liste est vide ou pleine; certaines opérationspeuvent alors provoquer des erreurs.

14.7.2 En Ada

• Une liste statique est implémentée par un article.

• Une queue statique comporte quatre éléments: le contenu sous forme detableau, la longueur et les indicateurs de tête et de queue.

• Une pile statique comporte trois éléments: le contenu sous forme detableau, la longueur et l’indicateur du sommet.

Page 317: Cours Ada de A à Z

317

C H A P I T R E 1 5

T Y P E S

Page 318: Cours Ada de A à Z

318 TYPES ACCÈS

Page 319: Cours Ada de A à Z

MOTIVATION 319

15.1 MOTIVATION

Les pointeurs sont difficiles à appréhender, en particulier pour les program-meurs néophytes. Les pièges apparaissant lors de leur manipulation sont souventdéconcertants. Une comparaison tirée de [BAR 97] illustre parfaitement lasituation: «Manipuler les pointeurs, c’est un peu comme jouer avec le feu. Le feuest sans doute l’outil le plus important pour l’homme. Utilisé avec précaution, lefeu est considérablement utile; mais quel désastre s’il n’est plus contrôlé!».Cependant, l’usage très largement répandu des pointeurs rend indispensable leurprésentation et leur maîtrise. Les types accès permettent la déclaration de pointeursen Ada.

Page 320: Cours Ada de A à Z

TYPE ACCÈS 320

15.2 TYPE ACCÈS

15.2.1 Motivation

Les structures de données statiques ont l’avantage d’une réalisation simple etd’un accès en général efficace en temps processeur. Leur principal inconvénientréside dans leur nature statique, impossible en effet d’augmenter leur taille lors del’exécution puisque celle-ci est fixée à la compilation; on parle ici d’allocationstatique de mémoire (static memory allocation, note 15.1). Il est vrai que lelangage Ada permet de déterminer la taille d’un tableau à l’exécution grâce auxtypes tableaux non contraints (§ 8.2.3), voire de faire varier celle-ci dans certaineslimites en utilisant des types articles à discriminants avec valeurs par défaut (sect.11.3). Mais, même avec ces quelques propriétés, la taille (maximale) reste unefrontière infranchissable.

L’allocation dynamique de mémoire (dynamic memory allocation) permetd’éliminer cette frontière. Il s’agit d’une idée toute simple: permettre au program-me de se réserver, de s’allouer des portions de mémoire lorsqu’il en a besoin. Maisil existe évidemment aussi une limite à cette technique, c’est la quantité demémoire disponible de l’ordinateur utilisé, d’où l’importance de prendre en com-pte les possibilités de restitution, de libération de mémoire (memory deallocation)lorsque certaines portions deviennent inutilisées. Cette restitution peut être auto-matique, par exemple s’il existe un ramasse-miettes (garbage collector) associé auprogramme en exécution. En l’absence d’un tel outil, le programme lui-même peutassurer la libération de la mémoire.

L’allocation dynamique de mémoire est cependant une technique de bas niveau,dans le sens où le programmeur qui l’utilise doit travailler avec des portions demémoire, comprendre que celles-ci sont définies par une adresse et une taille. Demanière générale, dans les langages de programmation, la gestion des adressesmémoire (la taille ne pose aucun problème) s’effectue par des pointeurs (pointers),c’est-à-dire par des variables qui contiendront des adresses mémoire.

NOTE 15.1 Variables et allocation de mémoire.

Il faut absolument comprendre que toute déclaration de variable dans une partie décla-rative esttraduite par le compilateur en code réalisant, à l’exécution, l’allocation d’une place mémoiresuffisamment grande pour cette variable. Cette règle est donc aussi valable pour une variablepointeur, la place mémoire allouée dans ce cas permettant de contenir une adresse.

15.2.2 Généralités

Un type pointeur en Ada se déclare au moyen du mot réservé access, d’où laterminologie des types accès pour désigner ces types pointeurs. Mais, avec ce mot

Page 321: Cours Ada de A à Z

TYPE ACCÈS 321

réservé, la déclaration du type n’est pas complète. Il faut encore indiquer quelcontenu les variables pointeurs de ce type vont repérer, ceci en effet pour permettreau compilateur les vérifications (de type) classiques lors d’affectation ou depassage en paramètre par exemple. La forme la plus simple d’une déclaration detype accès est:

type identificateur_type_acces is access ident_de_type_ou_sous_type;

avec• identificateur_type_acces le nom du type accès défini;• ident_de_type_ou_sous_type le type ou le sous-type du contenu des

portions mémoire qui seront repérées par les variables du typeidentificateur_type_acces; ce type ou sous-type s’appelle type ousous-type pointé.

Exemple 15.1 Déclaration de types pointeurs sur des types habituels.

type T_Tableau is array (1..9) of Integer; -- Un type tableautype T_Article is -- Un type article

recordNombre : Integer;Tab : T_Tableau;

end record;

type T_Pt_Integer is access Integer; -- Un type pointeur sur le-- type pointe Integer

type T_Pt_Float is access Float; -- Un type pointeur sur le-- type pointe Float

type T_Pt_Tableau is access T_Tableau;-- Un type pointeur sur le-- type pointe T_Tableau

type T_Pt_Article is access T_Article;-- Un type pointeur sur le-- type pointe T_Article

Dans l’exemple 15.1, le type T_Pt_Integer signifie que les variables de cetype contiendront une adresse (c’est un type accès) d’une portion mémoire prévuepour un nombre du type pointé Integer. De manière similaire, les typesT_Pt_Float, T_Pt_Tableau et T_Pt_Article signifient que les variables deces types contiendront une adresse d’une portion mémoire prévue pour un nombredu type pointé Float, un tableau du type pointé T_Tableau et un article du typepointé T_Article. Ces portions mémoire portent le nom de variables pointéespuisque désignées, repérées par un pointeur.

Il existe une seule constante pointeur appelée null qui, affectée à une variablepointeur, indique que cette variable ne repère aucune variable pointée. Une variablepointeur se déclare comme toute autre variable et possède la particularité d’avoirautomatiquement la valeur initiale (valeur par défaut) null.

Page 322: Cours Ada de A à Z

TYPE ACCÈS 322

Les opérations possibles (en plus de l’affectation et du passage en paramètre)sur les pointeurs consiste en l’utilisation de l’allocateur new (§ 15.2.3) et l’accès àla variable pointée (dereference).

Les expressions se limitent aux constantes, variables, attributs applicables auxpointeurs ainsi qu’à l’utilisation de l’allocateur new.

Finalement, les types pointeurs font partie des types simples (elementary types,[ARM 3.2]).

15.2.3 Allocateur new et affectation

Comme déjà mentionné (note 15.1), la place mémoire pour une variable poin-teur est prévue par le compilateur comme pour toute autre variable de n’importequel type. Mais la création d’une variable pointée s’effectue à l’exécution (c’est lebut visé) par un allocateur de mémoire (memory allocator), utilitaire présent danstoute application nécessitant l’allocation dynamique de mémoire.

En Ada, le mot réservé new désigne cet allocateur qui s’utilise comme unefonction-opérateur et de deux manières différentes:

new identificateur_de_type_ou_sous_type

ounew expression_qualifiee

avec• l’identificateur_de_type_ou_sous_type qui indique le type ou

sous-type (pointé) de la variable pointée créée;

• l’expression_qualifiee (sect. 3.10) qui non seulement indique le typeou sous-type (pointé) de la variable pointée créée mais encore lui donne lavaleur initiale placée entre les parenthèses.

Exemple 15.2 Utilisation de l’allocateur new avec les types de l’exemple 15.1.

new Integerune variable pointée du type Integer est créée;new Floatune variable pointée du type Float est créée;new T_Tableauune variable pointée du type T_Tableau est créée;new T_Articleune variable pointée du type T_Article est créée;new Integer'(10)une variable pointée du type Integer est créée et savaleur initiale est 10;new Float'(10.0)une variable pointée du type Float est créée et sa valeurinitiale est 10.0;new T_Tableau'(others=>0)une variable pointée du type T_Tableau estcréée et sa valeur initiale est (others=>0);new T_Article'(1,(others=>0))une variable pointée du type T_Article

Page 323: Cours Ada de A à Z

TYPE ACCÈS 323

est créée et sa valeur initiale est (1, (others=>0));

Puisque l’allocateur new s’utilise comme une fonction, quel est le résultatretourné? Ce résultat comprend l’adresse de la variable pointée créée puisqu’ilfaudra naturellement pouvoir accéder à cette variable. Et pour cela, cette adressedevra être placée dans une variable pointeur du bon type par une affectation,comme dans l’exemple 15.3. C’est ainsi qu’une variable pointeur repérera,désignera, pointera (tous ces termes sont synonymes) une variable pointée.

Exemple 15.3 Affectation du résultat de l’allocateur new.

-- On utilise les types de l'exemple 15.1Pt_Integer : T_Pt_Integer; -- Une variable pointeur sur

IntegerPt_Float : T_Pt_Float; -- Une variable pointeur sur FloatPt_Tableau : T_Pt_Tableau; -- Une variable pointeur sur

T_TableauPt_Article : T_Pt_Article; -- Une variable pointeur sur

T_Article...-- Utilisation de l'allocateur:Pt_Integer := new Integer; -- Pt_Integer repere la variable

-- pointee creee

Pt_Float := new Float'(10.0); -- Pt_Float repere la variable-- pointee creee de valeur initiale-- 10.0

Pt_Tableau := new T_Tableau; -- Pt_Tableau repere la variable-- pointee creee

Pt_Article := new T_Article'(1,(others=>0)); -- Pt_Article repere-- la variable pointee creee de-- valeur initiale (1,(others=>0))

15.2.4 Représentation schématique

L’expérience montre que le travail avec les pointeurs est facilité, pour lesnéophytes, par la pratique de schémas illustrant la gestion des pointeurs et desvariables pointées (fig. 15.1). Ces schémas comprennent des rectangles pour lesvariables et des flèches qui symbolisent les adresses des variables pointées;l’extrémité initiale d’une telle flèche indique où est enregistrée l’adresse alors quel’extrémité terminale désigne la variable pointée possédant cette adresse. La valeurparticulière null est représentée par une barre oblique.

Page 324: Cours Ada de A à Z

TYPE ACCÈS 324

Figure 15.1 Représentation schématique des déclarations et affectations de l’exemple 15.3.

15.2.5 Accès aux variables pointées

L’accès à une variable pointée ne peut se faire que par l’intermédiaire d’unevariable pointeur, ou indirectement par une autre variable pointée (§ 15.3.1). Cetaccès peut se faire sur l’entier de la variable pointée ou sur l’un de ses composantssi elle est de type composé (tableau ou article).

L’accès à l’entier de la variable pointée (attention à la note 15.2) s’effectue enajoutant le suffixe all à la variable pointeur qui la repère. L’accès à un composantest réalisé par la notation habituelle d’accès à ce composant, la variable pointeur(suivie ou non de all) jouant alors le rôle de préfixe. L’expression que constituecet accès est naturellement du type de la variable pointée ou du composant accédé.

NOTE 15.2 Accès et valeur null.

Soit une variable pointeur de valeur null. L’exception Constraint_Error sera levée si cette variable

Pt_Integer

Pointeurs Variables pointées

Pt_Float

?

10.0

Pt_Tableau

Pt_Article

1

0 0 0 0 0 0 0 0 0

? ? ? ? ? ? ? ? ?

Pt_Integer

Déclaration des pointeurs:

Pt_FloatPt_Tableau Pt_Article

Affectation des pointeurs:

Page 325: Cours Ada de A à Z

TYPE ACCÈS 325

est utilisée pour une tentative d’accès à une variable pointée. Cette erreur est très fréquente dans lesprogrammes comportant des pointeurs et écrits par des néophytes.

Exemple 15.4 Accès aux variables pointées (les types sont définis dans l’exemple 15.1).

with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Exemple_15_4 is

-- Quatre variables pointeursPt_Integer : T_Pt_Integer := new Integer;Pt_Float : T_Pt_Float := new Float'(10.0);Pt_Tableau : T_Pt_Tableau := new T_Tableau;Pt_Article : T_Pt_Article := new T_Article'(1, (others=>0));

Entier : Integer; -- Une variable auxiliaire

begin -- Exemple_15_4

-- Acces aux variables pointees creees par l'allocateurPt_Integer.all := 5; -- 1Entier := 3 * Pt_Integer.all – 1; -- 2Put (Pt_Integer.all); -- 3Pt_Tableau.all := (1..4 =>0, 5|7|9 => 1, others => 2);-- 4

-- Acces aux composantsPt_Tableau (1) := 5; -- 5Pt_Tableau.all (1) := 5; -- Identique a l'instruction 5for I in Pt_Tableau.all'Range loop -- 6

Put ( Pt_Tableau (I) );end loop;

Pt_Article.Nombre := 10; -- 7Pt_Article.all.Nombre := 10; -- Identique a l'instruction 7Pt_Article.Tab := Pt_Tableau.all; -- 8

for I in Pt_Article.Tab'Range loop -- 9Put ( Pt_Article.Tab (I) );

end loop;...

Afin de bien comprendre ce mécanisme d’accès, l’effet des différentes ins-tructions de l’exemple 15.4 est représenté dans la figure 15.2.

Page 326: Cours Ada de A à Z

TYPE ACCÈS 326

Figure 15.2 Effet des instructions de l’exemple 15.4.

15.2.6 Affectation

L’affectation entre variables pointeurs s’effectue comme d’habitude; la seule

Pt_Integer

Pointeurs Variables pointées

5

Pt_Tableau

0 0 0 0 1 2 1 2 1

Instruction 1:

Instruction 2: la variable Entier reçoit la valeur 14

Instruction 3: affichage de la valeur 5

Instruction 4:

Pt_Tableau

5 0 0 0 1 2 1 2 1

Instruction 5:

Instruction 6: affichage successif des valeurs 5 0 0 0 1 2 1 2 1

Instruction 7:

Pt_Article

Instruction 8:

Pt_Article

Instruction 9: affichage successif des valeurs 5 0 0 0 1 2 1 2 1

10

0 0 0 0 0 0 0 0 0

10

5 0 0 0 1 2 1 2 1

Page 327: Cours Ada de A à Z

TYPE ACCÈS 327

contrainte réside dans le fait que la variable pointeur et l’expression doivent êtredu même type pointeur. Seul fait marquant, il ne faut pas confondre affectation depointeurs et affectation de variables pointées. L’affectation d’un pointeur remplacel’adresse contenue dans la variable affectée, alors que l’affectation d’une variablepointée change la valeur de la variable pointée affectée sans modifier son adresse.

Exemple 15.5 Affectation de pointeurs et de variables pointées

-- On utilise les types de l'exemple 15.1Pt_Integer : T_Pt_Integer := new Integer'(5);-- Trois variables

-- pointeursAutre_Pt_Integer : T_Pt_Integer;Pt_Float : T_Pt_Float; -- 1,situation initiale...Autre_Pt_Integer := Pt_Integer; -- 2Pt_Integer.all := 1; -- 3-- que vaut Autre_Pt_Integer.all? -- 4

Autre_Pt_Integer := new Integer; -- 5Autre_Pt_Integer.all := Pt_Integer.all; -- 6

Pt_Float := Pt_Integer; -- 7,erreur de typePt_Float.all := 0.0; -- 8,leve l'exception

-- Constraint_Error

L’effet des lignes numérotées de 1 à 6 de l’exemple 15.5 est illustré dans lafigure 15.3 afin de visualiser ce qui se passe. L’instruction 7 est non compilable carles deux variables pointeurs sont de types différents. L’instruction 8 pourras’exécuter mais provoquera Constraint_Error car le pointeur Pt_Floatpossède la valeur null (§ 15.2.2) et ne désigne donc aucune variable pointée!

15.2.7 Pièges dans l’utilisation de pointeurs et de variables pointées

L’instruction 8 de l’exemple 15.5 permet de relever que l’affectation, et plusgénéralement toute tentative de modification de pointeurs ou de variables pointées,recèle des pièges dans lesquels tout programmeur est tombé à plusieurs reprises.Ces pièges sont d’autant plus méchants qu’en général le compilateur ne les détectepas car ils surgissent uniquement lors de l’exécution du code. Parmi ces piègesclassiques, les plus répandus sont la tentative d’accès à une variable pointée alorsqu’elle n’existe pas (note 15.2) comme dans l’instruction 8 de l’exemple 15.5, laconfusion entre affectation de pointeurs, instruction 2 du même exemple, et devariables pointées, instruction 6 du même exemple également, et finalement lacréation de variables pointées inaccessibles (note 15.3).

Page 328: Cours Ada de A à Z

TYPE ACCÈS 328

Figure 15.3 Effet des lignes 1 à 6 de l’exemple 15.5.

Pt_Integer

Pointeurs Variables pointées

5

Ligne 1: les trois déclarations

Instruction 2:

Autre_Pt_Integer

Pt_Float

Pt_Integer

5

Autre_Pt_Integer

Instruction 3:

Pt_Integer

1

Autre_Pt_Integer

Ligne 4: Autre_Pt_Integer.all vaut aussi 1 car c’est la même variable pointée que Pt_Integer.all

Instruction 5:

Autre_Pt_Integer

?

Instruction 6:

Autre_Pt_Integer

1

Page 329: Cours Ada de A à Z

TYPE ACCÈS 329

NOTE 15.3 Variables pointées inaccessibles.

Une variable pointée devient inaccessible si elle n’est plus référencée, repérée par une autrevariable. De ce fait elle ne sera plus jamais utilisable par le programme et occupera inutilement dela place en mémoire. Cet état de fait découle toujours d’une ou de plusieurs erreurs deprogrammation si l’information contenue dans la variable inaccessible devait encore servir àl’application. De plus, ce gaspillage de mémoire peut conduire dans le pire des cas à un blocagepartiel ou total de cette application.

Une situation typique d’une variable pointée devenue inaccessible (exemple15.6) est illustrée dans la figure 15.4. La similitude avec le début de l’exemple 15.5est frappante et illustre le piège que recèle l’affectation de pointeurs.

Exemple 15.6 Création de variables inaccessibles.

type T_Pt_Integer is access Integer;

Pt_Integer : T_Pt_Integer := new Integer'(5);Autre_Pt_Integer : T_Pt_Integer; -- 1, situation initiale...Pt_Integer := Autre_Pt_Integer; -- 2

Figure 15.4 Situation aux lignes 1 et 2 de l’exemple 15.6.

Pt_Integer

Pointeurs Variables pointées

5

Ligne 1: les deux déclarations

Instruction 2:

Autre_Pt_Integer

Pt_Integer

5Autre_Pt_Integer

variable pointée devenue inaccessible

Page 330: Cours Ada de A à Z

TYPE ACCÈS 330

Il faut absolument éviter les pièges posés par les pointeurs. De plus, en cas debizarreries lors de l’exécution d’un programme, il faut se rappeler que ces piègesexistent. Un examen très soigneux de l’usage des pointeurs dans le code sources’impose.

15.2.8 Types accès et contraintes

Jusqu’à présent, tous les types accès présentés étaient définis sur des typescontraints. Mais un type tableau non contraint (§ 8.2.3) ou un type article àdiscriminants (§ 11.2.1) peut être utilisé comme type pointé dans la définition d’untype accès. Il faut alors simplement se souvenir que c’est à l’élaboration d’unevariable que la ou les contraintes nécessaires sont données. Ici, c’est donc lors del’utilisation de l’allocateur new qu’il faudra se préoccuper de ces contraintes(exemple 15.7).

Les règles régissant une variable pointée d’un type tableau non contraint ouarticle à discriminants sans valeur par défaut sont les mêmes que pour une variablestatique ordinaire. Par contre, le ou les discriminants d’une variable pointée d’untype article à discriminants avec valeur par défaut ne peuvent plus changer une foiscette variable pointée créée.

Exemple 15.7 Type accès et types tableau non contraint ou article à discriminant.

subtype T_Intervalle is Integer range 0..100;

-- Discriminant sans valeur par defauttype T_Article_Sans_Val_Def (Taille : T_Intervalle) is

record

Nombre : Integer;Tab : String (1..Taille);

end record;

-- Discriminant avec valeur par defauttype T_Article_Avec_Val_Def (Taille : T_Intervalle := 10) is

record

Nombre : Integer;Tab : String (1..Taille);

end record;

-- Trois types accestype T_Pt_String is access String; -- String est non contraint

type T_Pt_Article_Sans_Val_Def is access T_Article_Sans_Val_Def;type T_Pt_Article_Avec_Val_Def is access T_Article_Avec_Val_Def;

-- Trois variables pointeursPt_String : T_Pt_String;

Pt_Article_Sans_Val_Def : T_Pt_Article_Sans_Val_Def;Pt_Article_Avec_Val_Def : T_Pt_Article_Avec_Val_Def;

Page 331: Cours Ada de A à Z

TYPE ACCÈS 331

-- Differents cas d'utilisation de l'allocateur:

-- La contrainte est necessairePt_String := new String (1..10);Pt_Article_Sans_Val_Def := new T_Article_Sans_Val_Def (10);

-- La contrainte, necessaire, est donnee par la valeur initialePt_String := new String'("Bonjour"); -- Remarquer l'apostrophePt_Article_Sans_Val_Def :=

new T_Article_Sans_Val_Def'(5, 10, "Hello");

-- La contrainte est possible mais pas necessairePt_Article_Avec_Val_Def := new T_Article_Avec_Val_Def (10);

-- Pas de contrainte, mais le discriminant ne pourra cependant-- pas varierPt_Article_Avec_Val_Def := new T_Article_Avec_Val_Def;Pt_Article_Avec_Val_Def :=

new T_Article_Avec_Val_Def'(5, 10, "Hello");

Page 332: Cours Ada de A à Z

UTILISATION CLASSIQUE DES TYPES ACCÈS 332

15.3 UTILISATION CLASSIQUE DES TYPES ACCÈS

15.3.1 Motivation

Une question légitime se pose après la présentation des notions de base destypes accès: comment créer des structures de taille variable? Jusqu’à présent l’al-location dynamique offre une certaine souplesse pour l’utilisation de la mémoiremais tous les objets présentés dans les exemples ont néanmoins une taille fixée àleur élaboration.

L’idée principale à la base des structures dynamiques consiste à chaîner, relierles variables pointées entre elles de manière à ce que chacune d’elles soit repéréepar une autre variable pointée au moins, à l’exception d’une (ou de plusieurs) deces variables qui est (sont) désignée(s) par une variable pointeur particulière. Ainsi,grâce à cette variable pointeur, l’accès à une (première) variable pointée est pos-sible puis, par l’intermédiaire du chaînage il est possible d’accéder à une autrevariable pointée et ainsi de suite. La structure dynamique est alors composée del’ensemble des variables pointées dont le nombre est quelconque; la taille de cettestructure peut donc varier au cours de l’exécution par création ou destruction devariables pointées.

Comme les variables pointées ne seront plus repérées uniquement par despointeurs mais aussi par d’autres variables pointées, elles seront appelées de ma-nière usuelle variables dynamiques (dynamic variables). Cette appellation seradorénavant utilisée en lieu et place de variables pointées. Par ailleurs, le chaînageentre deux variables dynamiques est habituellement dénommé lien (link) entre cesvariables. Ce lien est aussi un pointeur.

15.3.2 Généralités

Une variable dynamique comporte au moins une information et un lien. L’in-formation est propre à l’application, alors que le lien sert à accéder à une autrevariable dynamique. Du fait de leur structure en champs de types différents, lestypes articles sont donc appropriés pour implémenter une variable dynamique.

De manière générale, une variable dynamique se présente sous la formesuivante:

type T_Variable_Dynamique isrecordInformation : T_Information;-- Un ou plusieurs champs pour l'information-- geree dans la structure dynamiqueLiens : T_Lien;-- Un ou plusieurs liens pour la realisation de-- la structure dynamiqueend record;

Dans sa forme la plus simple, une variable dynamique ne comporte qu’un seullien (fig. 15.5) et le chaînage de telles variables permet de gérer des listes

Page 333: Cours Ada de A à Z

UTILISATION CLASSIQUE DES TYPES ACCÈS 333

dynamiques (dynamic lists). Comme une variable dynamique est toujours repéréepar un pointeur, le type de ce pointeur est le même que celui du lien. Pour une telleliste ce pointeur est naturellement appelé tête (head).

Figure 15.5 Représentation schématique d’une liste dynamique de trois éléments.

Il existe bien d’autres structures dynamiques comme les multilistes, les arbres,les graphes, etc. Leur étude dépasse les objectifs de cet ouvrage.

Tete

Information 1 Information 2 Information 3

Page 334: Cours Ada de A à Z

LISTES DYNAMIQUES 334

15.4 LISTES DYNAMIQUES

15.4.1 Définition

Une liste dynamique est donc une structure de longueur variable et prête àcontenir une suite d’informations. Elle peut être définie sous forme d’unecollection de variables dynamiques où chacune d’elles est un article et où lesinformations sont supposées du type T_Info. L’exemple 15.8 montre une tentativede définition des types nécessaires et du pointeur de tête.

Exemple 15.8 Définitions nécessaires pour la réalisation d’une liste dynamique.

type T_Lien is access T_Element; -- ATTENTION...

type T_Element is -- Pour une variable dynamiquerecord

Information : T_Info; -- L'information, sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Liste_Dynamique is -- Pour une liste dynamiquerecord

Tete : T_Lien; -- Tete et queue de la listeQueue : T_Lien;

end record;

Le type T_Liste_Dynamique (exemple 15.8) devrait permettre la gestiond’une ou de plusieurs listes formées d’une suite d’informations enregistrées dansdes variables dynamiques de type T_Element et de deux pointeurs désignant leursTete et Queue. Ces listes seront toutes initialement vides puisqu’un pointeurpossède la valeur null à sa création. Mais le compilateur va refuser le typeT_Lien car T_Element n’est pas encore déclaré. Comment faire puisque siT_Element est déclaré le premier, c’est alors T_Lien qui n’est pas encore connu?

15.4.2 Prédéclaration d’un type pointé

La dépendance mutuelle de l’exemple 15.8 peut être brisée par uneprédéclaration du type pointé T_Element (exemple 15.9), prédéclaration limitéeau nom du type et, éventuellement, à ses discriminants.

Exemple 15.9 Définitions complètes et correctes pour la réalisation d’une liste dynamique.

type T_Element; -- Predeclarationtype T_Lien is access T_Element; -- Le type acces

type T_Element is -- Pour une variable dynamiquerecord

Information : T_Info; -- L'information, sur un seul champ

Page 335: Cours Ada de A à Z

LISTES DYNAMIQUES 335

Suivant : T_Lien; -- Pour reperer l'element qui suitend record;

type T_Liste_Dynamique is -- Pour une liste dynamiquerecord

Tete : T_Lien; -- Tete et queue de la listeQueue : T_Lien;

end record;

La prédéclaration, appelée aussi déclaration incomplète, et la déclarationcomplète du type T_Element doivent se situer dans la même partie déclarative, àune exception près (§ 16.2.1). De plus, entre la prédéclaration et la déclarationcomplète, l’identificateur T_Element ne peut s’utiliser que comme type pointé.

Si le type pointé possède des discriminants, ils peuvent figurer ou non dans laprédéclaration. Cette distinction, utile si le type pointé est privé (§ 16.2.1), peut êtreignorée ici.

15.4.3 Gestion, manipulations de base

Les généralités mentionnées pour les listes (sect. 14.2) s’appliquent évidem-ment aux listes dynamiques. Comme pour les listes statiques, seules les queues etles piles dynamiques vont être étudiées.

Page 336: Cours Ada de A à Z

QUEUES DYNAMIQUES 336

15.5 QUEUES DYNAMIQUES

15.5.1 Définition et créationLa définition de cette structure est identique à celle de l’exemple 14.2 pour les

listes quelconques.

Exemple 15.10 Définition d’une queue dynamique.

type T_Element; -- Predeclaration

type T_Lien is access T_Element; -- Le type acces

type T_Element is -- Pour une variable dynamique

record

Information : T_Info; -- L'information, sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Queue_Dynamique is -- Pour une queue dynamique

record

Tete : T_Lien; -- Tete et queue de la queueQueue : T_Lien;

end record;

Le type T_Queue_Dynamique défini dans l’exemple 15.10 permet ladéclaration (création) d’une ou de plusieurs queues formées d’une suited’informations enregistrées dans des variables dynamiques de type T_Element etde deux pointeurs désignant leur Tete et leur Queue. Ces queues seront toutesinitialement vides car Tete et Queue ont la valeur null à la déclaration (fig. 15.6).

Figure 15.6 Définition schématique d’une queue dynamique initialement vide.

Une queue dynamique peut maintenant être créée par une simple déclaration devariable de type T_Queue_Dynamique.

15.5.2 Manipulations de base

Queue dynamique

Queue

Tete

Page 337: Cours Ada de A à Z

QUEUES DYNAMIQUES 337

Les opérations de base pour une queue dynamique s’implémentent sansdifficulté particulière. Comme dans la variante statique, elles consistent plusprécisément à:

• insérer une information en queue;• supprimer l’information de tête;• modifier l’information présente en tête de la queue;• consulter l’élément de tête;• savoir si la queue est vide;• rechercher une information dans la queue;• parcourir la queue, c’est-à-dire effectuer un traitement sur chacun de ses

éléments.

La spécification d’un paquetage de gestion de queues dynamiques est donnédans l’exemple 15.11. Le corps de ce paquetage Queues_Dynamiques contiendrauniquement les corps des opérations énumérées dans la spécification, corpsprésentés dans les paragraphes qui suivent.

Exemple 15.11 Spécification d’un paquetage de gestion de queues dynamiques.

-- Bases de gestion de queues dynamiquespackage Queues_Dynamiques is

type T_Info is ...; -- Depend de l'application

type T_Element; -- Predeclaration

type T_Lien is access T_Element; -- Le type acces

type T_Element is -- Pour une variable dynamiquerecord

Information : T_Info; -- L'information sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Queue_Dynamique is -- Pour une queue dynamiquerecord

Tete : T_Lien; -- Tete et queue de la queueQueue : T_Lien;

end record;

Queue_Vide : exception; -- Levee si la queue est vide-------------------------------------------------------------- Insere Info en queue de La_Queueprocedure Inserer ( La_Queue : in out

T_Queue_Dynamique;Info : in T_Info );

-------------------------------------------------------------- Supprime l'information (en tete de La_Queue) qui est rendue-- dans Info. Leve Queue_Vide si la suppression est impossible-- (la queue est vide)

Page 338: Cours Ada de A à Z

QUEUES DYNAMIQUES 338

procedure Supprimer ( La_Queue : in out T_Queue_Dynamique;

Info : out T_Info );

-------------------------------------------------------------- Change l'information en tete de La_Queue. Leve Queue_Vide-- si la modification est impossible (la queue est vide)procedure Modifier ( La_Queue : in T_Queue_Dynamique;

Info : in T_Info );

-------------------------------------------------------------- Retourne l'information en tete de La_Queue. Leve Queue_Vide-- si la consultation est impossible (la queue est vide)function Tete ( La_Queue : T_Queue_Dynamique ) return T_Info;

-------------------------------------------------------------- Retourne True si La_Queue est vide, False sinonfunction Vide ( La_Queue : T_Queue_Dynamique ) return Boolean;

-------------------------------------------------------------- Retourne True si l'information Info est presente dans la-- queue, False sinonfunction Recherche ( La_Queue : T_Queue_Dynamique;

Info : T_Info ) return Boolean;

-------------------------------------------------------------- Effectue un traitement sur tous les elements de la queueprocedure Parcourir ( La_Queue : in T_Queue_Dynamique );------------------------------------------------------------

end Queues_Dynamiques;

15.5.3 InsertionPour l’insertion, les informations sont chaînées, reliées les unes aux autres,

selon la politique FIFO (§ 14.2.2). Il faut mettre en évidence les pointeurs Tete etQueue parce que, tels qu’ils sont utilisés, l’insertion et la suppression vont êtrefaciles à réaliser (fig. 15.7).

Figure 15.7 Queue dynamique après trois insertions.

Queue dynamique

Queue

Tete Information 1 Information 2 Information 3

Page 339: Cours Ada de A à Z

QUEUES DYNAMIQUES 339

Exemple 15.12 Procédure d’insertion d’une information dans une queue dynamique.

-- Insere Info en queue de La_Queueprocedure Inserer ( La_Queue : in out

T_Queue_Dynamique;Info : in T_Info ) is

Pt_Nouveau : T_Lien := new T_Element'(Info, null);-- 1

begin -- Inserer

-- Placer le nouvel element apres celui en queue; il faut-- distinguer si la queue est vide ou nonif La_Queue.Tete = null then -- Queue vide

La_Queue.Tete := Pt_Nouveau; -- 2

else -- Queue non vide

La_Queue.Queue.Suivant := Pt_Nouveau; -- 3

end if;

-- Modifier le chainage; le nouvel element devient celui en-- queueLa_Queue.Queue := Pt_Nouveau; -- 4

end Inserer; -- 5

La procédure Inserer (exemple 15.12) est plus simple que dans le cas statique(§ 14.4.3), mais elle mérite néanmoins quelques explications.

L’insertion du premier élément est un cas particulier car le pointeur Tete nerepère encore aucune variable dynamique; il faut donc simplement lui donnerl’adresse du premier élément. Dans le cas d’une queue non vide, le nouvel élémentdoit suivre (politique FIFO) le dernier existant, ce qui explique que ce nouvelélément est chaîné derrière l’élément de queue. Puis, dans tous les cas, l’élémentde queue est le nouvel élément. Les figures 15.8 et 15.9 illustrent les deux casmentionnés.

Page 340: Cours Ada de A à Z

QUEUES DYNAMIQUES 340

Figure 15.8 Insertion d’un élément à partir d’une queue dynamique vide.

Information 1

Ligne 1: création d’un nouvel élément

Queue

Tete

Pt_Nouveau

Instruction 2:

Information 1

Queue

Tete

Pt_Nouveau

Instruction 4:

Information 1

Queue

Tete

Pt_Nouveau

Ligne 5:

Information 1

Queue

Tete

L’instruction 3 ne s’exécute pas

Page 341: Cours Ada de A à Z

QUEUES DYNAMIQUES 341

Figure 15.9 Insertion d’un élément à partir d’une queue dynamique non vide (un élément existant).

Information 2

Instruction 1: création d’un nouvel élément

Pt_Nouveau

Instruction 3:

Information 2

Pt_Nouveau

Instruction 4:

Information 1

Queue

Tete

Information 1

Queue

Tete

Information 2

Pt_Nouveau

Information 1

Queue

Tete

Ligne 5:

Information 2Information 1

Queue

Tete

L’instruction 2 ne s’exécute pas

Page 342: Cours Ada de A à Z

QUEUES DYNAMIQUES 342

15.5.4 SuppressionPour la suppression, les informations sont extraites les unes après les autres, en

commençant par l’information de tête (fig. 15.19).

Figure 15.10 Queue dynamique après N insertions et N-1 suppressions.

Lorsque la dernière variable dynamique a été supprimée de la queue, toutetentative de suppression doit lever l’exception Queue_Vide (fig. 15.11).

Figure 15.11 Tentative de suppression alors que la queue dynamique est vide.

La réalisation de la procédure de suppression est illustrée dans l’exemple 15.13.La place mémoire de la variable dynamique éliminée de la queue devrait êtrerécupérée (sect. 15.7), ce qui n’est pas explicitement réalisé dans cet exemple. Unramasse-miettes serait ici très utile.

Exemple 15.13 Procédure de suppression d’une information dans une queue dynamique.

-- Supprime l'information (en tete de La_Queue) qui est rendue-- dans Info. Leve Queue_Vide si la suppression est impossible (la-- queue est vide)procedure Supprimer ( La_Queue : in out

T_Queue_Dynamique;Info : out T_Info ) is

begin -- Supprimer -- 1-- Cas si la queue est videif La_Queue.Tete = null then

raise Queue_Vide;

Queue dynamique

Information N

Queue

Tete

Queue dynamique

Q u e u e _ VideQueue

Tete

Page 343: Cours Ada de A à Z

QUEUES DYNAMIQUES 343

end if;

-- Recuperer l'information en teteInfo := La_Queue.Tete.Information;

-- L'element de tete est l'element supprime, et celui qui-- le suit est dorenavant en tete. La place memoire n'est-- pas recuperee

La_Queue.Tete := La_Queue.Tete.Suivant; -- 2

-- Cas du dernier element supprimeif La_Queue.Tete = null then

La_Queue.Queue := null; -- 3

end if;

end Supprimer;

Cette procédure supprime l’élément de tête de la queue (fig. 15.12). Lorsque ledernier élément a été éliminé (fig. 15.13), les pointeurs Tete et Queue retrouventla valeur initiale null. Les variables enlevées de la queue deviennent inacces-sibles, ce qui peut être toléré puisque l’information contenue n’est plus utile.

Figure 15.12 Suppression d’un élement à partir d’une queue dynamique de longueur 2.

Ligne 1:

Information 2Information 1

Queue

Tete

Instruction 2:

Information 2Information 1

Queue

Tete

L’instruction 3 ne s’exécute pas

Page 344: Cours Ada de A à Z

QUEUES DYNAMIQUES 344

Figure 15.13 Suppression du dernier élement d’une queue dynamique.

15.5.5 ModificationLa modification d’une information n’est possible qu’en tête de la queue, par

définition de celle-ci (fig. 15.14).

Figure 15.14 Modification de l’information de tête dans une queue dynamique.

Ligne 1:

Information 2

Queue

Tete

Instruction 2:

Queue

Tete Information 2

Instruction 3:

Queue

Tete Information 2

Queue dynamique

Information 2Information 1

Queue

Tete

Information à modifier

Page 345: Cours Ada de A à Z

QUEUES DYNAMIQUES 345

Lorsque la queue est vide, toute tentative de modification doit lever l’exceptionQueue_Vide pour en avertir l’auteur (fig. 15.15).

Figure 15.15 Tentative de modification alors que la queue dynamique est vide.

La réalisation de la procédure de modification est facile, illustrée dansl’exemple 15.14.

Exemple 15.14 Procédure de modification d’une information dans une queue dynamique.

-- Change l'information en tete de La_Queue. Leve Queue_Vide si la-- modification est impossible (la queue est vide)procedure Modifier ( La_Queue : in T_Queue_Dynamique;

Info : in T_Info ) isbegin -- Modifier

-- Cas si la queue est videif La_Queue.Tete = null then

raise Queue_Vide;end if;

-- Modifier l'information en teteLa_Queue.Tete.Information := Info;

end Modifier;

15.5.6 ConsultationLa consultation d’une information n’est possible qu’en tête de la queue, par

définition de celle-ci. Cette opération est donc très proche de la modification, cequi permet de donner directement la fonction adéquate dans l’exemple 15.15.

Exemple 15.15 Fonction de consultation d’une information dans une queue dynamique.

-- Retourne l'information en tete de La_Queue. Leve Queue_Vide si-- la consultation est impossible (la queue est vide)

Queue dynamique

Q u e u e _ VideQueue

Tete

Page 346: Cours Ada de A à Z

QUEUES DYNAMIQUES 346

function Tete ( La_Queue : T_Queue_Dynamique ) return T_Info isbegin -- Tete

-- Cas si la queue est videif La_Queue.Tete = null then

raise Queue_Vide;end if;

-- Retourner l'information en tetereturn La_Queue.Tete.Information;

end Tete;

15.5.7 Queue videLe fait qu’une queue soit vide implique que tous les éléments insérés ont été

extraits, ou éventuellement que la queue n’a pas encore servi. Cette information esttrès souvent utilisée dans les algorithmes qui mettent en œuvre une ou plusieursqueues. La fonction correspondante est présentée dans l’exemple 15.16.

Exemple 15.16 Fonction retournant l’état (vide/non vide) d’une queue dynamique.

-- Retourne True si La_Queue est vide, False sinonfunction Vide (La_Queue : T_Queue_Dynamique) return Boolean isbegin -- Vide

-- La queue est vide si le pointeur de tete ne repere aucun-- elementreturn La_Queue.Tete = null;

end Vide;

15.5.8 RechercheL’algorithme de recherche séquentielle (§ 14.4.8) s’applique également aux

queues dynamiques. L’exemple 15.17 donne la fonction réalisant cette opération.

Exemple 15.17 Fonction de recherche d’une information dans une queue dynamique.

-- Retourne True si l'information Info est presente dans la queue,-- False sinonfunction Recherche ( La_Queue : T_Queue_Dynamique;

Info : T_Info ) return Boolean is

-- Repere l'element courantPt_Courant : T_Lien := La_Queue.Tete;

begin -- Recherche

-- Tant que la queue n'est pas depassee et l'information pas

Page 347: Cours Ada de A à Z

QUEUES DYNAMIQUES 347

-- trouveewhile Pt_Courant /= null and then

Info /= Pt_Courant.Information loop

-- Passer a l'element suivantPt_Courant := Pt_Courant.Suivant;

end loop;

-- Si la fin de la queue n'est pas depassee, l'information a-- ete trouveereturn Pt_Courant /= null;

end Recherche;

La présence de la forme de contrôle en raccourci and then (§ 3.4.3) est le seulpoint délicat de cet exemple; elle est nécessaire pour éviter de consulterl’Information si Pt_Courant ne pointe sur aucun élément. A noter que le casd’une queue vide ne provoque cette fois aucune exception; la recherche d’uneinformation dans une queue vide aboutit immédiatement et simplement à la valeurFalse (information introuvable), ce qui est conforme à la logique et à la pratiquecourante.

15.5.9 Parcours

Le parcours d’une queue dynamique est très semblable à celui présenté pourune queue statique (§ 14.4.9). L’exemple 15.18 présente une procédure réalisant untel parcours où l’on suppose qu’une procédure Traiter effectue le traitementvoulu.

Exemple 15.18 Procédure de parcours d’une queue dynamique.

-- Effectue un traitement sur tous les elements de la queue.-- Le traitement est suppose fait dans la procedure interne-- Traiterprocedure Parcourir ( La_Queue : in T_Queue_Dynamique ) is

-- Repere l'element courantPt_Courant : T_Lien := La_Queue.Tete;

procedure Traiter ( Info : in out T_Info ) is ... end Traiter;

begin -- Parcourir

-- Prendre les elements les uns apres les autreswhile Pt_Courant /= null loop

-- Effectuer le traitementTraiter ( Pt_Courant.Information );

-- Passer a l'element suivantPt_Courant := Pt_Courant.Suivant;

end loop;

Page 348: Cours Ada de A à Z

QUEUES DYNAMIQUES 348

end Parcourir;

Comme pour la recherche et pour les mêmes raisons, le cas d’une queue videne provoque aucune exception.

15.5.10 Exemple d’utilisation d’une queue dynamique

Lors de la demande d’impression d’un fichier sur une imprimante partagée,cette impression n’est pas toujours réalisée immédiatement (l’imprimante peut êtredéjà occupée à imprimer un texte). La demande est alors mise en queue, en généralderrière toutes les demandes en cours et non encore satisfaites. Or la queue de cesdemandes est gérée dynamiquement car le nombre maximal de demandesd’impression simultanées n’est pas quantifiable. Une gestion statique de la queuepourrait aboutir à des messages d’avertissement aux utilisateurs de l’imprimantealors que leur demande d’impression semble parfaitement acceptable. De telsmessages risqueraient de ne pas donner une bonne image du logiciel de gestion del’imprimante...

Page 349: Cours Ada de A à Z

PILES DYNAMIQUES 349

15.6 PILES DYNAMIQUES

15.6.1 Définition et créationLa définition de la structure de pile dynamique ressemble fortement à celle

d’une queue dynamique (§. 15.5.1). La seule différence réside en l’absence dudeuxième champ pointeur du type T_Pile_Dynamique puisqu’une pile n’estaccédée qu’en un seul endroit: au sommet.

Exemple 15.19 Définition d’une pile dynamique.

type T_Element; -- Predeclaration

type T_Lien is access T_Element; -- Le type acces

type T_Element is -- Pour une variable dynamique

record

Information : T_Info; -- L'information, sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Pile_Dynamique is -- Pour une pile dynamique

record

Sommet : T_Lien; -- Sommet de la pile

end record;

Le type T_Pile_Dynamique défini dans l’exemple 15.19 permet ladéclaration (création) d’une ou de plusieurs queues formées d’une suited’informations enregistrées dans des variables dynamiques de type T_Element etd’un pointeur désignant leur Sommet. Ces piles seront toutes initialement vides carSommet a la valeur null à la déclaration (fig. 15.16).

Figure 15.16 Définition schématique d’une pile dynamique initialement vide.

Le champ pointeur Suivant repère en fait l’élément au-dessous de lui si l’onimagine les éléments de la pile empilés les uns sur les autres comme une piled’assiettes. Par contre, pour les représentations schématiques, une pile est repré-sentée horizontalement, comme une liste.

Pile dynamique

Sommet

Page 350: Cours Ada de A à Z

PILES DYNAMIQUES 350

L’utilisation d’un article à un seul champ pour le type T_Pile_Dynamique sejustifie si la définition de ce type doit être étendue avec des champssupplémentaires, par exemple la longueur de la pile. Dans le cas contraire, unedéfinition simplifiée comme celle de l’exemple 15.20 est tout à fait adéquate. Dansl’idée de faciliter l’extension de la définition, celle de l’exemple 15.19 seraconservée pour la suite.

Exemple 15.20 Définition simplifiée d’une pile dynamique.

type T_Element; -- Predeclaration

-- Le type d'une pile dynamiquetype T_Pile_Dynamique is access T_Element;

type T_Element is -- Pour une variable dynamique

record

Information : T_Info; -- L'information, sur un seul-- champ

Suivant : T_Pile_Dynamique; -- Pour reperer l'element qui-- suit

end record;

Une pile dynamique peut maintenant être créée par une simple déclaration devariable de type T_Pile_Dynamique.

15.6.2 Manipulations de baseLes opérations de base (sect. 14.2) pour une pile dynamique s’implémentent

sans difficulté particulière et sont très semblables à celles d’une queue. Laspécification d’un paquetage de gestion de piles dynamiques est donné dansl’exemple 15.21.

Exemple 15.21 Spécification d’un paquetage de gestion de piles dynamiques.

-- Bases de gestion de piles dynamiquespackage Piles_Dynamiques is

type T_Info is ...; -- Depend de l'application

type T_Element; -- Predeclaration

type T_Lien is access T_Element;-- Le type acces

type T_Element is -- Pour une variable dynamiquerecord

Information : T_Info; -- L'information sur un seul-- champ

Suivant : T_Lien; -- Pour reperer l'element qui-- suit

Page 351: Cours Ada de A à Z

PILES DYNAMIQUES 351

end record;

type T_Pile_Dynamique is -- Pour une pile dynamiquerecord

Sommet : T_Lien; -- Sommet de la pile

end record;

Pile_Vide : exception; -- Levee si la pile est vide

-------------------------------------------------------------- Insere Info au sommet de Pileprocedure Empiler ( Pile : in out T_Pile_Dynamique;

Info : in T_Info );

-------------------------------------------------------------- Supprime l'information (au sommet de Pile) qui est rendue-- dans Info. Leve Pile_Vide si la suppression est impossible-- (la pile est vide)procedure Desempiler ( Pile : in out T_Pile_Dynamique;

Info : out T_Info );

-------------------------------------------------------------- Change l'information du sommet de Pile. Leve Pile_Vide si la-- modification est impossible (la pile est vide)procedure Modifier ( Pile : in T_Pile_Dynamique;

Info : in T_Info );

-------------------------------------------------------------- Retourne l'information du sommet de Pile. Leve Pile_Vide si-- la consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile_Dynamique ) return T_Info;

-------------------------------------------------------------- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile_Dynamique ) return Boolean;

-------------------------------------------------------------- Retourne True si l'information Info est presente dans la -- pile, False sinonfunction Recherche ( Pile : T_Pile_Dynamique;

Info : T_Info ) return Boolean;-- Effectue un traitement sur tous les elements de la pileprocedure Parcourir ( Pile : in T_Pile_Dynamique );------------------------------------------------------------

end Piles_Dynamiques;

Le corps de ce paquetage Piles_Dynamiques contiendra uniquement lescorps des opérations énumérées dans la spécification, corps présentés dans lesparagraphes qui suivent.

15.6.3 InsertionPour l’insertion les informations sont chaînées, reliées les unes aux autres,

Page 352: Cours Ada de A à Z

PILES DYNAMIQUES 352

selon la politique LIFO (§ 14.2.2). Une fois le premier élément placé, chaquenouvel élément vient en tête et repousse les éléments existants. Insérer une infor-mation est appelé empiler (push). La figure 15.17 présente l’état d’une pile aprèstrois insertions, alors que la procédure d’insertion est illustrée dans l’exemple15.22.

Figure 15.17 Pile dynamique après trois insertions.

Exemple 15.22 Procédure d’insertion d’une information dans une pile dynamique.

-- Insere Info au sommet de Pileprocedure Empiler ( Pile : in out T_Pile_Dynamique;

Info : in T_Info ) is

-- Pour reperer le nouvel elementPt_Nouveau : T_Lien := new T_Element'(Info, null);-- 1

begin -- Inserer

Pt_Nouveau.Suivant := Pile.Sommet; -- 2

Pile.Sommet := Pt_Nouveau; -- 3

end Empiler; -- 4

Après sa création, le nouvel élément devra précéder (politique LIFO) le premierexistant, ce qui explique que le sommet actuel de la pile est chaîné au nouvelélément. Puis le nouvel élément devient l’élément au sommet.

Les figures 15.18 et 15.19 illustrent l’empilement de deux éléments, la premièrefois dans une pile vide, la seconde fois lorsque la pile possède déjà un élément.

Pile dynamique

Sommet Information 3 Information 2 Information 1

Page 353: Cours Ada de A à Z

PILES DYNAMIQUES 353

Figure 15.18 Insertion d’un élément à partir d’une pile dynamique vide.

Information 1

Ligne 1, création d’un nouvel élément:

Pt_Nouveau

Instruction 2: pas de changement sur le schéma précédent

Instruction 3:

Ligne 4:

Sommet

Information 1

Pt_Nouveau

Sommet

Information 1Sommet

Page 354: Cours Ada de A à Z

PILES DYNAMIQUES 354

Figure 15.19 Insertion d’un élément à partir d’une pile dynamique non vide (un élément existant).

Information 2

Instruction 1, création d’un nouvel élément:

Pt_Nouveau

Instruction 2:

Instruction 3:

Ligne 4:

Information 1Sommet

Information 2

Pt_Nouveau

Information 1Sommet

Information 2

Pt_Nouveau

Information 1Sommet

Information 2Sommet Information 1

Page 355: Cours Ada de A à Z

PILES DYNAMIQUES 355

Le code de la procédure Empiler donnée dans l’exemple 15.22 peut (et doit)être condensé en une seule instruction (exemple 15.23). La forme développée a étéprésentée pour illustrer graphiquement les étapes de l’insertion.

Exemple 15.23 Procédure condensée d’insertion d’une information dans une pile dynamique.

-- Insere Info au sommet de Pileprocedure Empiler ( Pile : in out T_Pile_Dynamique;

Info : in T_Info ) is

begin -- Empiler

Pile.Sommet := new T_Element'(Info, Pile.Sommet);

end Empiler;

15.6.4 Suppression

Pour la suppression, les informations sont extraites les unes après les autres, encommençant par l’information au sommet (fig. 15.20). Supprimer une informationest communément appelé désempiler (pop).

Figure 15.20 Pile dynamique après N insertions et N-3 suppressions.

Lorsque la dernière information de la pile a été désempilée, toute tentative desuppression doit lever l’exception Pile_Vide pour en avertir l’auteur (fig. 15.21).Cette situation est en fait plus généralement celle de la pile vide.

Figure 15.21 Tentative de suppression alors que la pile est vide.

Pile dynamique

Sommet Information 3 Information 2 Information 1

Pile dynamique

P i l e _ VideSommet

Page 356: Cours Ada de A à Z

PILES DYNAMIQUES 356

La réalisation de la procédure de suppression est alors facile, illustrée dansl’exemple 15.24. La place mémoire de la variable dynamique éliminée de la queuedevrait être récupérée (sect. 15.7), ce qui n’est pas explicitement réalisé dans cetexemple (fig. 15.22); un ramasse-miettes serait ici très utile.

Exemple 15.24 Procédure de suppression d’une information dans une pile dynamique.

-- Supprime l'information (au sommet de Pile) qui est rendue dans-- Info. Leve Pile_Vide si la suppression est impossible (la pile-- est vide)procedure Desempiler ( Pile : in out T_Pile_Dynamique;

Info : out T_Info ) isbegin -- Desempiler -- 1

-- Cas si la pile est videif Pile.Sommet = null then

raise Pile_Vide;end if;

-- Recuperer l'information au sommetInfo := Pile.Sommet.Information;

-- L'element au sommet est supprime, celui qui le suit est-- dorenavant au sommet; la place memoire n'est pas recupereePile.Sommet := Pile.Sommet.Suivant; -- 2

end Desempiler;

Figure 15.22 Suppression d’un élément à partir d’une pile dynamique de longueur 2.

Le cas particulier de suppression du dernier élément ne pose aucune difficulté;le champ pointeur Sommet retrouve simplement la valeur initiale null.

Ligne 1:

Instruction 2:

Information 2Sommet Information 1

Information 2Sommet Information 1

Page 357: Cours Ada de A à Z

PILES DYNAMIQUES 357

15.6.5 ModificationLa modification d’une information n’est possible qu’au sommet de la pile, par

définition de celle-ci (fig. 15.23). Lorsque la pile est vide, toute tentative de mo-dification doit lever l’exception Pile_Vide pour en avertir l’auteur (fig. 15.24).Enfin, la réalisation de la procédure de modification est facile, illustrée dans l’ex-emple 15.25.

Figure 15.23 Modification de l’information au sommet dans une pile dynamique.

Figure 15.24 Tentative de modification alors que la pile dynamique est vide.

Exemple 15.25 Procédure de modification d’une information dans une pile dynamique.

-- Change l'information du sommet de Pile. Leve Pile_Vide si la-- modification est impossible (la pile est vide)procedure Modifier ( Pile : in T_Pile_Dynamique;

Info : in T_Info ) isbegin -- Modifier

-- Cas si la pile est videif Pile.Sommet = null then

raise Pile_Vide;end if;

Pile dynamique

Information à modifier

Sommet Information 3 Information 2 Information 1

Pile dynamique

P i l e _ VideSommet

Page 358: Cours Ada de A à Z

PILES DYNAMIQUES 358

-- Modifier l'information du sommetPile.Sommet.Information := Info;

end Modifier;

15.6.6 ConsultationLa consultation d’une information n’est possible qu’au sommet de la pile, par

définition de celle-ci. Cette opération est donc très proche de la modification, cequi permet de donner directement la fonction adéquate dans l’exemple 15.26.

Exemple 15.26 Fonction de consultation d’une information dans une pile dynamique.

-- Retourne l'information du sommet de Pile. Leve Pile_Vide si la-- consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile_Dynamique ) return T_Info isbegin -- Sommet

-- Cas si la pile est videif Pile.Sommet = null then

raise Pile_Vide;end if;

-- Retourner l'information du sommetreturn Pile.Sommet.Information;

end Sommet;

15.6.7 Pile vide

Le fait qu’une pile soit vide implique que tous les éléments insérés ont étéextraits, ou éventuellement que la pile n’a encore pas servi. Cette information esttrès souvent utilisée dans les algorithmes mettant en œuvre une ou plusieurs piles.La fonction correspondante est présentée dans l’exemple 15.27.

Exemple 15.27 Fonction retournant l’état (vide/non vide) d’une pile dynamique.

-- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile_Dynamique ) return Boolean isbegin -- Vide

-- La pile est vide si le pointeur du sommet ne repere aucun-- elementreturn Pile.Sommet = null;

end Vide;

Page 359: Cours Ada de A à Z

PILES DYNAMIQUES 359

15.6.8 RechercheL’algorithme de recherche séquentielle (§ 14.4.8) s’applique également aux

piles dynamiques. L’exemple 15.28 donne la fonction réalisant cette opération.

Exemple 15.28 Fonction de recherche d’une information dans une pile dynamique.

-- Retourne True si l'information Info est presente dans la pile,-- False sinonfunction Recherche ( Pile : T_Pile_Dynamique;

Info : T_Info ) return Boolean is

-- Repere l'element courantPt_Courant : T_Lien := Pile.Sommet;

begin -- Recherche

-- Tant que la queue n'est pas depassee et l'information pas-- trouveewhile Pt_Courant /= null and then

Info /= Pt_Courant.Information loop

-- Passer a l'element suivantPt_Courant := Pt_Courant.Suivant;

end loop;

-- Si le dernier element n'a pas ete depasse, l'information a-- ete trouveereturn Pt_Courant /= null;

end Recherche;

La présence de la forme de contrôle en raccourci and then (§ 3.4.3) est le seulpoint délicat de cet exemple; elle est nécessaire pour éviter de consulterl’Information si Pt_Courant ne pointe sur aucun élément. A noter que le casd’une pile vide ne provoque cette fois aucune exception; la recherche d’uneinformation dans une pile vide aboutit immédiatement et simplement à la valeurFalse (information introuvable), ce qui est conforme à la logique et à la pratiquecourante.

15.6.9 ParcoursLe parcours d’une pile est semblable à celui appliqué à une queue (§ 14.4.9) en

commençant par le sommet de la pile, avec la même remarque concernant letraitement à effectuer, à savoir que ce traitement peut ou non modifier les élémentsou la structure de la pile.

L’exemple 15.29 présente une procédure réalisant un parcours où l’on supposequ’une procédure Traiter effectue le traitement voulu.

Page 360: Cours Ada de A à Z

PILES DYNAMIQUES 360

Exemple 15.29 Procédure de parcours d’une pile dynamique.

-- Effectue un traitement sur tous les elements de la pile. Le-- traitement est suppose fait dans la procedure interne Traiterprocedure Parcourir ( Pile : in T_Pile_Dynamique ) is

-- Repere l'element courantPt_Courant : T_Lien := Pile.Sommet;

procedure Traiter ( Info : in out T_Info ) is ... end Traiter;

begin -- Parcourir

-- Prendre les elements les uns apres les autreswhile Pt_Courant /= null loop

-- Effectuer le traitementTraiter ( Pt_Courant.Information );

-- Passer a l'element suivantPt_Courant := Pt_Courant.Suivant;

end loop;

end Parcourir;

Comme pour la recherche et pour les mêmes raisons, le cas d’une pile vide neprovoque aucune exception.

15.6.10 Exemple d’utilisation d’une pile dynamiqueParmi toutes les applications simples de l’utilisation d’une pile dynamique,

l’inversion des éléments d’une suite est probablement l’une des plus connues. Ils’agit simplement de prendre tous les éléments et de les restituer dans l’ordreinverse. L’exemple 15.30 montre comment cet algorithme fonctionne en supposantque les éléments (de type T_Info) présents dans un tableau de type T_Suite sontpassés en paramètre de la procédure Inverser. L’inversion est réalisée grâce à lagestion LIFO (§ 14.2.2) de la pile.

Exemple 15.30 Inversion des éléments d’une suite grâce à une pile dynamique.

-- Inverse l'ordre des elements de Suiteprocedure Inverser ( Suite : in out T_Suite ) is

-- Pour l'inversionPile : Piles_Dynamiques.T_Pile_Dynamique;

begin -- Inverser

-- Prendre les elements les uns apres les autresfor Position in Suite'Range loop

-- Empiler l'element courantPiles_Dynamiques.Empiler ( Pile, Suite (Position) );

end loop;

Page 361: Cours Ada de A à Z

PILES DYNAMIQUES 361

-- Retirer les elements dans l'ordre inversefor Position in Suite'Range loop

-- Desempiler un elementPiles_Dynamiques.Desempiler ( Pile, Suite (Position) );

end loop;

end Inverser;

Page 362: Cours Ada de A à Z

RESTITUTION DE LA MÉMOIRE 362

15.7 RESTITUTION DE LA MÉMOIRE

A chaque appel de l’allocateur new, une nouvelle portion mémoire (variabledynamique) est réservée pour l’application. Comme les sections et paragraphesprécédents l’ont montré, cette mémoire est utilisée par exemple pour construire desstructures de données dynamiques. Il se pose alors la question de la libération(restitution) de cette mémoire lorsque celle-ci n’est plus utilisée.

Une fois l’application terminée, le système d’exploitation a naturellement lacharge de récupérer toute la mémoire dont l’application a eu besoin. Mais en coursd’exécution, il peut être pratique de réutiliser des portions mémoire devenuesinaccessibles afin de permettre la création de nouvelles structures sans besoinssupplémentaires en place mémoire. En fait, cette récupération peut s’effectuer àplusieurs niveaux.

Tout d’abord, la place mémoire, occupée par toutes les variables dynamiquesdésignées par des pointeurs d’un type accès donné, est restituée lorsque l’exécutionquitte la portée (sect. 4.4) de ce type accès. Le système s’en occupe lui-même, et iln’y a aucun risque d’obtenir des pointeurs fantômes, c’est-à-dire des pointeurs quirepèrent des variables dynamiques dont l’emplacement mémoire est déjà réutilisépour autre chose! En effet, tous les pointeurs du type accès auront également cesséd’exister lorsque la portée du type sera abandonnée.

Une variable dynamique devenue inaccessible (§ 15.2.7) peut être récupéréepar le système en vue de la réutilisation, dans le même programme, de la placemémoire occupée. On appelle ramasse-miettes (garbage collector) la partie dusystème chargée de cette tâche. La norme Ada permet mais n’impose pas qu’uneimplémentation fournisse un tel ramasse-miettes.

Finalement, le programmeur dispose d’un outil pour gérer lui-même larécupération de la place mémoire occupée par une variable dynamique. Il s’agitd’une unité de bibliothèque (§ 10.6.1), en fait une procédure générique (sect. 17.5)prédéfinie appelée Ada.Unchecked_Deallocation et qui s’utilise comme lemontre l’exemple 15.31.

Exemple 15.31 Utilisation de la procédure générique Ada.Unchecked_Deallocation.

with Ada.Unchecked_Deallocation; -- Pas de use-- ...procedure Exemple_15_31 is

type T_Element is ...; -- Un type quelconquetype T_Pt_Element is access T_Element; -- Le type acces

-- Utilisation de la procedure Ada.Unchecked_Deallocation en-- creant une procedure de liberation de la memoire adaptee aux-- types de cet exempleprocedure Liberer is

Page 363: Cours Ada de A à Z

RESTITUTION DE LA MÉMOIRE 363

new Ada.Unchecked_Deallocation (T_Element, T_Pt_Element);

Pt_Element : T_Pt_Element; -- Une variable auxiliaire

begin -- Exemple_15_31

-- Creation d'une variable dynamiquePt_Element := new T_Element;

-- Utilisation de la variable dynamique...

-- Restitution de la memoire occupee par la variable dynamiqueLiberer ( Pt_Element );...

La création de la procédure Liberer ressemble fortement à l’opérationnécessaire pour effectuer des entrées-sorties sur des valeurs énumérées parexemple (§ 5.2.5) où le nom du type énumératif est placé entre les parenthèses. Ici,il faut mentionner d’abord le nom du type des variables dynamiques, puis le nomdu type accès. Après l’exécution de la procédure Liberer, le pointeurPt_Element obtient la valeur null. Attention cependant à ne pas tomber dans lepiège des pointeurs fantômes comme illustré dans l’exemple 15.32. Dans de telscas, l’exécution du programme devient imprévisible...

Exemple 15.32 Création volontaire d’un pointeur fantôme.

with Ada.Unchecked_Deallocation; -- Pas de use

-- ...procedure Exemple_15_32 is

type T_Element is ...; -- Un type quelconquetype T_Pt_Element is access T_Element; -- Le type acces

-- Utilisation de la procedure Ada.Unchecked_Deallocation en-- creant une procedure de liberation de la memoire adaptee aux-- types de cet exempleprocedure Liberer is

new Ada.Unchecked_Deallocation (T_Element, T_Pt_Element);

Pt_Element : T_Pt_Element; -- Une variable auxiliaire

Fantome : T_Pt_Element; -- Va devenir un pointeur fantome

begin -- Exemple_15_32

-- Creation d'une variable dynamiquePt_Element := new T_Element;

-- Utilisation de la variable dynamique...

-- Les deux pointeurs reperent la meme variable dynamiqueFantome := Pt_Element;

-- Restitution de la memoire occupee par la variable dynamiqueLiberer ( Pt_Element );

Page 364: Cours Ada de A à Z

RESTITUTION DE LA MÉMOIRE 364

...

Dès l’exécution de Liberer, Fantome repère une variable dynamique dontl’emplacement mémoire a peut-être déjà été réutilisé par le système. Il ne se passerien de problématique si une nouvelle adresse (ou la valeur null) est ensuiteaffectée à Fantome, ce qui est couramment réalisé dans la pratique. Par contre, sile programme essaie d’accéder via Fantome à la variable dynamique qui n’existeplus, son comportement n’est plus prédictible.

Page 365: Cours Ada de A à Z

AUTRES ASPECTS DES TYPES ACCÈS 365

15.8 AUTRES ASPECTS DES TYPES ACCÈS

15.8.1 Types accès généralisés

A l’instar d’autres langages, Ada permet la déclaration de pointeurs généraliséssur des objets statiques à condition que ces objets soient prévus pour cela. Desconstantes ou des variables de n’importe quel type simple ou composé, de mêmeque des éléments de tableaux ou des champs d’articles, peuvent ainsi êtreréférencées pourvu que leur déclaration comporte le mot réservé aliased quiindique au compilateur que l’accès à l’entité déclarée va se faire également par unpointeur. Ces entités sont de ce fait appelées aliasées.

La déclaration d’un type accès généralisé (permettant la création de pointeursgénéralisés sur des objets statiques) se caractérise par la présence de l’un des motsréservés all ou constant placé après access. Le mot réservé all permet de lireet de modifier la variable référencée par le pointeur alors que constant ne permetque la lecture de la constante ou de la variable référencée par le pointeur.

L’attribut Access, appliqué à un identificateur de constante ou variable aliasée,donne l’adresse de cette constante ou variable, adresse qui peut alors être affectéeà un pointeur généralisé (exemple 15.33). Il faut bien entendu que les typesconcordent lors de cette affectation.

Exemple 15.33 Déclaration de types accès généralisés et de pointeurs de ces types.

-- Deux types acces generalisestype T_Pt_Gen_Integer is access all Integer;type T_Pt_Gen_String is access constant String;

-- aliased permet de referencer les objets qui suivent par des-- pointeurs generalisesEntier : aliased Integer := 5;Deux : aliased constant Integer := 2;Bonjour : aliased constant String := "Bonjour";

-- Deux pointeurs generalises, de valeur initiale null (mise-- automatiquement pour tous les pointeurs)Pt_Gen_Integer : T_Pt_Gen_Integer;Pt_Gen_String : T_Pt_Gen_String;...-- Utilisation de l'attribut Access pour obtenir l'adresse des-- objetsPt_Gen_Integer := Entier'Access; -- Pt_Gen_Integer reference

-- Entier

Pt_Gen_Integer.all := 8; -- Entier vaut maintenant 8

Pt_Gen_Integer := Deux'Access; -- INTERDIT, Deux est une-- constante

Pt_Gen_String := Bonjour'Access; -- Pt_Gen_String reference-- Bonjour

Page 366: Cours Ada de A à Z

AUTRES ASPECTS DES TYPES ACCÈS 366

Pt_Gen_String (1) := 'b'; -- INTERDIT, Bonjour est une... -- constante

Lorsqu’elle est référencée par un pointeur généralisé, une constante ou variablealiasée peut donc être accédée soit directement par son identificateur, soit indi-rectement par le pointeur qui la repère.

Afin d’éviter des cas de pointeurs fantômes (§ 15.7.1), c’est-à-dire ici despointeurs généralisés qui désignent des objets statiques qui n’existent plus, la duréede vie de tout objet aliasé doit être au moins aussi longue que celle du type accèsgénéralisé.

La norme Ada précise quelques points supplémentaires non abordés ici. Lacréation de paquetages d’interface (sect. 19.3) entre des bibliothèques de sous-programmes écrits en langage C et des applications Ada est une des raisons del’existence des types accès généralisés. En effet, la notion de pointeur sur desobjets statiques s’utilise couramment en C.

15.8.2 Types accès à sous-programme

Ada permet la déclaration de pointeurs sur des sous-programmes. Ladéclaration d’un type accès à sous-programme se caractérise par la présence d’unespécification de sous-programme sans nom (!) après le mot réservé access. Despointeurs d’un tel type permettent l’appel de tout sous-programme possédant lemême profil (sect. 4.8).

L’attribut Access, appliqué à un identificateur de sous-programme, donnel’adresse de ce sous-programme, adresse qui peut alors être affectée à un pointeurà sous-programme (exemple 15.34). Il faut cependant que les profils concordentpour que cette affectation soit acceptée par le compilateur.

Exemple 15.34 Déclaration de types à sous-programmes et de pointeurs de ces types.

-- Deux types acces a sous-programmetype T_Pt_Procedure is access procedure; -- Pas de parametretype T_Pt_Fonction is access function (F:Float) return Float;

procedure P is begin ... end P;

-- Deux pointeurs a sous-programmePt_Procedure : T_Pt_Procedure;Pt_Fonction : T_Pt_Fonction;

-- Pour l'appel de la fonctionNombre_Reel : Float;...-- Utilisation de l'attribut Access pour l'adresse des sous--- programmesPt_Procedure := P'Access; -- Pt_Procedure reference P

Page 367: Cours Ada de A à Z

AUTRES ASPECTS DES TYPES ACCÈS 367

Pt_Procedure.all; -- Appel de la procedure P

-- Pt_Fonction reference la fonction logarithme du paquetage-- de bibliotheque Ada.Numerics.Elementary_Functions-- [ARM A.5.2]Pt_Fonction := Ada.Numerics.Elementary_Functions.Log'Access;

Nombre_Reel := Pt_Fonction (10.0); -- Appel de la fonction Log

Lorsqu’il est référencé par un pointeur à sous-programme, un sous-programmepeut donc être appelé soit directement par son identificateur, soit indirectement parle pointeur qui le référence. Les appels s’effectuent comme présentés dansl’exemple 15.34 en précisant que le mot réservé all doit être présent si l’appel necomporte pas de paramètres effectifs comme avec Pt_Procedure.all, alorsqu’il peut être omis en présence d’un ou de plusieurs paramètres comme pourPt_Fonction(10.0). L’exemple 15.35 présente, lui, le passage en paramètred’un sous-programme grâce à un type accès à sous-programme.

Afin d’éviter les pointeurs fantômes (§ 15.7.1), c’est-à-dire ici des pointeurs àsous-programme qui désignent des sous-programmes qui n’existent plus, la duréede vie d’un sous-programme doit être au moins aussi longue que celle du type accèsà sous-programme.

Exemple 15.35 Sous-programmes en paramètre grâce à un type accès à sous-programme.

with Ada.Numerics.Elementary_Functions;use Ada.Numerics.Elementary_Functions;

-- Illustre le passage en parametre de sous-programmesprocedure Exemple_15_35 is

type T_Pt_Fonction is access function (F:Float) return Float;

-------------------------------------------------------------- Calcule la valeur approchee de l'integrale entre deux bornes-- de la fonction passee en parametre, sans aucune verification

procedure Integrer ( Pt_Fonction : in T_Pt_Fonction;Borne_Inf : in Float;Borne_Sup : in Float;Nombre_Pas : in Integer;Aire : out Float ) is

-- Pour l'avance entre les deux bornesX : Float := Borne_Inf;Pas : Float := (Borne_Sup – Borne_Inf) / Float(Nombre_Pas);

begin -- Integrer

Aire := 0.0;

for I in 1..Nombre_Pas loop

-- Aire d'un rectangle de largeur Pas et de longueur-- moyenne entre la valeur de la fonction en X et en X+Pas

Page 368: Cours Ada de A à Z

AUTRES ASPECTS DES TYPES ACCÈS 368

Aire := Aire + Pas *((Pt_Fonction(X) + Pt_Fonction(X+Pas)) / 2.0);

X := X + Pas; -- Abscisse suivante

end loop;

end Integrer;------------------------------------------------------------

Integrale : Float; -- Pour le resultat du calcul

begin -- Exemple_15_35

-- Quelques exemples d'appelIntegrer ( Sqrt'access, 0.0, 1.0, 100, Integrale );

Integrer ( Log'access, 1.0, 10.0, 10, Integrale );

Integrer ( Exp'access, –1000.0, 0.0, 10_000, Integrale );...

La norme Ada précise quelques points supplémentaires peu ou pas abordés ici.Le passage de sous-programmes en paramètres, la création d’interfaces-utilisateursgraphiques ainsi que la création de paquetages d’interface (sect. 19.3) entre desbibliothèques de sous-programmes écrits en langage C et des applications Ada sontdes raisons de l’existence des types accès à sous-programmes.

Page 369: Cours Ada de A à Z

369

15.9 EXERCICES

15.9.1 Représentation schématique

En considérant les déclarations des types T_Lien et T_Element de l’exemple15.9 et les variables

El_1 : T_Element;Lien_1 : T_Lien;Lien_2 : T_Lien;

et en supposant que le type des informations (T_Info) est Integer, représenterschématiquement l’effet des instructions suivantes:

Lien_1 := new T_Element'(10, Lien_2);Lien_1 := new T_Element'(8, Lien_1);Lien_2 := Lien_1;Lien_1.all := El_1;El_1.Suivant := Lien_2;

15.9.2 Représentation schématique

Représenter schématiquement chaque étape de la suite d’opérations suivantessur une queue dynamique initialement vide et formée d’éléments entiers:

• insérer successivement les nombres 18, 24, –5 et 2;• modifier la valeur de tête par le nombre 6;• rechercher la valeur –5, puis la valeur 3;• supprimer tous les éléments de la queue.

15.9.3 Chaînes de longueur variable

Soient les déclarations suivantes:type T_Pt_String is access String;

function "+" ( Chaine : String ) return T_Pt_String isbegin

return new String'(Chaine);end "+";

En utilisant la fonction-opérateur "+" ci-dessus, déclarer un tableau Calcio(de type T_Pt_String pour ses éléments) contenant les chaînes "Juventus","Milan", "Inter", "Fiorentina", "Napoli", "Parma" et "Sampdoria".

Constater qu’il s’agit d’un tableau d’éléments de longueurs différentes! Cetexercice est inspiré de [BAR 97].

15.9.4 Chaînes de longueur variable

Ecrire la fonction-opérateur "-" inverse de "+" (exercice 15.9.3) qui rend lachaîne repérée par le pointeur (de type T_Pt_String) passé en paramètre. Cetexercice est également inspiré de [BAR 97].

Page 370: Cours Ada de A à Z

370

15.9.5 Fiches personnelles et liste dynamique

Adapter la solution de l’exercice 12.7.5 de manière à utiliser une listedynamique pour stocker les fiches en mémoire. Le fichier binaire servira toujoursà sauvegarder les fiches lorsque l’utilisateur le jugera nécessaire.

15.9.6 Paquetage enfant pour les queues dynamiques

Ecrire un paquetage enfant du parent Queues_Dynamiques mettant à dispo-sition les opérations suivantes: copier une queue, appondre une queue à une autre,savoir si une queue est pleine, et supprimer tous les éléments d’une queue.

15.9.7 Paquetage enfant pour les piles dynamiques

Ecrire un paquetage enfant du parent Piles_Dynamiques similaire à celuidemandé dans l’exercice 15.9.6.

15.9.8 Types accès à sous-programme

Ecrire un sous-programme qui trouve un zéro d’un polynôme de degré impair(ou d’une autre fonction) par dichotomie dans un intervalle contenant le zéro.

Page 371: Cours Ada de A à Z

POINTS À RELEVER 371

15.10 POINTS À RELEVER

15.10.1 En général

• Attention lorsqu’une liste est vide ou pleine; certaines opérations peuventalors provoquer des erreurs.

• Un pointeur est une variable (statique) dont le contenu est toujours uneadresse mémoire.

• Une variable dynamique (ou pointée) est une variable créée à l’exécutionpar un allocateur de mémoire.

• L’accès à une variable dynamique s’effectue toujours par l’intermédiaired’un pointeur ou d’une autre variable dynamique.

• L’utilisation de variables dynamiques est facilitée par des représentationsschématiques.

• La manipulation des pointeurs comporte des pièges à éviter absolument, enparticulier lors d’une tentative d’accès si le pointeur mentionné possède lavaleur null.

• Attention aux variables dynamiques devenues inaccessibles.

• Les pointeurs et les variables dynamiques permettent d’implanter lesstructures de données, comme les listes, sous leur forme dynamique.

• Il faut être attentif au problème de restitution de la mémoire.

15.10.2 En Ada

• Les types accès permettent de manipuler des pointeurs.

• Une prédéclaration de type est nécessaire lors de la définition d’unestructure de données dynamique.

• Les types accès généralisés permettent d’obtenir des pointeurs sur desobjets statiques.

• Les types accès à sous-programme permettent d’obtenir des pointeurs surdes sous-programmes.

Page 372: Cours Ada de A à Z

372

C H A P I T R E 1 6

T Y P E S P R I V É S , T Y P E S

Page 373: Cours Ada de A à Z

373 TYPES PRIVÉS, TYPES LIMITÉS, UNITÉS ENFANTS

Page 374: Cours Ada de A à Z

MOTIVATION 374

16.1 MOTIVATION

La notion de type privé est indissociable de la notion de paquetage. S’il estpossible de concevoir un paquetage sans type privé, la déclaration d’un type privéne peut s’effectuer que dans un paquetage. Comme vu précédemment (sect. 10.2),la spécification déclare toutes les entités exportées alors que le corps implémentecertaines de ces entités en cachant leur réalisation aux utilisateurs du paquetage.Mais dans tous les exemples présentés jusqu’ici, la structure des types exportésétait complètement visible, avec comme conséquence une diminution de la fiabilitéde ces paquetages puisque, intentionnellement ou par maladresse, tout utilisateur ala possibilité d’agir directement sur les constituants des objets de ces types, sanspasser obligatoirement par les opérations mises à disposition. De plus, tout chan-gement dans la structure même du type pourrait demander de nombreuses modifi-cations des unités utilisatrices, avec le coût que cela peut représenter et les risquesd’introduction d’erreurs supplémentaires.

L’introduction d’un type privé dans la spécification d’un paquetage va per-mettre d’éliminer tous ces inconvénients et de transformer le paquetage en unestructure de boîte noire (black box), c’est-à-dire en une pièce de programme dontl’intérieur est invisible et dont l’utilisation passe obligatoirement et uniquementpar les opérations mises à disposition. En fait, avec cette définition, un sous-pro-gramme est déjà une sorte de boîte noire rudimentaire. Dans d’autres langages deprogrammation, de tels types sont appelés opaques.

Enfin, en plus de ces arguments en faveur de la présentation de cette notion, cechapitre va également servir comme préparation à la notion de généricité (chap. 17et 18).

Page 375: Cours Ada de A à Z

GÉNÉRALITÉS 375

16.2 GÉNÉRALITÉS

Un type privé (private type) n’a rien de mystérieux! Il s’agit d’offrir un typedont la réalisation est placée dans une zone de la spécification appelée partie privée(private part) et inaccessible de l’extérieur du paquetage malgré sa présence dansla spécification pour des raisons de compilation. L’exemple des nombres rationnels(sect. 10.7) va être utilisé pour la présentation de ces modifications.

16.2.1 Spécification d’un paquetage avec type et partie privés

La spécification (partielle) du paquetage de calcul avec les nombres rationnelsest rappelée dans l’exemple 16.1. Elle est formée de la déclaration du typeT_Rationnel, de la constante Zero, de la fonction de construction d’un nombrerationnel et des opérations de calcul et de comparaison. La transformation du typeT_Rationnel en un type privé va influencer la déclaration de la constante etdonner tout son sens à la fonction de construction. La version avec le typeT_Rationnel privé est donnée dans l’exemple 16.2.

Exemple 16.1 Spécification (partielle) du paquetage Nombres_Rationnels.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage Nombres_Rationnels is

type T_Rationnel is -- Le type d'un nombre rationnelrecord

Numerateur : Integer;Denominateur : Positive;-- Le signe est au numerateur

end record;

Zero : constant T_Rationnel := (0, 1); -- Le nombre rationnel 0

-------------------------------------------------------------- Construction d'un nombre rationnelfunction "/" ( Numerateur : Integer;

Denominateur : Positive ) return T_Rationnel;

-------------------------------------------------------------- Autres operations (sect. 10.7)...------------------------------------------------------------

end Nombres_Rationnels;

Exemple 16.2 Spécification (partielle) du paquetage Nombres_Rationnels avec type privé.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage Nombres_Rationnels is

type T_Rationnel is private; -- Le type prive

Page 376: Cours Ada de A à Z

GÉNÉRALITÉS 376

Zero : constant T_Rationnel; -- Une constante differee

-------------------------------------------------------------- Construction d'un nombre rationnelfunction "/" ( Numerateur : Integer;

Denominateur : Positive ) return T_Rationnel;

-------------------------------------------------------------- Autres operations (sect. 10.7)...------------------------------------------------------------

private

type T_Rationnel is -- La declaration complete du typerecord

Numerateur : Integer;Denominateur : Positive; -- Le signe est au numerateur

end record;

Zero : constant T_Rationnel := (0, 1); -- La constante complete

end Nombres_Rationnels;

Comme mentionné préalablement, la spécification a été complétée par unepartie déclarative privée située à la fin et commençant par le nouveau mot réservéprivate. Dans cette partie déclarative, n’importe quelle déclaration (sauf un corpsnaturellement) est possible, mais on doit au moins y trouver les déclarationscomplètes du ou des types privés et celles de la ou des constantes différées.

Entre la définition privée du type et sa déclaration complète, il existe desrestrictions concernant son utilisation puisque sa structure n’est pas encore connue.L’utilisation du type est dans ce cas possible uniquement comme type d’uneconstante différée, comme identificateur dans une déclaration de type ou de sous-type et finalement comme type d’un paramètre de sous-programme ou du résultatd’une fonction. Ces restrictions sont respectées dans la spécification deNombres_Rationnels (exemple 16.2)

Une constante différée est, comme son nom l’indique, une constante dontl’identificateur est déclaré dans la partie visible (non privée) de la spécification etdont la déclaration complète est située dans la partie privée. Cette déclarationretardée est rendue nécessaire parce qu’il n’est possible de donner la valeur à laconstante qu’une fois le type privé complètement défini. Comme pour un typeprivé, avant sa déclaration complète, une constante différée est soumise à desrestrictions d’utilisation. Elle ne peut servir en effet que dans l’expression d’unevaleur par défaut d’un paramètre d’entrée de sous-programme

16.2.2 Corps d’un paquetage avec type et partie privés dans sa spécification

Page 377: Cours Ada de A à Z

GÉNÉRALITÉS 377

La présence d’un type privé n’affecte en rien la visibilité dans le corps dupaquetage. Puisque la déclaration complète se place dans la spécification, la struc-ture du type est utilisable dans le corps du paquetage, ainsi que toute autre entitéde la partie privée. Le corps du paquetage Nombres_Rationnels est donc a priori(sect. 16.4) identique à celui de l’exemple 10.10.

Page 378: Cours Ada de A à Z

UTILISATION D’UN PAQUETAGE AVEC TYPE PRIVÉ 378

16.3 UTILISATION D’UN PAQUETAGE AVEC TYPE PRIVÉ

Un paquetage avec type privé s’importe avec une clause de contexte commen’importe quel autre paquetage. Mais comme le type est privé, les unités utili-satrices du paquetage ne connaissent pas la structure du type. En fait, les seulesopérations mises à disposition pour un type privé sont les suivantes:

• l’affectation;• l’égalité et l’inégalité;• les opérations définies dans la partie visible de la spécification du

paquetage qui contient la déclaration du type privé, ou encore cellesexportées de ses paquetages enfants publics (sect. 16.6).

L’exemple 16.3 illustre l’utilisation de ces opérations.

Exemple 16.3 Utilisation du paquetage Nombres_Rationnels avec type privé.

with Nombres_Rationnels; -- § 16.2.1use type Nombres_Rationnels.T_Rationnel; -- § 10.5.3

-- ...procedure Exemple_16_3 is

-- Une constante rationnelle (remarquer l'utilisation de / )Une_Demi : constant Nombres_Rationnels.T_Rationnel := 1 / 2;

Nombre_1 : Nombres_Rationnels.T_Rationnel;-- Deux variablesNombre_2 : Nombres_Rationnels.T_Rationnel;

begin -- Exemple_16_3

-- AffectationsNombre_1 := Une_Demi;Nombre_2 := Nombres_Rationnels.Zero;

-- Addition de deux nombres rationnelsNombre_2 := Nombre_1 + 3 / 12;

-- Division de deux nombres rationnelsNombre_1 := Nombre_1 / Nombre_2;

-- Comparaison de deux nombres rationnelsif Nombre_1 > Nombre_2 then ... end if;

-- L'acces aux champs est interdit puisque le type est priveNombre_2.Numerateur := 5;...

Page 379: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE AVEC TYPE PRIVÉ 379

16.4 CONCEPTION D’UN PAQUETAGE AVEC TYPE PRIVÉ

La conception d’un paquetage avec type privé suit les mêmes règles que cellesénoncées pour les paquetages simples (sect. 10.7). Mais, toujours parce que le typeest privé, il faut en général penser à introduire des opérations pour construire unobjet de ce type, pour obtenir les valeurs qui composent un tel objet et,éventuellement, prévoir des entrées-sorties pour des valeurs de ce même type. Dansl’exemple du paquetage Nombres_Rationnels, il existe la fonction "/" qui créeun nombre rationnel à partir d’un couple de nombres entiers. Mais il est ensuiteimpossible d’obtenir les numérateur et dénominateur d’un tel nombre! Afin que lepaquetage soit cohérent, il faut donc rajouter deux fonctions à la spécification pourobtenir celle de l’exemple 16.4, ce qui amènera à la modification correspondantedu corps. Le choix des fonctions s’impose puisqu’on désire obtenir les deuxcomposants d’un nombre rationnel sans modifier la valeur dudit nombre.

Pour des raisons de simplicité, aucune opération d’entrée-sortie ne sera parcontre ajoutée au paquetage. Mais leur introduction ne poserait aucun problèmeparticulier.

Exemple 16.4 Spécification cohérente (partielle) de Nombres_Rationnels avec type privé.

-- Ce paquetage permet le calcul avec les nombres rationnelspackage Nombres_Rationnels is

type T_Rationnel is private; -- Le type d'un nombre rationnel

Zero : constant T_Rationnel; -- Le nombre rationnel 0

Division_Par_0 : exception; -- Exception si division par 0-------------------------------------------------------------- Construction d'un nombre rationnelfunction "/" ( Numerateur : Integer;

Denominateur : Positive ) return T_Rationnel;

-------------------------------------------------------------- Numerateur d'un nombre rationnelfunction Numerateur ( X : T_Rationnel ) return Integer;-------------------------------------------------------------- Denominateur d'un nombre rationnelfunction Denominateur ( X : T_Rationnel ) return Positive;-------------------------------------------------------------- Autres operations (sect. 10.7)...

private... -- Pas de changement

end Nombres_Rationnels;

Page 380: Cours Ada de A à Z

CONCEPTION D’UN PAQUETAGE AVEC TYPE PRIVÉ 380

Exemple 16.5 Corps (partiel) du paquetage Nombres_Rationnels avec type privé.

package body Nombres_Rationnels is-------------------------------------------------------------- Numerateur d'un nombre rationnelfunction Numerateur ( X : T_Rationnel ) return Integer isbegin -- Numerateur

return X.Numerateur;end Numerateur;-------------------------------------------------------------- Denominateur d'un nombre rationnelfunction Denominateur ( X : T_Rationnel ) return Positive isbegin -- Denominateur

return X.Denominateur;end Denominateur;-------------------------------------------------------------- Autres corps (sect. 10.7)...

end Nombres_Rationnels;

Page 381: Cours Ada de A à Z

TYPES PRIVÉS ET DISCRIMINANTS 381

16.5 TYPES PRIVÉS ET DISCRIMINANTS

La déclaration complète d’un type privé peut comporter un type à discriminantcomme un type article (§ 11.2.1). Dans ce cas, la définition privée peut mentionnerou non le discriminant, selon la manière avec laquelle les objets de ce type vont êtremanipulés.

16.5.1 Définition privée avec mention du discriminant

Si la définition privée mentionne le discriminant sans valeur par défaut, alorsun objet de ce type doit être contraint par une valeur pour le discriminant. Enprésence d’une valeur par défaut, la contrainte est optionnelle comme pour lestypes articles à discriminants (§ 11.3.1). Dans les deux cas, cela permet deconserver masquée la structure du type tout en permettant la construction d’objetsde taille différente. L’exemple 16.6, adapté de l’exemple 14.3, permet ladéclaration de queues de longueurs différentes.

Exemple 16.6 Spécification d’un paquetage de gestion de queues statiques privées.

-- Bases de gestion de queues statiques priveespackage Queues_Statiques is

type T_Info is ...; -- Depend de l'application

-- Pour une queue statique de longueur Tailletype T_Queue_Statique ( Taille : Positive ) is private;-------------------------------------------------------------- Operations sur une queue statique (§ 14.4.2)...------------------------------------------------------------

private

type T_Contenu is array ( Positive range <>) of T_Info;

type T_Queue_Statique ( Taille : Positive ) is

record-- Contenu de la queueContenu : T_Contenu ( 1 .. Taille );

Longueur : Natural := 0; -- Longueur de la queue

Tete : Positive := 1; -- Tete de la queueQueue : Positive := 1; -- Queue de la queue

end record;

end Queues_Statiques;

Il est maintenant possible de déclarer les queues suivantes:Tampon : Queues_Statiques.T_Queue_Statique ( 100 );Longue_Queue : Queues_Statiques.T_Queue_Statique ( 10_000 );Petite_Queue : Queues_Statiques.T_Queue_Statique ( 10 );

Page 382: Cours Ada de A à Z

TYPES PRIVÉS ET DISCRIMINANTS 382

16.5.2 Définition privée sans mention du discriminant

Si la définition privée ne mentionne pas le discriminant, alors la déclarationcomplète doit comporter une valeur par défaut pour le discriminant. Cela permetde conserver masquée la structure du type tout en permettant la création d’objetsde taille variable. L’exemple 16.7 reprend le cas des polynômes (§ 11.3.1) sousforme de paquetage.

Exemple 16.7 Spécification d’un paquetage de gestion de polynomes privés.

-- Bases de gestion de polynomes privespackage Polynomes is

type T_Degre is range 0 .. 100;

-- Pour les coefficients des termes d'un polynometype T_Coefficients is array (T_Degre range <>) of Integer;

-- Pour une polynome de degre inferieur ou egal a 100type T_Polynome is private;

-------------------------------------------------------------- Creation d'un polynomefunction Creer ( Coefficients : in T_Coefficients )

return T_Polynome;

-------------------------------------------------------------- Autres operations sur un polynome...------------------------------------------------------------

private

type T_Polynome (Degre : T_Degre := 0) is -- Expression-- par defaut

recordCoefficients : T_Coefficients (T_Degre’First..Degre)

:= (T_Degre'First..Degre => 0);end record;

end Polynomes;

Il est maintenant possible de déclarer les polynômes suivants, tous initialementnuls:

Pol_Const : Polynomes.T_Polynome;Parabole : Polynomes.T_Polynome;Grand : Polynomes.T_Polynome;

Comme ces polynômes ne sont pas contraints (sect. 11.3), il sera possible de lesmodifier en utilisant l’opération Creer du paquetage, comme par exemple:

Pol_Const := Polynomes.Creer ( (0 => –6) ); -- Le polynome-- constant –6

Page 383: Cours Ada de A à Z

TYPES PRIVÉS ET DISCRIMINANTS 383

Parabole := Polynomes.Creer ( (3, –2, 5) ); -- 3 – 2x + 5x2

Grand := Polynomes.Creer ( (0..N => 1) ); -- 1+x+x2+...+xn

A titre d’illustration, le corps de la fonction Creer serait le suivant:function Creer ( Coefficients : in T_Coefficients )

return T_Polynome isbegin -- Creer

return ( Coefficients'Last, Coefficients );

end Creer;

La valeur du discriminant (le degré du polynôme) est obtenue par la bornesupérieure des indices du paramètre; cela implique que ledit paramètre doit être untableau de coefficients de borne inférieure égale à zéro pour que la valeur fourniepar l’attribut Last corresponde effectivement au degré du paramètre effectif. Cetteconvention est respectée dans les trois exemples précédents.

Page 384: Cours Ada de A à Z

PAQUETAGES ENFANTS PUBLICS 384

16.6 PAQUETAGES ENFANTS PUBLICS

Lors de la présentation des paquetages, la notion d’enfant a été introduite (sect.10.8). Maintenant que les paquetages peuvent comporter des types privés, il estnécessaire de revenir sur les enfants. En fait, les paquetages enfants se répartissenten deux catégories: les enfants publics et les enfants privés.

Le paquetage enfant Nombres_Rationnels.Utilitaires (§ 10.8.1) est unexemple de paquetage enfant public car il n’est pas déclaré comme privé (sect.16.7). Un tel paquetage possède la même structure qu’un paquetage parent, à savoirune spécification, avec optionnellement une partie privée, et un corps. Les règlesénoncées préalablement (sect. 10.8) pour les paquetages enfants s’appliquent enfait à tous les paquetages enfants publics, avec ou sans partie privée.

Figure 16.1 Visibilité entre paquetages parent et enfant public.

Les seules précisions à donner concernent la visibilité (fig. 16.1 et 16.2). Dansla partie privée et le corps de l’enfant, toutes les déclarations de la spécification duparent, partie privée comprise, sont utilisables sans préfixe. Par contre, lesdéclarations de la partie privée du parent sont invisibles dans la partie visible (nonprivée) de l’enfant.

package Parent.Enfant is...

private...

end Parent.Enfant;

package body Parent.Enfant is...

end Parent.Enfant;

Paquetage parentpackage Parent is

...private

...end Parent;

package body Parent is...

end Parent;

Paquetage enfant public

Page 385: Cours Ada de A à Z

PAQUETAGES ENFANTS PUBLICS 385

Figure 16.2 Vue schématique de la visibilité entre paquetages parent et enfant public.

package Parent.Enfant is...

private...

end Parent.Enfant;

package body Parent.Enfant is.........

end Parent.Enfant;

package Parent is...

private...

end Parent;

package body Parent is............

end Parent;

Page 386: Cours Ada de A à Z

PAQUETAGES ENFANTS PRIVÉS 386

16.7 PAQUETAGES ENFANTS PRIVÉS

Dans une hiérarchie de paquetages, les enfants publics permettent d’offrir desfonctionnalités supplémentaires aux unités utilisatrices des paquetages parents. Lespaquetages enfants privés (spécification précédée de private) n’offrent aucunefonctionnalité supplémentaire mais servent à décomposer de manière structurée lesopérations internes, invisibles hors des paquetages parents.

Figure 16.3 Visibilité des paquetages parents dans les enfants privés.

Un enfant privé n’est visible que pour le corps de son parent et pour tous lesenfants de son parent à l’exclusion des spécifications des enfants publics (fig. 16.3et 16.4). Finalement, la spécification et le corps d’un paquetage enfant privépeuvent aussi accéder à la partie privée de tous ses ancêtres.

Figure 16.4 Vue schématique de la visibilité entre paquetages parent et enfant privé.

private package Parent.Enfant is...

private...

end Parent.Enfant;

package body Parent.Enfant is...

end Parent.Enfant;

Paquetage parentpackage Parent is

...private

...end Parent;

package body Parent is...

end Parent;

Paquetage enfant privé

private packageParent.Enfant is

...private

...end Parent.Enfant;

package body Parent.Enfant is............

end Parent.Enfant;

package Parent is...

private...

end Parent;

package body Parent is.........

end Parent;

Page 387: Cours Ada de A à Z

PAQUETAGES ENFANTS PRIVÉS 387

Exemple 16.8 Spécification du paquetage enfant privé Nombres_Rationnels.Operations_Internes.

-- Ce paquetage fournit des operations internes de calcul avec les-- nombres rationnelsprivate package Nombres_Rationnels.Operations_Internes is

-------------------------------------------------------------- Pour l'addition et la soustraction afin de rendre le-- resultat le plus petit possiblefunction P_P_M_C ( X, Y : Positive ) return Positive;

-------------------------------------------------------------- Pour la reduction en un nombre rationnel irreductiblefunction P_G_C_D ( X, Y : Positive ) return Positive;

-------------------------------------------------------------- Rendre un nombre rationnel irreductiblefunction Irreductible ( X : T_Rationnel) return T_Rationnel;

------------------------------------------------------------end Nombres_Rationnels.Operations_Internes;

Les opérations mises à disposition par le paquetage enfant privéNombres_Rationnels.Operations_Internes (exemple 16.8) sont destinéesuniquement à la réalisation de certaines des fonctionnalités des paquetagesNombres_Rationnels et Nombres_Rationnels.Utilitaires. Il est doncparfaitement logique d’utiliser un paquetage enfant privé pour les implémenter.

Après l’acquisition de ces notions nouvelles, la réalisation d’un ensembled’unités de calcul et de traitement des nombres rationnels doit être revue par rap-port à la proposition faite préalablement (sect. 10.7 et 10.8). La figure 16.5 montreune décomposition qui tire parti de toutes les notions maintenant connues.

Page 388: Cours Ada de A à Z

PAQUETAGES ENFANTS PRIVÉS 388

Figure 16.5 Hiérarchie d’unités de traitement des nombres rationnels.

Le corps du paquetage Nombres_Rationnels.Operations_Internescontiendra uniquement les corps des opérations exportées (exemple 10.10). Parcontre, cette nouvelle décomposition rendra nécessaire l’utilisation de la clause decontexte with Nombres_Rationnels.Operations_Internes avant le corpsdu paquetage enfant Nombres_Rationnels.Utilitaires si nécessaire. Dansce corps, il sera alors possible et facile de rendre les nombres rationnels (calculés)irréductibles par l’utilisation de la fonction Irreductible exportée du paquetage

type T_Rationnel

zero,addition,

soustraction,multiplication,division,

numerateur,denominateur

construction,

Corps de

multiplication par un

division par un nombre

Spécification de

nombre entier,

entier

valeur absolue,

P_P_M_C,Irreductible

Spécification de

P_G_C_D,

Nombres_Rationnels.Operations_Internes

Corps de

Corps deNombres_Rationnels.Operations_Internes

puissance,

comparaisons,

addition,

soustraction,multiplication,division,

numerateur,denominateur

construction,

comparaisons,

multiplication par un

division par un nombrenombre entier,

entier

valeur absolue,puissance,

P_P_M_C,Irreductible

P_G_C_D,

visibilité directevisibilité grâce à une clause de contexte

Nombres_Rationnels.Utilitaires

Nombres_Rationnels.Utilitaires

Nombres_Rationnels

Nombres_RationnelsSpécification de

Page 389: Cours Ada de A à Z

PAQUETAGES ENFANTS PRIVÉS 389

enfant privé Nombres_Rationnels.Operations_Internes.

Page 390: Cours Ada de A à Z

REMARQUES FINALES SUR LES UNITÉS ENFANTS 390

16.8 REMARQUES FINALES SUR LES UNITÉS ENFANTS

Une unité enfant (publique ou privée) peut être en fait n’importe quelle unité debibliothèque. Autrement dit, non seulement un paquetage mais encore uneprocédure, une fonction, une unité générique (sect. 18.4) peuvent être les enfantsd’un paquetage parent et former une hiérarchie.

Dans le cas des nombres rationnels, les trois fonctions P_G_C_D, P_P_C_M etIrreductible pourraient être extraites du paquetage enfant les contenant(Nombres_Rationnels.Operations_Internes) et transformées en unitésenfants.

L’exemple 16.9 montre la fonction P_G_C_D rendue privée.

Exemple 16.9 Fonction P_G_C_D rendue privée.

private function Nombres_Rationnels.P_G_C_D ( X, Y : Positive )return Positive;

function Nombres_Rationnels.P_G_C_D ( X, Y : Positive )return Positive is

Diviseur_X : Positive := X; -- Pour les soustractionsDiviseur_Y : Positive := Y;

begin -- P_G_C_D

while Diviseur_X /= Diviseur_Y loop -- PGCD trouve?

if Diviseur_X > Diviseur_Y then

Diviseur_X := Diviseur_X – Y;

else

Diviseur_Y := Diviseur_Y – X;

end if;

end loop;

return Diviseur_X; -- C'est le PGCD

end Nombres_Rationnels.P_G_C_D;

En guise de conclusion, il faut souligner que les notions de paquetage et d’uni-tés enfants publiques et privées garantissent le caractère évolutif des composantslogiciels pour leurs utilisateurs et permettent une conception très fine pour leursconcepteurs. Une présentation plus complète et très détaillée des unités enfantspeut être trouvée dans [BAR 97].

Page 391: Cours Ada de A à Z

TYPES LIMITÉS 391

16.9 TYPES LIMITÉS

16.9.1 Motivation

L’opération d’affectation permet de remplacer la valeur d’une variable par lerésultat du calcul d’une expression. Parmi tous les cas particuliers existants, celuioù l’expression est réduite à la présence d’une seule variable permet en faitd’effectuer une copie de la valeur de cette variable. Or il existe des situations où cecas particulier d’affectation peut produire des effets non désirés. Par exemple, sil’on considère une queue (ou une pile) dynamique (sect. 15.5 et 15.6), l’affectationdu contenu d’une variable du type T_Queue_Dynamique ne va pas copier toute laqueue, mais uniquement le contenu de la variable, c’est-à-dire les valeurs(adresses) des pointeurs Tete et Queue. Le résultat de l’affectation ne sera doncqu’une copie de pointeurs, autrement dit la même queue sera désignée par deuxvariables différentes de type T_Queue_Dynamique (§ 15.5.1). Si l’intention duprogrammeur était d’effectuer la copie de toute la queue, alors cette affectationconduira à des erreurs à l’exécution.

Ada permet de se protéger contre de telles erreurs par le biais des types limités,qui empêchent l’utilisation de l’opération d’affectation.

16.9.2 Généralités

Un type limité (limited type) est un type article pour lequel la déclarationmentionne qu’il est limité par l’emploi du (nouveau) mot réservé limited placéimmédiatement après le mot réservé is. Cette mention empêche toute affectationd’objets d’un tel type et interdit l’utilisation des opérations prédéfinies d’égalité etd’inégalité.

Exemple 16.10 Déclarations d’un type limité.

type T_Date is limitedrecord

Jour : T_Jour;Mois : Mois_De_L_Annee;Annee : Integer;

end Date;

L’exemple 16.10 illustre simplement une première et peu fréquente utilisationdu mot réservé limited. Il est bien clair qu’un tel type T_Date reste utile carl’accès aux champs d’une date n’est pas restreint par la limitation (globale) du type.Dans l’exemple des queues dynamiques, si le type T_Queue_Dynamique étaitlimité, une affectation globale serait ainsi détectée et interdite par le compilateur.

Mais l’intérêt majeur de la notion de type limité réside dans son application aux

Page 392: Cours Ada de A à Z

TYPES LIMITÉS 392

types privés. Il devient alors possible de créer des paquetages qui exportent destypes dont l’utilisation est strictement restreinte aux opérations exportées par lepaquetage ou l’un de ses enfants publics, avec comme conséquence un niveau defiabilité du paquetage encore augmenté. En plus de l’utilisation des opérationsexportées, la déclaration de variables et de paramètres d’un type limité privé estpossible.

Exemple 16.11 Spécification (partielle) d’un paquetage de gestion de queues dynamiques limitées privées.

-- Bases de gestion de queues dynamiques limitees priveespackage Queues_Dynamiques is

type T_Info is ...; -- Depend de l'application

type T_Queue_Dynamique is limited private;-- Pour une queue-- dynamique

Queue_Vide : exception; -- Levee si la queue est vide-------------------------------------------------------------- Insere Info en queue de La_Queueprocedure Inserer ( La_Queue : in out

T_Queue_Dynamique;Info : in T_Info );

-------------------------------------------------------------- Autres operations (§ 15.5.2)...

private

type T_Element; -- Predeclaration

type T_Lien is access T_Element; -- Le type acces

type T_Element is -- Pour une variable dynamiquerecord

Information : T_Info; -- L'information sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Queue_Dynamique is -- Pour une queue dynamiquerecord

Tete : T_Lien; -- Tete et queue de la queueQueue : T_Lien;

end record;

end Queues_Dynamiques;

Dans l’exemple 16.11, le type T_Queue_Dynamique est limité privé. Dans lesunités utilisatrices du paquetage Queues_Dynamiques, il sera donc possible dedéclarer des queues dynamiques et d’utiliser les opérations d’insertion, desuppression, etc., comme dans l’exemple 16.12. Il faut relever à ce propos que ladéclaration d’une variable de type T_Queue_Dynamique est initialement vide car

Page 393: Cours Ada de A à Z

TYPES LIMITÉS 393

tout pointeur est toujours initialisé à la valeur null!

Exemple 16.12 Utilisation d’une queue dynamique limitée privée.

-- ...with Queues_Dynamiques;

procedure Exemple_16_12 is

Queue : Queues_Dynamiques.T_Queue_Dynamique;Info : Queues_Dynamiques.T_Info := ...; -- Info initialisee

procedure P (Queue: in Queues_Dynamiques.T_Queue_Dynamique) is...

end P;

begin -- Exemple_16_12

Queues_Dynamiques.Inserer ( Queue, Info );

P ( Queue );

if Queues_Dynamiques.Vide ( Queue ) then ... end if;

...

Pour tous les types en Ada, il est possible de surcharger les opérateursprédéfinis d’égalité et d’inégalité. Pour les types limités, la définition desopérateurs d’égalité et d’inégalité est autorisée sous la forme habituelle desfonctions-opérateurs "=" et "/=" avec les mêmes règles que précédemment (sect.4.9). Par contre, il est interdit de redéfinir l’affectation. Il faut dans ce cas créer uneprocédure de copie qui simule ladite affectation, tout ceci dans la spécification dupaquetage contenant le type limité privé ou dans celle de l’un de ses enfantspublics. Bien entendu, les opérations d’affectation, d’égalité et d’inégalité restentutilisables partout où la structure du type est visible, comme par exemple dans lecorps du paquetage et dans celui de ses enfants.

16.9.3 Remarques sur les types limités

• Le critère de décision entre le choix de limiter ou non un type privé résidedans la volonté d’interdire ou de permettre l’affectation.

• Comme l’affectation est interdite, une déclaration de constante ou unevaleur initiale pour une variable sont impossibles.

• Des tableaux d’élements limités ou des articles comportant un champ limitésont autorisés. Les tableaux ou les articles sont alors eux-mêmes limités.

Page 394: Cours Ada de A à Z

394

16.10 EXERCICES

16.10.1 Types privés

Supposons que le type T_Queue_Statique de l’exemple 16.6 soit déclarécomme suit:

type T_Queue_Statique ( Taille : Positive := 100 ) is private;

Déclarer alors quelques queues, en utilisant parfois la valeur par défaut.

16.10.2 Paquetage avec type privé

Reprendre et modifier le paquetage Piles_Statiques (§ 14.5.2 et suivants)en transformant le type T_Pile_Statique en type privé.

16.10.3 Types limités privés

Reprendre et modifier les deux paquetages Queues_Dynamiques (§ 15.5.2 etsuivants) et Piles_Dynamiques (§ 15.6.2 et suivants) en transformant les typesT_Queue_Dynamique et T_Pile_Dynamique en types limités privés.

16.10.4 Types privés ou limités privés

Pourquoi l’exercice 16.10.2 précise-t-il que les types doivent être limités? Quepourrait-il arriver s’ils ne l’étaient pas?

16.10.5 Paquetages parent et enfants

Reprendre le problème des fiches personnelles et des opérations associées(exercices 10.9.1, 10.9.2, 10.9.6, 11.5.5, 12.7.5 et 15.9.5) et concevoir unehiérarchie de paquetages parent et enfants (publics et/ou privés) ainsi qu’un typeprivé (ou limité privé) pour représenter une fiche.

Page 395: Cours Ada de A à Z

POINTS À RELEVER 395

16.11 POINTS À RELEVER

16.11.1 En général

• Les types privés d’Ada possèdent des équivalents dans d’autres langages deprogrammation; ils sont parfois appelés types opaques.

• Empêcher le plus possible les erreurs de l’utilisateur est une propriétéfondamentale des langages modernes, concept que les types opaquesréalisent.

16.11.2 En Ada

• Les types privés permettent d’augmenter la fiabilité d’un paquetage.

• La partie privée est une partie déclarative dans laquelle doivent se situer lesdéclarations complètes des types privés.

• Entre la déclaration privée et la déclaration complète d’un type, il existe desrestrictions sur son utilisation.

• Une constante différée est une constante dont la valeur est donnée lors dela déclaration complète.

• Un type privé peut avoir des discriminants, visibles ou non.

• Les paquetages enfants peuvent être publics ou privés.

• Les types limités empêchent l’utilisation de l’affectation et des opérationsprédéfinies d’égalité et d’inégalité.

• Les opérations sur les valeurs d’un type limité privé sont celles visiblesdans les spécifications du paquetage de déclaration du type ainsi que de sesenfants publics.

Page 396: Cours Ada de A à Z

396

C H A P I T R E 1 7

B A S E S D E S U N I T É S

Page 397: Cours Ada de A à Z

397 BASES DES UNITÉS GÉNÉRIQUES

Page 398: Cours Ada de A à Z

MOTIVATION 398

17.1 MOTIVATION

Au cours des chapitres précédents, certaines incertitudes sont apparues dans lesstructures ou les mécanismes présentés. Il s’agissait parfois d’un type qui dépen-dait de l’utilisation faite d’un paquetage (§ 15.5.2) ou d’une procédure de traite-ment (§ 15.5.9). Dans les deux cas, les informations nécessaires à la levée de cesincertitudes sont extérieures au paquetage concerné; elles dépendent de l’utilisa-tion qu’une unité externe en fera. Or le fonctionnement du paquetage est complè-tement indépendant de la nature du type ou du corps de la procédure.

Un mécanisme tel que la généricité (genericity) permet l’écriture d’unitésgénériques (paquetages ou sous-programmes), structures paramétrables à la com-pilation ou à l’exécution dont les paramètres sont spécifiés par les unités utili-satrices. Il est donc possible d’écrire des paquetages ou des sous-programmes plusfacilement réutilisables puisque, rendus génériques, ils pourront s’adapter à desemplois différents sans nécessiter de réécriture.

Les paquetages d’entrées-sorties de base comme Ada.Text_IO.Integer_IO

(§ 6.2.2), Ada.Text_IO.Modular_IO (§ 6.2.3), Ada.Text_IO.Float_IO

(§ 6.2.4), Ada.Text_IO.Enumeration_IO (§ 5.2.5), ou encore la procédureAda.Unchecked_Deallocation (sect. 15.7) sont des exemples d’unités généri-ques prédéfinies.

Page 399: Cours Ada de A à Z

PAQUETAGES GÉNÉRIQUES ET INSTANCIATIONS 399

17.2 PAQUETAGES GÉNÉRIQUES ET INSTANCIATIONS

Un paquetage devient générique dès que le nouveau mot réservé genericprécède le début de sa spécification. Mais l’intérêt essentiel de cette transformationréside en la possibilité de le paramétrer dans le but déjà mentionné de l’utiliser àplusieurs reprises en fournissant chaque fois des valeurs différentes pour cesparamètres. Ceux-ci, appelés paramètres formels génériques, se placent dans unezone nommée partie formelle générique et délimitée par les mots réservésgeneric et package. Ces paramètres peuvent être des valeurs, des types, dessous-programmes ou encore des paquetages. Leur diversité donne au mécanismede généricité tout son intérêt et sa puissance.

Figure 17.1 Partie formelle générique d’un paquetage.

Le diagramme syntaxique de la figure 17.1 montre qu’un paquetage génériquepeut n’avoir aucun paramètre. Cette possibilité est particulièrement utile si lepaquetage est un enfant (sect. 18.4) d’un paquetage parent générique.

Exemple 17.1 Parties formelles génériques avec ou sans paramètres.

genericpackage Sans_Parametre is -- Specification d'un paquetage

-- generique sans parametre-- Parties visible et privee...

end Sans_Parametre;---------------------------------------------------------------generic

-- Valeurs en parametres, sect. 17.3Longueur : in Positive;Taille : Natural := 80;Caractere_Courant : in out Character;

-- Types en parametres, sect. 17.4 et 18.1type T_Mois is (<>);type T_Entier is range <>;type T_Tableau is array (T_Mois range <>) of Integer;type T_Pointeur is access T_Tableau;type T_Inconnu is private;

Partie formelle générique

Paramètres génériques

generic

Page 400: Cours Ada de A à Z

PAQUETAGES GÉNÉRIQUES ET INSTANCIATIONS 400

type T_Indefini (<>) is private;type T_Vraiment_Inconnu is limited private;

-- Sous-programmes en parametres, sect. 18.2with procedure Imprimer ( Tableau : in T_Tableau );with function Nombre_Jours ( Mois : T_Mois )

return Natural is <>;

-- Paquetage en parametre, cas non traite dans cet ouvragewith package Entrees_Sorties is

new Ada.Text_IO.Enumeration_IO (<>);

package Avec_Parametres is -- Specification d'un paquetage -- generique avec parametres

-- Parties visible et privee...

end Avec_Parametres;

L’exemple 17.1 est uniquement illustratif. Il présente simplement quelquesformes de paramètres (ou aucune) placés avant une spécification. Celle-ci ainsi quele corps correspondant possèdent une structure identique à celle d’un paquetagenon générique. Les paramètres formels génériques sont visibles dans tout lepaquetage ainsi que dans ses enfants. La seule restriction réside dans le fait que lesparamètres génériques ne sont pas considérés comme statiques et par conséquentinutilisables là où un objet statique est requis, par exemple dans les choix d’uneinstruction case ou dans un intervalle de définition d’un type entier. Un paramètregénérique peut être utilisé dans la définition d’un autre paramètre générique sauf sila restriction précédente l’interdit.

L’utilisation d’un tel paquetage nécessite la déclaration d’un paquetagehabituel, non générique, à partir du paquetage générique. Cette opération (exemple17.2), qui permet en plus d’associer les paramètres génériques effectifs auxparamètres formels, s’appelle instanciation (instantiation). L’association desparamètres effectifs aux paramètres formels dépend du genre de paramètre ets’écrit comme une liste de paramètres effectifs (notation par nom ou par position)lors de l’appel d’un sous-programme (§ 4.3.7). On appelle paquetage instancié(instantiated package) le paquetage non générique résultant d’une instanciation.

Un paquetage générique joue le même rôle que le plan d’une maison. Un telplan ne sert qu’à construire des maisons qui, elles, sont réelles et servent àl’habitation. Les paquetages génériques permettent de créer des paquetagesinstanciés qui, comme les maisons, sont les entités utilisables.

Exemple 17.2 Exemples d’instanciations de paquetages génériques.

package E_S_Entiers is new Ada.Text_IO.Integer_IO ( Integer );

Page 401: Cours Ada de A à Z

PAQUETAGES GÉNÉRIQUES ET INSTANCIATIONS 401

package E_S_Jours isnew Ada.Text_IO.Enumeration_IO ( T_Jours_De_La_Semaine );

package Exemple_Simple is new Sans_Parametre;

package Exemple is new Avec_Parametres (Longueur => 30,Caractere_Courant => Lettre,... );

Un paquetage générique est souvent une unité de bibliothèque. Dans ce cas ildoit d’abord être compilé, puis importé par une clause de contexte dans toute unitéutilisatrice. Sinon, il doit être déclaré comme un paquetage non générique (sect.10.2). Comme un paquetage générique a pour seul but de permettre la création depaquetages instanciés, il est interdit d’utiliser une clause use ou use type avec untel paquetage. Ces clauses sont naturellement et logiquement possibles pour unpaquetage instancié. De plus, il est possible de compiler une instanciation pourelle-même, ce qui créera une nouvelle unité de bibliothèque, par exemple:

with Ada.Text_IO;package Entier_IO is new Ada.Text_IO.Integer_IO ( Integer );

C’est de cette manière qu’ont été mis à disposition les paquetages prédéfinisAda.Integer_Text_IO et Ada.Float_Text_IO. Finalement, par conventiondans cet ouvrage, le nom d’un paquetage générique se terminera par le suffixe _G.

Page 402: Cours Ada de A à Z

VALEURS COMME PARAMÈTRES GÉNÉRIQUES 402

17.3 VALEURS COMME PARAMÈTRES GÉNÉRIQUES

Les valeurs constituent le cas de paramètre générique le plus simple. Il s’agitsimplement de paramétrer un paquetage avec une constante (mode in) ou departager la valeur d’une variable entre le paquetage instancié et l’unité utilisatrice(mode in out). Ce dernier cas, peu fréquent, ne sera pas plus développé ici.Naturellement, les types des paramètres formels et effectifs devront être identiques.

L’utilisation d’une constante comme paramètre (formel) générique peut êtreillustrée grâce, par exemple, au paquetage de gestion de piles statiques (§ 14.5.2).Celui-ci comportait une longueur maximum constante Longueur_Max candidateidéale à la transformation en paramètre générique. Le paquetage obtenu (exemple17.3) permettra donc la déclaration et la gestion de piles statiques de longueurconstante définie lors de chaque instanciation.

Exemple 17.3 Squelette d’un paquetage générique de gestion de piles statiques.

-- Paquetage generique de piles statiquesgeneric

Longueur_Max : in Natural; -- Longueur maximum d'une pile

package Piles_Statiques_G is

type T_Info is ...; -- Depend de l'application

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Sommet is Integer range 0..Longueur_Max;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Pile_Statique is -- Pour une pile statique

record

-- Contenu de la pileContenu : T_Contenu;

-- Longueur de la pileLongueur : T_Longueur := T_Longueur'First;

-- Sommet de la pileSommet : T_Pos_Sommet := T_Pos_Sommet'First;

end record;

-- Autres declarations (§ 14.5.2)...

end Piles_Statiques_G;

package body Piles_Statiques_G is

...

end Piles_Statiques_G;

Page 403: Cours Ada de A à Z

VALEURS COMME PARAMÈTRES GÉNÉRIQUES 403

Un paquetage instancié se déclare en fournissant une valeur pour la constantegénérique:

package Piles_200 is new Piles_Statiques_G ( 200 );package Grandes_Piles is new Piles_Statiques_G ( 1000 );

Comme pour un paramètre d’entrée de sous-programme, un paramètreconstante générique peut comporter une valeur par défaut (exemple 17.4). Dans cecas il n’est pas nécessaire de donner de paramètre effectif lors de l’instanciation.

Exemple 17.4 Valeur par défaut et instanciation.

-- Paquetage generique de piles statiquesgeneric

Longueur_Max : in Natural := 100; -- Longueur maximum-- d'une pile

package Piles_Statiques_G is

-- Declarations (exemple 17.3)...

end Piles_Statiques_G;

... -- Corps de Piles_Statiques_G

-- Instanciations du paquetage generiquepackage Piles_100 is new Piles_Statiques_G;package Piles_80 is new Piles_Statiques_G ( 80 );

Le corps du paquetage Piles_Statiques_G est identique à celui dupaquetage non générique Piles_Statiques (§ 14.5.3 et suivants). Les paqueta-ges instanciés Piles_200, Grandes_Piles, Piles_100 et Piles_80 s’utilisentcomme n’importe quel paquetage non générique.

Page 404: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 404

17.4 TYPES COMME PARAMÈTRES GÉNÉRIQUES

17.4.1 Généralités

Les types constituent un cas des plus intéressants. Grâce à ce genre deparamètres génériques, il va être possible de constituer des paquetages de gestionde structures indépendantes du type des informations contenues dans cesstructures. De tels paquetages fourniront des opérations sur des valeurs dont lestypes seront fournis par les unités utilisatrices des paquetages! Les paquetages degestion de queues et de piles constituent de bons exemples où le type desinformations n’a aucune influence sur les opérations mises à disposition. A l’in-térieur des paquetages génériques et de tous leurs enfants, la structure précise destypes passés comme paramètres est inconnue mais les opérations globales qui leursont associées sont possibles, comme certains attributs par exemple. Cettegénéralisation n’est cependant pas absolue dans le sens que, comme Ada reste unlangage fortement typé, le compilateur aura besoin d’un minimum d’informationssur les types passés comme paramètres afin d’effectuer les vérifications néces-saires. C’est une des raisons pour lesquelles ces types sont groupés en catégoriesenglobant ceux de mêmes caractéristiques. Seules les catégories des types discretset numériques vont être discutées ici. Le chapitre 18 présentera les autres.

Lors de l’instanciation, des sous-types peuvent jouer le rôle de paramètreseffectifs pourvu que leur type de base soit compatible avec le paramètre formel.Dans ce cas, les contraintes fournies par ces sous-types s’appliquent dans le pa-quetage instancié.

17.4.2 Types discrets comme paramètres génériques

Les types discrets sont mentionnés comme paramètres génériques (exemple17.5) grâce à la forme syntaxique suivante:

type T_Discret is (<>);

Le paramètre effectif spécifié à l’instanciation doit naturellement être d’un typeou sous-type discret.

Exemple 17.5 Type discret comme paramètre générique et instanciation.

-- ...generic

type T_Discret is (<>);

package Exemple_17_5_G is

-- Retourne le successeur de Valeur, circulairement-- (l'attribut Succ reste naturellement applicable au type-- T_Discret)function Successeur (Valeur : T_Discret) return T_Discret;------------------------------------------------------------

Page 405: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 405

-- Autres declarations de la specification...

end Exemple_17_5_G;

-- Corps de Exemple_17_5_Gpackage body Exemple_17_5_G is ... end Exemple_17_5_G;

-- Instanciations du paquetage generique (§ 5.2.2 et 6.1.1)package Jours_Semaine is new

Exemple_17_5_G (T_Jours_De_La_Semaine);

package Jours_Travail is new Exemple_17_5_G (T_Jours_Travail);

Le paquetage Enumeration_IO, interne à Ada.Text_IO (sect. 19.3), est unautre exemple mentionnant un type énumératif comme paramètre générique.

17.4.3 Types numériques comme paramètres génériques

La déclaration des types numériques comme paramètres génériques ressembleà celle rencontrée lors d’une déclaration habituelle (à noter l’utilisation de <>):

type T_Entier is range <>; -- Pour un type entier signetype T_Modulaire is mod <>; -- Pour un type entier non signe

-- (modulaire)type T_Flottant is digits <>; -- Pour un type reel point-flottanttype T_Fixe is delta <>; -- Pour un type reel point-fixe

Les paramètres effectifs correspondants doivent naturellement correspondre augenre des paramètres formels. Les paquetages génériques internes à Ada.Text_IO(sect. 19.3) fournissent des exemples d’utilisation de telles déclarations.

Page 406: Cours Ada de A à Z

SOUS-PROGRAMMES GÉNÉRIQUES 406

17.5 SOUS-PROGRAMMES GÉNÉRIQUES

Comme les paquetages, les sous-programmes peuvent devenir génériques ettout ce qui s’applique aux paquetages (instanciation...) reste valable pour les sous-programmes. La seule contrainte est qu’un sous-programme générique doit êtredéclaré en une spécification et un corps puisqu’une partie (formelle) génériqueprécède toujours une spécification et non un corps. L’exemple 17.6 présente unefonction générique qui retourne le maximum de deux valeurs réelles point-flottant.

Exemple 17.6 Fonction générique de calcul d’une valeur maximum.

-- Fonction generique de recherche du maximum de deux valeurs-- reelles point-flottantgeneric

type T_Reel is digits <>;

function Maximum_G ( Valeur_1 : T_Reel;Valeur_2 : T_Reel ) return T_Reel;

function Maximum_G ( Valeur_1 : T_Reel;Valeur_2 : T_Reel ) return T_Reel is

begin -- Maximum_G

if Valeur_1 > Valeur_2 thenreturn Valeur_1;

elsereturn Valeur_2;

end if;

end Maximum_G;

-- Instanciations de la fonction generique (§ 6.2.4)function Maximum is new Maximum_G ( Float );function Maximum is new Maximum_G ( T_Reel_12 );

A propos de la fonction de l’exemple 17.6, il faut préciser qu’il existe lesattributs Max et Min [ARM ANNEXE K] applicables à n’importe quel type scalaire,attributs qui retournent les valeurs maximale et minimale d’un couple de valeurs.

Pour clore cette section, il faut citer la procédure générique prédéfinieAda.Unchecked_Deallocation (sect. 15.7) et la fonction générique prédéfinieAda.Unchecked_Conversion [ARM 13.9].

Page 407: Cours Ada de A à Z

PAQUETAGES GÉNÉRIQUES ET EXCEPTIONS 407

17.6 PAQUETAGES GÉNÉRIQUES ET EXCEPTIONS

Le paquetage Nombres_Rationnels peut être rendu générique, par exempleen transformant le type du numérateur et du dénominateur en paramètre générique.Qu’advient-il alors de l’exception exportée de ce paquetage (exemple 17.7)?

Exemple 17.7 Squelette du paquetage générique de gestion de nombres rationnels.

-- Ce paquetage generique permet le calcul avec les nombres-- rationnelsgeneric

type T_Entier is range <>;

package Nombres_Rationnels_G is

type T_Rationnel is private;

Division_Par_0 : exception; -- Exception levee si division-- par 0

-------------------------------------------------------------- Autres declarations et operations (sect. 16.4)...

private...

end Nombres_Rationnels_G;

Un exemplaire de l’exception Division_Par_0 sera créé dans chaquepaquetage instancié de Nombres_Rationnels_G. Cette situation normale peutconduire à un traitement d’exception où chaque exemplaire de l’exception doit êtreécrit en nom développé avec le nom du paquetage instancié comme préfixepuisqu’une exception ne peut pas être surchargée. Cette obligation de préfixage, sielle se révèle ennuyeuse, peut être évitée en créant un paquetage non génériqueenglobant la déclaration de Division_Par_0 et le paquetage générique, expurgéde cette même déclaration. Le traitement sera ainsi possible quel que soit lepaquetage instancié dont l’une des opérations a provoqué la levée de l’exception.

L’exemple 17.8 modifie l’exemple 17.7 de manière à déclarer l’exceptionDivision_Par_0 hors du paquetage générique Nombres_Rationnels_G.

Exemple 17.8 Paquetage englobant une déclaration d’exception et un paquetage générique.

-- Ce paquetage rend possible le traitement de l'exception-- Division_Par_0 independamment des instanciations

package Nombres_Rationnels_Complets is

Division_Par_0 : exception; -- Exception levee si division-- par 0

Page 408: Cours Ada de A à Z

PAQUETAGES GÉNÉRIQUES ET EXCEPTIONS 408

------------------------------------------------------------generic

type T_Entier is range <>;

package Nombres_Rationnels_G is

type T_Rationnel is private;

----------------------------------------------------------- Autres declarations et operations (sect. 16.4)...

private

...

end Nombres_Rationnels_G;

------------------------------------------------------------

end Nombres_Rationnels_Complets;

Page 409: Cours Ada de A à Z

TYPES DE DONNÉES ABSTRAITS 409

17.7 TYPES DE DONNÉES ABSTRAITS

Il est intéressant de terminer ce chapitre par une application particulièrementutile non seulement de la généricité mais encore des possibilités offertes par lespaquetages et les unités enfants. Il s’agit ici de mettre en œuvre la notion de typede données abstrait.

Un type de données abstrait (abstract data type) est un ensemble logique forméd’un type définissant la structure voulue ainsi que d’opérations disponibles sur lesvaleurs de ce type. Par définition, la composition exacte de la structure estinconnue, cachée à l’utilisateur d’où l’adjectif abstrait qualifiant le type dedonnées, et cela implique que les opérations proposées sont les seules disponiblespour ce type.

La création d’un type de données abstrait, souvent abrégé ADT, s’effectue toutd’abord indépendamment de tout langage de programmation. Il faut simplementdécider quelle structure mettre en œuvre et lui donner un nom (le nom du type),puis définir les opérations nécessaires, voire utiles, permettant la gestion de lastructure et de ses éléments. On parle alors d’encapsulation de la structure dedonnées et des opérations.

Figure 17.2 Types de données abstraits de nombre rationnel et de pile.

La figure 17.2 montre une première tentative de définition de deux types dedonnées abstraits sous une forme schématique simple séparant le nom du type desopérations. Si dans ces exemples les opérations sont énumérées arbitrairement, laconception propre d’un ADT doit obéir en fait à quelques règles simples.

Tout d’abord, un ADT doit être suffisant, si possible minimal et éventuellementcomplet. Un ADT est suffisant s’il met à disposition toutes les opérationsnécessaires à une application donnée. Il est minimal s’il ne comporte pasd’opérations redondantes et complet si toutes les opérations possibles raisonnablessont présentes. Dans la figure 17.2, les deux types de données abstraits sont

T_Nombre_Rationnel

ConstruireAdditionnerSoustraireMultiplierDiviserNumérateurDenominateur

T_Pile

CréerEffacerEmpilerDésempilerSommetVide

Page 410: Cours Ada de A à Z

TYPES DE DONNÉES ABSTRAITS 410

probablement suffisants pour des applications simples; ils sont assurémentminimaux, à condition que l’opération Effacer ne corresponde pas à effectuerDésempiler un certain nombre de fois; ils ne sont de loin pas complets puisque desopérations comme Copier ou Parcourir (pour une pile) ne sont pas présentes. Dansla pratique, la minimalité est parfois négligée pour des raisons de facilité etd’attractivité pour l’utilisateur.

Par ailleurs, les opérations sont toujours classées en trois catégories: lesconstructeurs, les sélecteurs et les itérateurs. Un constructeur (constructor) est uneopération agissant sur la structure complète et dont l’action modifie cette structure.Un sélecteur (selector) est une opération qui fournit une information sur lastructure dans sa globalité ou sur l’un de ses éléments, sans aucune modification.Enfin, un itérateur (iterator) agit sur tous les éléments de la structure, avec ou sansleur modification.

La figure 17.3 présente un type de données abstrait de pile suffisant, dont lesopérations sont classées dans les trois catégories précitées. Il n’est pas absolumentminimal car certaines opérations (Effacer, Sommet) pourraient être substituées pard’autres (Désempiler). Par contre, leur présence offre un confort nettement accrupour l’utilisateur.

Figure 17.3 Type de données abstrait de pile.

La mise en œuvre de tels types de données abstraits est agréablement simplifiéeen Ada non seulement par l’utilisation des paquetages génériques mais aussi parl’existence des types privés et limités exportés des paquetages. Et grâce aux

T_Pile

CréerEffacerEmpilerDésempilerCopier

Nom du type

Constructeurs

Sélecteurs

Itérateur

SommetVideCardinalité

Parcourir

Page 411: Cours Ada de A à Z

TYPES DE DONNÉES ABSTRAITS 411

paquetages enfants, il sera aussi facile d’étendre, de compléter la réalisation d’unADT si une extension de celui-ci est devenue nécessaire.

Exemple 17.9 Spécification de la réalisation d’un type de données abstrait de pile dynamique.

-- Bases de gestion de piles dynamiques generiquesgeneric

type T_Info is (<>);

package A_D_T_Piles_Dynamiques_G is

-- Le nom du type de donneestype T_Pile is limited private;

Pile_Vide : exception; -- Levee si la pile est vide

-------------------------------------------------------------- Constructeurs du type de donnees abstrait-------------------------------------------------------------- Cree une Pile videprocedure Creer ( Pile : out T_Pile );

-------------------------------------------------------------- Insere Info au sommet de Pileprocedure Empiler ( Pile : in out T_Pile;

Info : in T_Info );

-- Supprime l'information (au sommet de Pile) qui est rendue-- dans Info. Leve Pile_Vide si la suppression est impossible-- (la pile est vide)procedure Desempiler ( Pile : in out T_Pile;

Info : out T_Info );

-------------------------------------------------------------- Supprime tous les elements de Pileprocedure Effacer ( Pile : in out T_Pile );

-------------------------------------------------------------- Copie Pile_Source dans Pile_Destinationprocedure Copier ( Pile_Source : in T_Pile;

Pile_Destination : out T_Pile );

-------------------------------------------------------------- Selecteurs du type de donnees abstrait-------------------------------------------------------------- Retourne l'information du sommet de Pile. Leve Pile_Vide-- si la consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile ) return T_Info;

-------------------------------------------------------------- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile ) return Boolean;

-------------------------------------------------------------- Retourne le nombre d'elements de Pilefunction Cardinalite ( Pile : T_Pile ) return Natural;

------------------------------------------------------------

Page 412: Cours Ada de A à Z

TYPES DE DONNÉES ABSTRAITS 412

-- Iterateur du type de donnees abstrait-------------------------------------------------------------- Effectue un traitement sur tous les elements de la pileprocedure Parcourir ( Pile : in T_Pile );------------------------------------------------------------

private

type T_Element;

type T_Lien is access T_Element;

type T_Element is -- Pour un element de la pile

record

Information : T_Info; -- L'information sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Pile is -- Pour une pile dynamique

record

Sommet : T_Lien; -- Sommet de la pile

end record;

end A_D_T_Piles_Dynamiques_G;

L’exemple 17.9 réalise un type de données abstrait de pile dynamique. Lepaquetage A_D_T_Piles_Dynamiques_G est générique pour que la structure depile soit indépendante du type des informations gérées bien que cette indépendancesoit encore restreinte, ici à un type discret. De plus, la procédure Parcourircomporte toujours une procédure Traiter interne définie une fois pour toutes(sect. 10.7) et ne permet pas la définition d’un itérateur flexible. Le chapitre 18permettra d’obtenir toute la généralité nécessaire grâce aux types privés génériqueset aux sous-programmes paramètres génériques.

Il faut encore noter que le type exporté T_Pile est limité pour que toute copiede pile s’effectue par l’intermédiaire de la procédure Copier. Cependant, laprésence et l’utilité de la procédure Creer est discutable puisqu’en Ada, tout poin-teur est automatiquement initialisé à la valeur null.

En guise de conclusion à ce chapitre, il faut relever que les types de donnéesabstraits se situent entre la programmation structurée modulaire classique et laprogrammation orientée objet non abordée dans cet ouvrage.

Page 413: Cours Ada de A à Z

413

17.8 EXERCICES

17.8.1 Sous-programme générique

Ecrire une fonction générique Est_Pair_G qui indique si un nombre entier estpair ou non. Le type entier est le paramètre générique.

17.8.2 Type de données abstrait

Concevoir un type de données abstrait de queue en précisant les catégoriesd’opérations comme présenté dans la figure 17.3.

17.8.3 Réalisation d’un type de données abstrait

Réaliser le type de données abstrait de queue (exercice 17.8.2) de manièrestatique par un paquetage générique. Le type des informations ainsi que la taille desqueues seront des paramètres génériques.

17.8.4 Instanciations

Instancier plusieurs fois la fonction de l’exercice 17.8.1 et le paquetage del’exercice 17.8.3.

Page 414: Cours Ada de A à Z

POINTS À RELEVER 414

17.9 POINTS À RELEVER

17.9.1 En général

• La généricité permet l’indépendance de structures de données par rapport àleur contenu.

17.9.2 En Ada

• Une unité générique est une sorte de moule permettant la création depaquetages ou de sous-programmes instanciés.

• Les unités génériques favorisent la réutilisabilité du code source.

• Une unité générique est toujours déclarée en deux parties: la spécificationet le corps.

• Des valeurs, des types, des sous-programmes et des paquetages peuventêtre passés comme paramètres génériques.

• Les paramètres formels peuvent dépendre des paramètres précédents.

• Les paramètres formels ne sont jamais statiques.

• Un nouvel exemplaire d’une exception déclarée dans un paquetagegénérique est créé avec le même nom à chaque instanciation.

• Les types de données abstraits représentent une manière de structurercertains constituants d’une application.

Page 415: Cours Ada de A à Z

415

C H A P I T R E 1 8

U N I T É S G É N É R I QU E S : N O T I O N S

Page 416: Cours Ada de A à Z

416 UNITÉS GÉNÉRIQUES: NOTIONS AVANCÉES

Page 417: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 417

18.1 TYPES COMME PARAMÈTRES GÉNÉRIQUES

Dans le chapitre précédent les types discrets et numériques ont été présentéscomme paramètres génériques. Cette section complète cette présentation avec lestypes tableaux, accès et privés.

18.1.1 Types tableaux comme paramètres génériques

Les types tableaux constituent également une catégorie de paramètres géné-riques. Les deux façons les plus simples mais assez contraignantes de les écriresont les suivantes:

type T_Tableau_Contraint is array (T_Indice) of T_Element;

type T_Tableau_Non_Contraint is array (T_Indice range <>)of T_Element;

Mais l’inconvénient de cette manière de faire réside dans le respect des typesdes indices et des éléments pour le paramètre effectif. Il sera donc bien pluspratique de passer ces deux types comme paramètres génériques également,comme dans l’exemple 18.1.

Exemple 18.1 Types tableaux comme paramètres génériques et instanciation.

-- ...generic

type T_Indice is (<>); -- Les indices sont d'un type discret

type T_Element is range <>; -- Les elements sont d'un-- type entier

type T_Tableau_Contraint is array (T_Indice) of T_Element;

type T_Tableau_Non_Contraint is array (T_Indice range <>)of T_Element;

package Exemple_18_1_G is...

end Exemple_18_1_G;

-- Corps de Exemple_18_1_Gpackage body Exemple_18_1_G is ... end Exemple_18_1_G;

-- Instanciation de Exemple_18_1_G. On suppose declares les types-- type T_Contraint is array (1..10) of Integer;-- type T_Non_Contraint is array (Integer range <>) of Integer;

package Tableaux is new Exemple_18_1_G ( Integer, Integer,T_Contraint,T_Non_Contraint);

Les paramètres effectifs, ici aussi, doivent correspondre en tous points aux

Page 418: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 418

paramètres formels.

18.1.2 Types accès comme paramètres génériques

Toutes les formes de types accès se retrouvent dans la panoplie des paramètresgénériques. Les notations suivantes sont donc autorisées (sect. 15.2 et 15.8):

type T_Pt_Type is access T_Type;

type T_Pt_Type_Gen is access constant T_Type;type T_Pt_Type_Gen_All is access all T_Type;

type T_Pt_Procedure is access procedure ...;type T_Pt_Fonction is access function ...;

Dans tous les cas, les paramètres effectifs correspondants doivent être sem-blables. Les types pointés seront souvent également spécifiés comme paramètresgénériques.

18.1.3 Types privés comme paramètres génériques

Les types privés constituent le genre de paramètres génériques le plusintéressant. Ils permettent de paramétrer un paquetage générique par un type quel-conque non limité et soit contraint, soit non contraint mais alors avec valeur pardéfaut. La notation est identique à celle des types privés exportés d’un paquetage:

type T_Prive is private;

Il faut immédiatement insister sur le fait que, comme la structure du typeT_Prive est inconnue dans le paquetage générique, l’utilisation du type T_Privedans ce paquetage est semblable à l’utilisation d’un type privé commeT_Rationnel à l’extérieur du paquetage Nombres_Rationnels (sect. 16.4).

Exemple 18.2 Type privé comme paramètre générique et instanciations.

-- Bases de gestion de queues statiques generiquesgeneric

Longueur_Max : in Natural := 100; -- Longueur maximale d'une-- queue (sect. 17.3)

type T_Info is private; -- Fourni par l'unite-- utilisatrice

package Queues_Statiques_G is

subtype T_Longueur is Integer range 0..Longueur_Max;subtype T_Numerotation is Integer range 1..Longueur_Max;subtype T_Pos_Indic is Integer range 1..Longueur_Max + 1;

type T_Contenu is array ( T_Numerotation ) of T_Info;

type T_Queue_Statique is -- Pour une queue statiquerecord

-- Contenu de la queueContenu : T_Contenu;

Page 419: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 419

-- Longueur de la queueLongueur : T_Longueur := T_Longueur'First;

-- Tete de la queueTete : T_Pos_Indic := T_Pos_Indic'First;

-- Queue de la queueQueue : T_Pos_Indic := T_Pos_Indic'First;

end record;-------------------------------------------------------------- Autres declarations (§ 14.4.2)...

end Queues_Statiques_G;

-- Corps de Queues_Statiques_Gpackage body Queues_Statiques_G is ... end Queues_Statiques_G;

-- Instanciations du paquetage generique (§ 7.2.1 et 16.4)package Queues_Entiers is new

Queues_Statiques_G (T_Info => Integer);

package Queues_Dates is new Queues_Statiques_G (80, T_Date);

package Queues_Nombres_Rationnels is newQueues_Statiques_G (T_Info =>

Nombres_Rationnels.T_Rationnel);

package Queues_De_Queues is newQueues_Statiques_G (T_Info => Queues_Dates.T_Queue_Statique);

Le corps du paquetage générique Queues_Statiques_G (exemple 18.2) estidentique à celui du paquetage non générique Queues_Statiques (§ 14.4.3 etsuivants). Les instanciations montrent différentes possibilités pour les paramètres,du type Integer au type article T_Date pour les deux cas simples. Les typesprivés T_Rationnel et T_Queue illustrent le fait qu’un type privé peutnaturellement jouer le rôle du paramètre effectif d’un paramètre formel génériquecorrespondant. Un type privé comme paramètre générique est le seul moyenpermettant de spécifier un type article comme paramètre effectif.

18.1.4 Paramètres génériques constitués de types privés avec discriminants

La règle qui empêche un type non contraint (sans valeur par défaut) d’êtrementionné comme paramètre effectif d’un paramètre formel générique privé vientdu fait suivant: il serait alors impossible, dans le paquetage, de donner l’indis-pensable contrainte lors de la déclaration d’un objet de ce type (puisque tout objetdoit être contraint); ceci est donc contradictoire avec le fait que le paramètre formelest déclaré comme privé, donc de structure inconnue.

Cette restriction peut être levée si le paramètre formel générique mentionne la

Page 420: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 420

présence de discriminants inconnus par la notation:type T_Prive_Avec_Discr_Inconnus (<>) is private;

Dans ce cas, n’importe quel type non limité est possible comme paramètre effectifmais, à l’intérieur du paquetage générique, il ne sera pas possible de déclarer desobjets non initialisés. L’initialisation obligatoire fournira alors les valeurs desdiscriminants ou les contraintes nécessaires aux objets déclarés.

Un problème subsiste cependant: comment obtenir la valeur initiale d’un objetpuisque le type est privé? La réponse à cette question nécessite le passage deconstantes ou de fonctions comme paramètres génériques. L’exemple 18.3 quiillustre la mention d’un type privé avec discriminants inconnus sera donc reprisultérieurement (sect. 18.2).

Exemple 18.3 Type privé à discriminants inconnus comme paramètre générique.

-- Gestion d'un tampon generique de type quelconque non limitegeneric

type T_Info (<>) is private;

Valeur_Initiale : in T_Info; -- Pour la valeur initiale-- du tampon

package Tampon_Non_Limite_G is

procedure Nouvelle_Valeur (Info : in T_Info);function Valeur_Actuelle return T_Info;

end Tampon_Non_Limite_G;

package body Tampon_Non_Limite_G is

Tampon : T_Info := Valeur_Initiale; -- La valeur initiale-- donne la contrainte

------------------------------------------------------------

procedure Nouvelle_Valeur (Info : in T_Info) isbegin -- Nouvelle_Valeur

Tampon := Info;

end Nouvelle_Valeur;------------------------------------------------------------

function Valeur_Actuelle return T_Info isbegin -- Valeur_Actuelle

return Tampon;

end Valeur_Actuelle;------------------------------------------------------------

end Tampon_Non_Limite_G;

Pour être complet, mais sans entrer dans les détails, il est possible de men-tionner un type privé avec discriminants connus comme par exemple:

Page 421: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 421

type T_Prive_Avec_Discr_Connu (Discr : T_Discr) is private;

Le type mentionné comme paramètre effectif doit alors comporter un dis-criminant du type ou sous-type T_Discr, avec ou sans valeur par défaut (leparamètre formel ne mentionne jamais de valeur par défaut).

18.1.5 Types limités privés comme paramètres génériques

Un type privé mentionné comme paramètre générique peut être limité par lanotation:

type T_Limite_Prive is limited private;

Tout ce qui a été présenté pour les types privés non limités (§ 18.1.3 et 18.1.4)s’applique aussi pour de tels paramètres qui, puisqu’ils sont limités, interdisentl’affectation et les opérations d’égalité et d’inégalité prédéfinies sur les objets dece type. N’importe quel type est possible comme paramètre effectif pourvu qu’il nesoit pas non contraint sans valeur par défaut. Mais en utilisant la notation intro-duisant des discriminants inconnus

type T_Limite_Prive (<>) is limited private;

n’importe quel type sans restriction peut être mentionné comme paramètre effectif!Cependant, il devient impossible de déclarer des constantes ou variables réma-nentes (sect. 10.2) de type T_Limite_Prive dans le paquetage.

18.1.6 Types privés comme paramètres génériques et comme types exportés

Tous les exemples vus jusqu’à présent illustraient des paquetages dans lesquelsle type exporté, s’il existait, était visible, ceci pour ne pas induire de confusion pourle lecteur entre un type privé comme paramètre générique et ce type exporté. Maisil est naturellement possible et souhaitable de construire des paquetages génériquesdont le ou les types exportés sont privés (exemple 18.4), voire limités privés.

Exemple 18.4 Types privés comme paramètre générique et comme type exporté.

-- Bases de gestion de queues statiques generiquesgeneric

type T_Info is private; -- Fourni par l'unite utilisatrice

package Queues_Statiques_G is

-- Pour une queue statique de longueur Tailletype T_Queue_Statique ( Taille : Positive ) is private;-------------------------------------------------------------- Operations sur une queue statique (§ 14.4.2) sauf-- Parcourir dont le cas est traite plus loin (sect. 18.3)...

private

Page 422: Cours Ada de A à Z

TYPES COMME PARAMÈTRES GÉNÉRIQUES 422

type T_Contenu is array ( Positive range <>) of T_Info;

type T_Queue_Statique ( Taille : Positive ) is

record

-- Contenu de la queueContenu : T_Contenu ( 1 .. Taille );

Longueur : Natural := 0; -- Longueur de la queue

Tete : Positive := 1; -- Tete de la queueQueue : Positive := 1; -- Queue de la queue

end record;

end Queues_Statiques_G;

La partie privée de ce dernier exemple ne contient plus de déclarations de sous-types utilisés dans les champs du type T_Queue_Statique contrairement auxprécédents paquetages de gestion de queues statiques. En effet, la taille des queuesest ici fournie par un discriminant d’article ne permettant pas la création de cessous-types.

Page 423: Cours Ada de A à Z

SOUS-PROGRAMMES COMME PARAMÈTRES GÉNÉRIQUES 423

18.2 SOUS-PROGRAMMES COMME PARAMÈTRES GÉNÉRIQUES

Les paquetages de gestion de queues et de piles (chap. 14 et 15) comportent uneprocédure Traiter (alors interne) qui constitue une excellente candidate pourdevenir un paramètre générique. En effet, et plus généralement, des traitementssont souvent nécessaires dans un paquetage (générique), traitements connus dansl’unité utilisatrice mais pas (complètement) dans le paquetage. Le passage de sous-programmes en paramètres génériques donne une solution à ces problèmes.

La forme la plus simple pour la réalisation de ce mécanisme est donnée par lediagramme syntaxique de la figure 18.1.

Figure 18.1 Diagramme syntaxique de sous-programme comme paramètre générique.

Il faut noter la présence du mot réservé with qui permet de distinguer un sous-programme paramètre générique d’un sous-programme générique lui-même (sect.17.5 et 18.3)! L’exemple 18.5 illustre la présence d’un tel paramètre.

Exemple 18.5 Fonction d’initialisation comme paramètre générique.

-- Gestion d'un tampon generique de type quelconque non limite,-- voir l'exemple 18.3generic

type T_Info (<>) is private;with function Valeur_Initiale return T_Info;

package Tampon_Non_Limite_G is

procedure Nouvelle_Valeur (Info : in T_Info);function Valeur_Actuelle return T_Info;

end Tampon_Non_Limite_G;

Procédure comme paramètre générique (forme simple)

procedure identificateur liste de paramètreswith ;

fonction comme paramètre générique (forme simple)

function identificateur liste de paramètreswith ;return Id. type

Page 424: Cours Ada de A à Z

SOUS-PROGRAMMES COMME PARAMÈTRES GÉNÉRIQUES 424

package body Tampon_Non_Limite_G is

Tampon : T_Info := Valeur_Initiale; -- Utilisation du... -- parametre generique

end Tampon_Non_Limite_G;

L’instanciation (exemple 18.6) d’un paquetage comportant un ou plusieurssous-programmes comme paramètres génériques s’effectue comme toute autreinstanciation, en spécifiant un sous-programme dont le profil et le mode de passagede ses paramètres correspond à ceux du paramètre formel.

Exemple 18.6 Instanciations du paquetage générique Tampon_Non_Limite_G.

function Valeur_Nulle return Float isbegin -- Valeur_Nulle

return 0.0;

end Valeur_Nulle;

function Chaine_Nulle return String isbegin -- Chaine_Nulle

return "";

end Chaine_Nulle;...package Tampon_Reel is

new Tampon_Non_Limite_G (Float, Valeur_Nulle);

package Tampon_Chaine isnew Tampon_Non_Limite_G (String, Chaine_Nulle);

Une autre application plus connue des sous-programmes comme paramètresgénériques consiste à résoudre le problème du traitement des informationscontenues dans des structures comme les queues et les piles. En effet, le parcoursde telles structures (§ 15.4.9 par exemple) implique l’application d’une procédureTraiter à chaque élément de la structure. Or, si le type de ces informations estlui-même un paramètre générique (sect. 17.4 et 18.1), il devient ainsi naturel detransmettre cette procédure également comme paramètre générique.

Exemple 18.7 Traitement des informations contenues dans une queue statique générique.

-- Bases de gestion de queues statiques generiquesgeneric

type T_Info is private;

with procedure Traiter ( Info : in out T_Info );

package Queues_Statiques_G is

Page 425: Cours Ada de A à Z

SOUS-PROGRAMMES COMME PARAMÈTRES GÉNÉRIQUES 425

-- Pour une queue statique de longueur Tailletype T_Queue_Statique ( Taille : Positive ) is private;-------------------------------------------------------------- Operations sur une queue statique (§ 14.4.2)...------------------------------------------------------------

private...

end Queues_Statiques_G;

Il faut cependant relever que l’instanciation ne permettra de spécifier qu’uneseule procédure de traitement. Cette limitation pourra néanmoins être éliminée parla transformation de la procédure Parcourir, qui utilise Traiter, en procéduregénérique (sect. 18.3).

Finalement, il existe la possibilité de mentionner des valeurs par défaut pour lessous-programmes paramètres génériques en complétant leur déclaration commedans les cas suivants:

with procedure Traiter ( Info : in out T_Info ) is <>;with function Valeur_Initiale return T_Info is <>;

Si aucun paramètre effectif n’est donné lors de l’instanciation, la valeur pardéfaut consistera à utiliser une procédure ou fonction de même nom dont le profilet les modes de passage des paramètres sont identiques, procédure ou fonction quidoit être unique et visible à l’endroit de l’instanciation. Un exemple intéressantd’une telle valeur par défaut consiste à écrire une procédure de tri générique,applicable à un tableau d’éléments d’un type quelconque. Cette procédure estprésentée dans la section traitant des sous-programmes génériques (sect. 18.3).Pour être complet, il faut encore indiquer qu’il existe une deuxième forme devaleur par défaut non explicitée dans cet ouvrage:

with procedure Traiter ( Info : in out T_Info ) is Afficher;with function Valeur_Initiale return T_Info is Valeur_Nulle;

Page 426: Cours Ada de A à Z

RETOUR SUR LES SOUS-PROGRAMMES GÉNÉRIQUES 426

18.3 RETOUR SUR LES SOUS-PROGRAMMES GÉNÉRIQUES

Cette section va compléter l’exemple simple donné précédemment (sect. 17.5)par la présentation de deux cas plus complexes. Le premier réside dans lagénéralisation des procédures Parcourir examinées lors de la présentation desstructures linéaires. Il s’agit ici de les transformer en procédures génériquescomportant une seul paramètre générique constitué de la procédure Traiter. Lasolution ainsi définie (exemple 18.8) est meilleure que celle donnée auparavant(exemple 18.7) car plusieurs procédures de parcours vont pouvoir être instanciéespour chaque instanciation de l’un des paquetages de gestion de ces structures.

Exemple 18.8 Traitement des informations contenues dans une queue statique générique.

-- Bases de gestion de queues statiques generiquesgeneric

type T_Info is private;

package Queues_Statiques_G is

-- Pour une queue statique de longueur Tailletype T_Queue_Statique ( Taille : Positive ) is private;------------------------------------------------------------

-- Operations sur une queue statique (§ 14.4.2)...------------------------------------------------------------generic

with procedure Traiter ( Info : in out T_Info );

procedure Parcourir_G ( La_Queue : in out T_Queue_Statique );------------------------------------------------------------

private...

end Queues_Statiques_G;

-- Corps de Queues_Statiques_Gpackage body Queues_Statiques_G is ... end Queues_Statiques_G;

-- Instanciations du paquetage et de la procedure Parcourir_G. Le-- type T_Date est defini au paragraphe 6.2.1

package Queues_Dates is new Queues_Statiques_G ( T_Date );

-- On suppose que la procedure Afficher affiche une dateprocedure Afficher_Queue is

new Queues_Dates.Parcourir_G ( Afficher );

-- On suppose que la procedure Plus_Un_Jour incremente une date-- d'un jourprocedure Mise_A_Jour is

new Queues_Dates.Parcourir_G ( Plus_Un_Jour );

Page 427: Cours Ada de A à Z

RETOUR SUR LES SOUS-PROGRAMMES GÉNÉRIQUES 427

Cette section se termine par une procédure de tri générique (exemple 18.9) dontla réalisation regroupe l’utilisation de notions maintenant connues. L’algorithme detri choisi consiste à trouver le plus petit élément contenu dans un tableau et à leplacer en première position, par échange avec l’élément qui s’y trouvait, puis àrecommencer pour le tableau amputé de son premier élément et ainsi de suite. Cetalgorithme est connu sous le nom de tri par sélection (selection sort).

Exemple 18.9 Procédure de tri générique.

-- Procedure generique de tri d'une table selon l'algorithme de-- tri par selectiongeneric

type T_Indice is (<>); -- Les indices sont de type discret,type T_Element is private; -- les elements les plus generaux

-- possiblestype T_Table is array (T_Indice range <>) of T_Element;

with function "<" (X, Y : T_Element ) return Boolean is <>;

procedure Trier_G ( Table : in out T_Table );

procedure Trier_G ( Table : in out T_Table ) is

Tampon : T_Element; -- Pour la permutationIndice_Plus_Petit : T_Indice;-- Pour le plus petit element

begin -- Trier_G

for I in Table'First .. T_Indice'Pred ( Table'Last ) loop

Indice_Plus_Petit := I;

-- Rechercher le plus petit parmi ceux restantsfor J in T_Indice'Succ ( I ) .. Table'Last loop

if Table ( J ) < Table ( Indice_Plus_Petit ) thenIndice_Plus_Petit := J;-- Un nouveau plus petit trouve

end if;

end loop;

-- Permuter le plus petit element trouve avec le premierTampon := Table ( I );Table ( I ):= Table ( Indice_Plus_Petit );Table ( Indice_Plus_Petit ) := Tampon;

end loop;

end Trier_G;

L’opérateur "<" doit être mentionné comme paramètre formel puisque le typedes éléments du tableau à trier est privé. L’utilisation d’une valeur par défaut àl’instanciation est pratique dans tous les cas où le type des éléments est ordonné. A

Page 428: Cours Ada de A à Z

RETOUR SUR LES SOUS-PROGRAMMES GÉNÉRIQUES 428

contrario, il faut fournir une fonction de comparaison ad hoc. Des instanciationsavec des types prédéfinis peuvent s’écrire:

procedure Trier is new Trier_G ( Integer, Character, String );procedure Trier is new Trier_G ( Integer, Float, T_Vecteur, "<");

Ces deux procédures instanciées se surchargent! La première triera un tableaude caractères, la seconde un vecteur (§ 8.2.3). La fonction de comparaison utiliséeest l’opérateur "<" sur les caractères dans la première instanciation (valeur pardéfaut) alors qu’il s’agit de l’opérateur "<" disponible avec le type Float dans laseconde, opérateur mentionné cette fois explicitement.

Page 429: Cours Ada de A à Z

UNITÉS DE BIBLIOTHÈQUE ET ENFANTS GÉNÉRIQUES 429

18.4 UNITÉS DE BIBLIOTHÈQUE ET ENFANTS GÉNÉRIQUES

Une spécification de paquetage ou de sous-programme générique, de même queson corps peuvent être compilés pour eux-mêmes et former une nouvelle unité debibliothèque. Mais la généricité peut aussi être associée à la notion d’unité enfant(sect. 16.6 et suivantes). En effet, les enfants d’un parent non générique peuvent ounon être génériques, alors que ceux d’un parent générique le sont obligatoirement.Les règles définissant l’instanciation sont les suivantes:

• si l’unité parent n’est pas générique, un enfant générique peut être instanciépartout où il est visible;

• si l’unité parent est générique, un enfant peut être instancié sans restrictionà l’intérieur du parent, mais ne pourra l’être à l’extérieur que lorsque sonparent sera lui-même instancié; une clause de contexte pour l’enfant seraalors nécessaire.

Exemple 18.10 Squelette du paquetage parent générique de gestion de nombres rationnels.

-- Ce paquetage generique permet le calcul avec les nombres-- rationnelsgeneric

type T_Entier is range <>;

package Nombres_Rationnels_G is

type T_Rationnel is private;

-- Autres declarations et operations (sect. 16.4)...

private

...

end Nombres_Rationnels_G;

package body Nombres_Rationnels_G is-- Corps des operations exportees (sect. 16.4)...

end Nombres_Rationnels_G;

L’exemple 18.11 illustre la définition d’un paquetage enfant générique enreprenant le cas des nombres rationnels (sect. 16.4). Des opérations d’entrées-sorties sur des valeurs de type T_Rationnel constituent le paquetage enfant.Ainsi il est possible de travailler avec des nombres rationnels en choisissant ou nond’effectuer des lectures ou des écritures de tels nombres. Le parent est d’abordrappelé dans l’exemple 18.10, puis le corps de Nombres_Rationnels_G.ES_Gest donné dans l’exemple 18.12.

Page 430: Cours Ada de A à Z

UNITÉS DE BIBLIOTHÈQUE ET ENFANTS GÉNÉRIQUES 430

Exemple 18.11 Spécification du paquetage enfant générique Nombres_Rationnels_G.ES_G.

-- Ce paquetage enfant generique permet les entrees-sorties de-- nombres rationnels

generic

package Nombres_Rationnels_G.ES_G is

-------------------------------------------------------------- Lecture d'un nombre rationnelprocedure Get ( X : out T_Rationnel );-------------------------------------------------------------- Ecriture d'un nombre rationnelprocedure Put ( X : in T_Rationnel );------------------------------------------------------------

end Nombres_Rationnels_G.ES_G;

Exemple 18.12 Corps du paquetage générique Nombres_Rationnels_G.ES_G.

-- Ce paquetage generique permet le calcul avec les nombres-- rationnelswith Ada.Text_IO; use Ada.Text_IO;package body Nombres_Rationnels_G.ES_G is

package Entier_IO is new Integer_IO ( T_Entier );use Entier_IO; -- Evite de prefixer Get et Put sur les entiers

-------------------------------------------------------------- Lecture d'un nombre rationnelprocedure Get ( X : out T_Rationnel ) isbegin -- Get

Put ("Numerateur: ");Get ( X.Numerateur );Put ("Denominateur: ");Get ( X.Denominateur );

end Get;

-------------------------------------------------------------- Ecriture d'un nombre rationnelprocedure Put ( X : in T_Rationnel ) isbegin -- Put

Put ( X.Numerateur, 1); -- Minimum de positions [ARM A.10.6]Put ( " /" );Put ( X.Denominateur, 1);

end Put;------------------------------------------------------------

end Nombres_Rationnels_G.ES_G;

Page 431: Cours Ada de A à Z

UNITÉS DE BIBLIOTHÈQUE ET ENFANTS GÉNÉRIQUES 431

Finalement, les instanciations peuvent être effectuées en commençant par celledu paquetage parent comme mentionné dans l’une des règles du début de cettesection:

package Nombres_Rationnels isnew Nombres_Rationnels_G ( Integer

);package ES_Nombres_Rationnels is new Nombres_Rationnels.ES_G;

Il faut noter que les instanciations d’unités enfants génériques peuvent devenirdes unités de bibliothèque (§ 10.6.1).

Comme tous les enfants d’un parent générique sont aussi génériques, il estprobable que nombre d’entre eux, y compris Nombres_Rationnels_G.ES_G,n’auront pas de paramètres génériques propres car l’utilisation des paramètresgénériques du parent est autorisée.

Page 432: Cours Ada de A à Z

RETOUR SUR LES TYPES DE DONNÉES ABSTRAITS 432

18.5 RETOUR SUR LES TYPES DE DONNÉES ABSTRAITS

L’exemple 18.13 réalise un type de données abstrait de pile de la manière la plusgénérale possible. Le type T_Info est privé pour que le type des informationsgérées soit le plus général possible. Comme illustré précédemment (sect. 18.3), laprocédure Parcourir est maintenant générique, avec la procédure Traitercomme paramètre, ce qui va permettre de créer plusieurs itérateurs différents pourune seule instanciation du paquetage.

Le lecteur peut comparer le paquetage ci-dessous avec celui de l’exemple 17.9.

Exemple 18.13 Spécification de la réalisation d’un type de données abstrait de pile dynamique.

-- Bases de gestion de piles dynamiques generiquesgeneric

type T_Info is private;

package A_D_T_Piles_Dynamiques_G is

-- Le nom du type de donneestype T_Pile is limited private;

Pile_Vide : exception; -- Levee si la pile est vide

-------------------------------------------------------------- Constructeurs du type de donnees abstrait-------------------------------------------------------------- Cree une Pile videprocedure Creer ( Pile : out T_Pile );

-------------------------------------------------------------- Insere Info au sommet de Pileprocedure Empiler ( Pile : in out T_Pile;

Info : in T_Info );

-------------------------------------------------------------- Supprime l'information (au sommet de Pile) qui est rendue-- dans Info. Leve Pile_Vide si la suppression est impossible-- (la pile est vide)procedure Desempiler ( Pile : in out T_Pile;

Info : out T_Info );

-------------------------------------------------------------- Supprime tous les elements de Pileprocedure Effacer ( Pile : in out T_Pile );

-------------------------------------------------------------- Copie Pile_Source dans Pile_Destinationprocedure Copier ( Pile_Source : in T_Pile;

Pile_Destination : out T_Pile );

-------------------------------------------------------------- Selecteurs du type de donnees abstrait-------------------------------------------------------------- Retourne l'information du sommet de Pile. Leve Pile_Vide

Page 433: Cours Ada de A à Z

RETOUR SUR LES TYPES DE DONNÉES ABSTRAITS 433

-- si la consultation est impossible (la pile est vide)function Sommet ( Pile : T_Pile ) return T_Info;

-------------------------------------------------------------- Retourne True si Pile est vide, False sinonfunction Vide ( Pile : T_Pile ) return Boolean;

-------------------------------------------------------------- Retourne le nombre d'elements de Pilefunction Cardinalite ( Pile : T_Pile ) return Natural;

-------------------------------------------------------------- Iterateur du type de donnees abstrait-------------------------------------------------------------- Effectue un traitement sur tous les elements de la pilegeneric

with procedure Traiter ( Info : in out T_Info );

procedure Parcourir ( Pile : in T_Pile );

------------------------------------------------------------

private

type T_Element;

type T_Lien is access T_Element;

type T_Element is -- Pour un element de la pilerecord

Information : T_Info; -- L'information sur un seul champSuivant : T_Lien; -- Pour reperer l'element qui suit

end record;

type T_Pile is -- Pour une pile dynamiquerecord

Sommet : T_Lien; -- Sommet de la pileend record;

end A_D_T_Piles_Dynamiques_G;

Page 434: Cours Ada de A à Z

434

18.6 EXERCICES

18.6.1 Sous-programme générique

Ecrire une fonction générique Maximum qui détermine la valeur maximale d’ungroupe de valeurs dont le type est un paramètre générique discret. En faire de mêmepour un paramètre générique de type numérique, de type article, de type accès (lesvaleurs considérées sont ici celles des variables pointées) et enfin de type privé.

18.6.2 Type de données abstrait

Concevoir un type de données abstrait d’ensemble (au sens mathématique duterme) en précisant les catégories d’opérations comme présenté dans la figure 17.3.

18.6.3 Réalisation d’un type de données abstrait

Réaliser le type de données abstrait d’ensemble (exercice 18.6.1) dont leséléments sont d’un type discret.

18.6.4 Réalisation d’un type de données abstrait

Réaliser le type de données abstrait d’ensemble (exercice 18.6.1) de manièreaussi générale que possible.

Page 435: Cours Ada de A à Z

POINTS À RELEVER 435

18.7 POINTS À RELEVER

18.7.1 En général

• La généricité se rencontre dans des langages comme Ada et C++.

18.7.2 En Ada

• Les types privés constituent un cas intéressant de paramètre générique parla généralité qu’ils apportent.

• Les unités enfants peuvent aussi être génériques.

Page 436: Cours Ada de A à Z

436

C H A P I T R E 1 9

S U R N O M S , T Y P E S D É R I V É S , A N N E X E S

Page 437: Cours Ada de A à Z

437 SURNOMS, TYPES DÉRIVÉS, ANNEXES PRÉDÉFINIES

Page 438: Cours Ada de A à Z

SURNOMMAGE 438

19.1 SURNOMMAGE

Sans constituer un point absolument fondamental du langage Ada, lesurnommage (renaming) possède quelques avantages méritant une présentationdans cette section. Grâce à cette notion, il est possible de:

• donner un autre nom, c’est-à-dire déclarer un surnom, à un objet, uneexception, un paquetage, une unité générique ou un sous-programme;

• fournir un corps à un sous-programme dont la spécification a déjà étédéclarée.

Un objet est surnommé par la forme de déclaration suivante:id_d_objet : id_de_type_ou_sous_type renames objet_surnomme;

avec• id_d_objet le surnom déclaré;• id_de_type_ou_sous_type le type ou le sous-type du surnom;• renames un nouveau mot réservé indiquant le surnommage;• objet_surnomme l’objet surnommé.

L’objet surnommé peut être une constante, une variable, un élément d’un objetcomposé ou une tranche de tableau. Le surnom s’applique à l’objet identifié aumoment du surnommage comme pour la variable Reponse de l’exemple 19.1. Demanière peut-être surprenante, une éventuelle contrainte mentionnée par le sous-type n’a aucune influence sur le surnom. Celui-ci est simplement un nouveau nompour un objet et de ce fait hérite de toutes les caractéristiques de l’objet surnommé.

Exemple 19.1 Surnommage d’objets.

-- Voir la section 10.7 pour le paquetage Nombres_Rationnels et-- la constante ZeroZero : Nombres_Rationnels.T_Rationnel

renames Nombres_Rationnels.Zero;

-- Voir l'exemple 18.6 pour le paquetage Tampon_Chaine et la-- variable TamponTampon_Caracteres : String renames Tampon_Chaine.Tampon;

-- § 7.2.1 pour l'article NoelJour : Integer renames Noel.Jour;

-- § 9.2.3 pour la tranche Chaine (1..Nombre_Car_Lus)Reponse : String renames Chaine (1..Nombre_Car_Lus);

Le surnommage d’un composant ou d’une tranche de tableau est soumis à desrestrictions non mentionnées ici car elles ne concernent que quelques casparticuliers. L’intérêt de surnommer de tels objets réside principalement dans unemeilleure lisibilité et une simplification du code.

Page 439: Cours Ada de A à Z

SURNOMMAGE 439

Une exception est surnommée (exemple 19.2) selon la forme de déclarationsuivante:

id_d_exception : exception renames exception_surnommee;

avec• id_d_exception le surnom déclaré;• exception_surnommee l’exception surnommée.

Exemple 19.2 Surnommage d’exceptions.

-- Voir la section 10.7 pour le paquetage Nombres_RationnelsDiv_Par_0 : exception

renames Nombres_Rationnels.Division_Par_Zero;

-- Voir la section 12.5 pour le paquetage Ada.IO_ExceptionsFin_Fichier : exception renames Ada.IO_Exceptions.End_Error;

Un paquetage (non générique) est surnommé (exemple 19.3) selon la dé-claration suivante:

package id_de_paquetage renames paquetage_surnomme;

avec• id_de_paquetage le surnom déclaré;• paquetage_surnomme le paquetage surnommé.

Exemple 19.3 Surnommage de paquetages.

-- Voir la section 10.7 pour le paquetage Nombres_Rationnelspackage Fractions renames Nombres_Rationnels;

-- Voir la section 12.5 pour le paquetage Ada.IO_Exceptionspackage Erreurs_ES renames Ada.IO_Exceptions;

Une unité générique est surnommée (exemple 19.4) par l’une des trois formesde déclaration suivantes:

generic package id_de_paquetage renames paquetage_generique;generic procedure id_de_procedure renames procedure_generique;generic function nom_de_fonction renames fonction_generique;

avec• id_de_paquetage, id_de_procedure et nom_de_fonction les

surnoms déclarés où nom_de_fonction peut être un identificateur ou unopérateur;

• paquetage_generique le paquetage générique surnommé;

Page 440: Cours Ada de A à Z

SURNOMMAGE 440

• procedure_generique la procédure générique surnommée;• fonction_generique la fonction ou l’opérateur générique surnommé.

Exemple 19.4 Surnommage d’unités génériques.

-- Voir la section 17.6 pour le paquetage Nombres_Rationnels_Ggeneric package Fractions_G renames Nombres_Rationnels_G;

-- Voir la section 18.3 pour la procedure Trier_Ggeneric procedure Sort_G renames Trier_G;

-- [ARM 13.9] pour la fonction Ada.Unchecked_Conversiongeneric function Conversion_Brute

renames Ada.Unchecked_Conversion;

Pour les paquetages, comme pour les exceptions et les unités génériques, lesurnommage permet de simplifier la notation en raccourcissant les noms.

Le surnommage de sous-programmes (non génériques) permet non seulementde créer un surnom d’un sous-programme mais encore de fournir un corps à unespécification de sous-programme, dans un corps de paquetage par exemple. Dansles deux cas, la forme de déclaration est donnée par l’un des diagrammes syn-taxiques de la figure 19.1.

Figure 19.1 Diagrammes syntaxiques de surnommage de sous-programmes.

Surnommage de procédure

procedure identificateur liste de paramètres renames id de procédure

Surnommage de fonction ou de fonction-opérateur

function surnom de fonction liste de paramètres

renames

return id de type ousous-typeou d’opérateur

nom de fonctionou d’opérateur

de surnom

Page 441: Cours Ada de A à Z

SURNOMMAGE 441

Effectuer le surnommage d’un sous-programme (exemple 19.5) représente uneopération moins triviale que dans le cas des exceptions, des paquetages ou encoredes unités génériques. En effet, certaines règles doivent être respectées:

• lors de la définition d’un surnom d’un sous-programme, le nombre, le type,le mode de passage des paramètres et le type du résultat (dans le cas d’unefonction ou d’une fonction-opérateur) du surnom doivent être identiques àceux du sous-programme surnommé;

• lors de la fourniture du corps d’une spécification, non seulement la règleprécédente s’applique aussi, mais il faut encore que les contraintes des(éventuels) sous-types des paramètres et du résultat soient les mêmes.

Les noms des paramètres peuvent donc être librement choisis. Par contre, il fautmentionner que les valeurs par défaut apparaissant dans la définition d’un surnomainsi que leur présence ou absence se substituent à ce qui existait dans le sous-programme surnommé lors de l’utilisation du surnom. A contrario, les contraintessur les paramètres du sous-programme surnommé ou le résultat de la fonctionsurnommée s’appliquent aussi au surnom, quelles que soient celles présentes dansla définition de ce surnom.

Exemple 19.5 Surnoms de sous-programmes.

-- § 4.3.4procedure Up ( Caractere : in out Character )

renames Majusculiser;

-- [ARM A.5]function Sqrt (X : Float) return Float

renames Ada.Numerics.Elementary_Functions.Sqrt;

-- Voir la section 10.3function "**" (X : Nombres_Rationnels.T_Rationnel;

Exp : Natural)return Nombres_Rationnels.T_Rationnelrenames Nombres_Rationnels.Puissance;

-- Voir la section 10.7use Nombres_Rationnels;function Creer ( Numerateur : Integer;

Denominateur : Positive )return T_Rationnel renames "/";

Parmi les avantages du surnommage des sous-programmes, citons la levéed’ambiguïtés lors d’utilisation de clauses use (§ 10.5.2), l’utilisation de fonctionssous la forme de fonctions-opérateurs ou encore comme alternative aux clausesuse type (§ 11.5.3).

Page 442: Cours Ada de A à Z

SURNOMMAGE 442

Les unités enfants (paquetages ou sous-programmes) peuvent naturellementaussi être surnommées. Par exemple, le paquetage d’entrées-sorties Ada.Text_IOest surnommé en Text_IO dans la norme Ada 95 pour des raisons de compatibilitéavec la norme précédente Ada 83.

Le surnommage d’un type peut s’effectuer grâce à un sous-type sans contrainte(§ 6.1.1). Finalement et pour être complet, il est possible de surnommer des valeursénumérées ainsi que certains attributs comme Succ et Pred.

Page 443: Cours Ada de A à Z

OPÉRATIONS PRIMITIVES ET TYPES DÉRIVÉS 443

19.2 OPÉRATIONS PRIMITIVES ET TYPES DÉRIVÉS

Une opération primitive (primitive operation) est une opération disponible surles valeurs d’un type et appartenant à l’une des catégories suivantes:

• les opérations prédéfinies comme l’affectation, l’égalité prédéfinie,certains attributs;

• les sous-programmes déclarés dans une spécification de paquetage etcomportant un paramètre ou un résultat de ce type alors que ce type est unedéclaration de la spécification du paquetage;

• les opérations primitives héritées de son parent si le type est dérivé.

Par exemple, les opérateurs arithmétiques et ceux de comparaison sont desopérations primitives des types Integer et Float (conceptuellement, ces typessont déclarés dans un paquetage virtuel appelé Standard, sect. 19.3). Pour le typeT_Rationnel (sect. 10.7), toutes les fonctions de la spécification du paquetageNombres_Rationnels sont des opérations primitives. La notion d’opérationprimitive est utilisée principalement en relation avec les types dérivés.

Un type dérivé (derived type) est une nouveau type possédant toutes lescaractéristiques d’un type existant appelé type parent (parent type). Un type dérivé(exemple 19.6) est donc fondamentalement différent d’un sous-type sanscontrainte (§ 6.1.1). Il hérite automatiquement une copie des valeurs et desopérations primitives de son type parent mais il est naturellement possible dedéfinir d’autres opérations sur les valeurs de ce type, voire redéfinir certainesopérations héritées. Lors de la dérivation, des contraintes peuvent s’ajouter aunouveau type de la même manière que lors de la création d’un sous-type, ou alorsen dérivant directement d’un sous-type.

Exemple 19.6 Types dérivés et contraintes.

type T_Euro is new Integer; -- Integer est le type parenttype T_Franc is new Integer range –1E12 .. 1E12;type T_Positif is new Positive;type T_Nouvelle_Date is new T_Date; -- § 7.2.1

L’intérêt de la dérivation de type réside d’une part dans la création de typesdifférents permettant d’éviter le mélange accidentel de valeurs (vérification à lacompilation, exemple 19.7) et d’autre part dans la réalisation de la déclarationcomplète d’un type privé.

Exemple 19.7 Mélange accidentel de valeurs.

Euro : T_Euro := 0;

Page 444: Cours Ada de A à Z

OPÉRATIONS PRIMITIVES ET TYPES DÉRIVÉS 444

Franc : T_Franc;Dix : constant Integer := 10;...Euro := Euro + 1; -- Possible car 1 est un entierEuro := Euro + Dix; -- INTERDIT car types differentsFranc := Euro; -- INTERDIT car types differents

Comme exemple d’implémentation d’un type privé, voici un paquetaged’attribution d’identités privées (exemple 19.8), réalisées sous forme de numérosentiers positifs.

Exemple 19.8 Paquetage dont le type privé est réalisé par un type dérivé.

-- Fournit une identite differente a chaque appel de la fonction-- Nouvelle_Identitepackage Attribution_Identites is

type T_Identite is private;function Nouvelle_Identite return T_Identite;

private

type T_Identite is new Natural;

end Attribution_Identites;

package body Attribution_Identites is

Identite_Courante : T_Identite := 0; -- Variable remanente

-------------------------------------------------------------- Fournit une identite differente a chaque appelfunction Nouvelle_Identite return T_Identite isbegin -- Nouvelle_Identite

Identite_Courante := Identite_Courante + 1;

return Identite_Courante;

end Nouvelle_Identite;------------------------------------------------------------

end Attribution_Identites;

Le parent d’une dérivation peut être lui-même un type dérivé. La répétition decette situation peut conduire à l’existence d’une ou de plusieurs hiérarchiesarborescentes de types dérivés les uns des autres dans une application. Dans unetelle hiérarchie la conversion explicite d’une valeur de l’un des types en la mêmevaleur d’un autre type de la hiérarchie est possible en utilisant le nom du type cible(exemple 19.9), comme c’était déjà le cas pour les types numériques (§ 6.2.6) oupour les tableaux (sect. 8.4).

Page 445: Cours Ada de A à Z

OPÉRATIONS PRIMITIVES ET TYPES DÉRIVÉS 445

Exemple 19.9 Mélange de valeurs par conversion explicite.

Euro : T_Euro := 0;Franc : T_Franc;Dix : constant Integer := 10;...Euro := Euro + T_Euro (Dix); -- Possible car Dix est convertiFranc := T_Franc (Euro); -- Possible car Euro est converti

Enfin, les types dérivés représentent la base de la programmation orientéeobjets en Ada, et réalisent en particulier le concept d’héritage.

Page 446: Cours Ada de A à Z

UNITÉS PRÉDÉFINIES 446

19.3 UNITÉS PRÉDÉFINIES

Toutes les unités prédéfinies font partie d’une bibliothèque composée dupaquetage Standard, parent de trois paquetages principaux nommés Ada, Systemet Interfaces. Toutes les autres unités prédéfinies sont des enfants de ces troispaquetages. Une description précise de cette bibliothèque nécessiterait plusieursdizaines de pages, raison pour laquelle seules les idées générales de son contenuvont être présentées, accompagnées des références à la norme Ada.

Le paquetage Standard [ARM A.1] déclare entre autres les types de baseInteger, Boolean, Float, Character, String, Duration, les opérations surces types et les quatre exceptions Constraint_Error, Program_Error,Storage_Error et Tasking_Error. Une clause de contexte et une clause usepour ce paquetage sont implicitement présentes avant toute unité de compilation.

Dans ce qui suit, seuls les identificateurs des unités sont utilisés pour des rai-sons de lisibilité du texte. Dans la pratique de la programmation, il est bien entenduque cette mention des seuls identificateurs est réalisable par des clauses use.

Le paquetage Ada représente la racine de la première hiérarchie. Il ne contientpas de déclaration. Son premier fils, le paquetage Calendar [ARM 9.6], offre desopérations de traitement de dates et de durées. Il est aussi possible d’obtenirl’instant courant par la fonction Clock. Ce paquetage permet donc la datation,ainsi que l’évaluation de durées de traitement particulièrement utiles lors de laréalisation de programmes dits temps réel parce qu’ils doivent respecter descontraintes temporelles.

Les caractères peuvent être manipulés par les paquetages Handling etLatin_1 [ARM A.3], le paquetage Characters, lui-même vide, jouant sim-plement le rôle de leur parent. Le paquetage Handling offre des opérations commela transformation en minuscules ou majuscules ainsi que des fonctions déterminantsi un caractère est une lettre, un chiffre, un caractère de contrôle, etc. Latin_1définit essentiellement des constantes nommant les 256 éléments du jeu decaractères LATIN-1.

Command_Line [ARM A.15] offre la possibilité à un programme Ada d’ac-céder aux arguments de la commande qui l’a invoqué et de donner une valeur aucode de retour si ce dernier est défini dans l’environnement d’exécution [BAR 97].

Le petit paquetage Decimal [ARM F.2] fournit un ensemble de constantes etune procédure générique de division, applicables aux valeurs réelles décimales nontraitées dans cet ouvrage. Il doit être disponible si l’annexe F (sect. 19.4) est offertepar l’implémentation.

Les trois paquetages Direct_IO [ARM A.8.4], Exceptions [ARM 11.4.1] etIO_Exceptions [ARM A.13] ont déjà fait l’objet d’une présentation dans lessections 12.4, respectivement 13.4 et 12.5.

Page 447: Cours Ada de A à Z

UNITÉS PRÉDÉFINIES 447

Le paquetage Numerics [ARM A.5] définit les célèbres constantes mathéma-tiques π et e. Ses enfants [ARM A.5.1] Elementary_Functions etGeneric_Elementary_Functions offrent des opérations trigonométriques debase soit sous forme générique, soit par une instanciation réalisée avec le typeFloat. Les paquetages Discrete_Random et Float_Random [ARM A.5.2]offrent des générateurs de nombres pseudo-aléatoires, Discrete_Random permetd’obtenir une valeur pseudo-aléatoire comprise dans un type discret passé enparamètre alors que pour Float_Random, la valeur est toujours comprise dansl’intervalle 0.0..1.0. Dans les deux cas, les suites de valeurs générées peuvent ounon être reproductibles. Les derniers enfants de Numerics sont définis dansl’annexe G (sect. 19.4) et concernent le traitement des nombres complexes.

Les deux paquetages Sequential_IO [ARM A.8.1] et Storage_IO [ARMA.9] ont également déjà fait l’objet d’une présentation dans les sections 12.4,respectivement 12.6. De même, le paquetage Streams_IO [ARM A.12.1] a étémentionné dans la section 12.6. Son parent, Streams [ARM 13.13.1], permet lacréation et la gestion de flots (streams), c’est-à-dire de fichiers formés d’élémentshétérogènes. De plus amples informations sont données dans [BAR 97].

Le paquetage Strings [ARM A.4.1] est le parent de tous les paquetagesenfants de traitement de chaînes de caractères. Il définit uniquement des entitésglo-bales comme des constantes caractères, des exceptions et des types. La raisonde la présence de plusieurs enfants réside dans la diversité de la gestion des chaînesde caractères. Les trois paquetages Bounded [ARM A.4.4], Fixed [ARM A.4.3]et Unbounded [ARM A.4.5] fournissent des opérations comme celles présentéespour Fixed dans la section 9.3. Le paquetage Bounded gère des chaînes ayant unelongueur maximale dont seule une partie est utilisée en permanence; Fixed estprévu pour des chaînes de taille fixe; enfin Unbounded s’occupe des chaînes delongueur quelconque, non bornée. Les paquetages Maps [ARM A.4.2] etConstants [ARM A.4.6] permettent d’effectuer des correspondances entrecaractères ou ensembles de caractères, comme par exemple entre minuscules etmajuscules, ou encore entre une lettre accentuée et son équivalent sans accent. Cescorrespondances facilitent énormément la recherche de motifs (patterns), c’est-à-dire de textes particuliers, dans des chaînes de caractères.

Le très connu paquetage Text_IO [ARM A.10.1] offre des opérationsd’entrées-sorties textuelles classiques. Il fournit directement la lecture et l’écriturede caractères de type Character ou de chaînes de type String ainsi que destraitements sur les lignes, les colonnes et les pages d’un fichier texte. Par ladéfinition de paquetages internes génériques, il permet également la création depaquetages instanciés pour la lecture et l’écriture d’entiers signés (instanciation deInteger_IO), d’entiers non signés (instanciation de Modular_IO), de nombresréels en virgule flottante (instanciation de Float_IO), en virgule fixe(instanciation de Fixed_IO) ou encore décimaux (instanciation de Decimal_IO),

Page 448: Cours Ada de A à Z

UNITÉS PRÉDÉFINIES 448

et enfin de valeurs énumérées (instanciation de Enumeration_IO). Le paquetageenfant Complex_IO [ARM G.1.3] est défini dans l’annexe G (sect. 19.4) et traitela lecture et l’écriture de nombres complexes alors que Editing [ARM F.3.3]provient, lui, de l’annexe F (sect. 19.4). Finalement, Text_Streams [ARMA.12.3] réalise des entrées-sorties sur des flots et permet le mélange de texte et debinaire.

La fonction générique Unchecked_Conversion [ARM 13.9] est un utilitairede bas niveau et convertit sans aucune vérification la suite de bits représentant unevaleur d’un type en la même suite binaire mais interprétée selon un autre type. Lesdeux types sont a priori quelconques mais l’implémentation peut imposer descontraintes raisonnables comme par exemple un nombre de bits identiques pour lesvaleurs de ces types.

La procédure générique Unchecked_Deallocation [ARM 13.11.2] a étédécrite dans la section 15.7 lors de la présentation des types accès.

Pour terminer l’examen des enfants du paquetage Ada, il reste à releverl’existence de paquetages identiques à Text_IO et ses enfants mais applicables àdes caractères de type Wide_Character occupant 16 bits. Ils sont reconnaissablesà leur nom qui est systématiquement préfixé par Wide_.

Le paquetage Interfaces [ARM B.2] constitue la racine de la deuxièmehiérarchie et définit des types entiers signés et non signés propres à l’im-plémentation utilisée ainsi que des opérations de décalage et de rotation de bitspour les types entiers non signés. Ses trois enfants permettent l’interfaçage entredes unités Ada et d’autres, écrites dans les langages C, COBOL et Fortran.

Enfin, le paquetage System [ARM 13.7] et ses enfants forment la dernièrehiérarchie et définissent des caractéristiques dépendantes de l’implémentationcomme le plus grand intervalle pour des nombres entiers, la précision maximaledes nombres réels ou encore la taille d’un mot mémoire. Leur contenu dépasselargement les objectifs de cet ouvrage.

Page 449: Cours Ada de A à Z

ANNEXES PRÉDÉFINIES SPÉCIALISÉES 449

19.4 ANNEXES PRÉDÉFINIES SPÉCIALISÉES

La norme Ada propose, en plus de la partie obligatoire (core) du langage, c’est-à-dire l’ensemble du langage que tous les compilateurs Ada doivent pouvoircompiler, six annexes dédiées à des classes d’application bien précises. Commepour la section précédente, un examen approfondi des annexes nécessiterait nonseulement plusieurs dizaines de pages d’explications mais aussi des connaissancespréliminaires particulières pour plusieurs d’entre elles. Seules les fonctionnalitésgénérales vont donc être présentées, accompagnées des références à la norme Ada.Il faut encore préciser que toutes les annexes spécialisées sont facultatives, uneimplémentation peut parfaitement n’en supporter aucune.

L’annexe Programmation des systèmes [ARM ANNEXE C] offre principalementl’accès au code machine, le traitement des interruptions, des pragmas pour l’accèsconcurrent à des variables partagées et des paquetages pour l’identification detâches.

L’annexe Systèmes temps réel [ARM ANNEXE D] précise l’utilisation depriorités, l’ordonnancement et le contrôle direct de tâches; elle fournit unpaquetage de temps monotone croissant, définit les restrictions conduisant à desapplications temps réel simplifiées et impose des conditions sur l’instantanéité deseffets d’une instruction abort.

Grâce à l’annexe Systèmes répartis [ARM ANNEXE E], il est possible dedécomposer un programme en partitions destinées à s’exécuter sur un ou plusieursprocesseurs et d’assurer la communication entre elles.

L’annexe Systèmes d’informations [ARM ANNEXE F] permet l’interfaçage desprogrammes Ada avec des programmes COBOL.

Dans l’annexe Numérique [ARM ANNEXE G], des paquetages de gestion denombres complexes sont définis, ainsi que les exigences pour la précision del’arithmétique des nombres réels en virgule flottante et en virgule fixe.

Enfin, l’annexe Fiabilité et sécurité [ARM ANNEXE H] propose plusieurspragmas visant à obtenir entre autres une meilleure sécurité à l’exécution et uneanalyse facilitée du code source.

Page 450: Cours Ada de A à Z

450

19.5 EXERCICES

19.5.1 Surnoms

Déclarer des surnoms pour les entités suivantes:

• le type T_Queue_Statique de l’exemple 16.6 (attention au piège);• la fonction Creer de l’exemple 16.7;• le paquetage enfant Nombres_Rationnels.Operations_Internes

de l’exemple 16.8;• la tranche de tableau Chaine(1..Nombre_Car_Lus) de l’exemple 9.3.

19.5.2 Opérations primitives

Enumérer le plus d’opérations primitives possible pour les types

• Boolean;• T_Jours_De_La_Semaine (§ 5.2.2);• T_Vecteur et T_Matrice (exemple 8.3);• T_Polynome (exemple 11.1);• T_Queue_Statique (exemple 14.3).

19.5.3 Types dérivés

Dériver les types ci-dessous et énumérer leurs opérations primitives:

• Boolean;• T_Polynome (exemple 11.1);• T_Queue_Statique (exemple 14.3);• T_Queue_Dynamique (exemple 16.11);• T_Rationnel (fig. 16.5)

Page 451: Cours Ada de A à Z

POINTS À RELEVER EN ADA 451

19.6 POINTS À RELEVER EN ADA

• Le surnommage peut améliorer la lisibilité du code source.

• Grâce au surnommage, il est possible de fournir un corps de sous-programme correspondant à une spécification, déclarée par exemple dansla spécification d’un paquetage.

• Les types dérivés permettent d’éviter des erreurs dues à des mélanges devaleurs.

• La définition complète d’un type privé peut être fournie par une dérivationde type.

• Les unités prédéfinies ainsi que les annexes spécialisées constituent lependant des bibliothèques prédéfinies existant dans d’autres langages deprogrammation.