• Aucun résultat trouvé

[PDF] Cours d’introduction aux Objets répartis avec Ada | Formation informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Cours d’introduction aux Objets répartis avec Ada | Formation informatique"

Copied!
80
0
0

Texte intégral

(1)

_____________________________________________________________ ____________________________________________

COURS ADA 95

Excluant POO, Task

____________________________________________

____________________________________________________________

Véronique Gaildrat

Université Paul Sabatier, IRIT

Porte : 218, téléphone : 05 61 55 74 31 Email : veronique.gaildrat@irit.fr

(2)

Table des matières

1.

INTRODUCTION...5

1.1 ORIGINES DE ADA...5

1.1.1 Historique...5

1.1.2 Objectifs du langage...5

1.2 ELÉMENTS DE BASE D'UN PROGRAMME ADA95...5

1.2.1 Eléments lexicaux, séparateurs et délimiteurs...5

1.2.2 Littéraux numériques...6 1.2.2.1 Littéraux entiers...6 1.2.2.2 Littéraux réels...6 1.2.3 Mots réservés...6 1.2.4 Identificateurs...6 1.2.5 Commentaires et Pragmas...7 1.2.6 Opérateurs et expressions...7

1.2.7 Evaluation des expressions...7

1.2.8 Instructions (sauf return et raise)...8

1.2.8.1 Instruction d'affectation...8 1.2.8.2 Instruction nulle...8 1.2.8.3 Instruction bloc...8 1.2.8.4 Instructions composées...8 1.2.9 Sous-programmes...10 1.2.10 Instruction return...11 1.2.11 Paquetages...11

1.2.12 Variable et Type d'une variable...12

1.2.13 Caractères et chaînes littérales...13

1.2.13.1 Caractères littéraux...13

1.2.13.2 Chaînes littérales...13

1.2.14 Paquetages de manipulation des caractères et des chaînes...13

1.2.15 Exemple d'un programme simple en ADA...13

2.

LES TYPES...14

2.1 NOTION DE TYPE...14

2.1.1 Définition...14

2.1.2 Règles...14

2.1.3 Classification de types ADA...14

2.1.4 Les modèles de types...15

2.1.5 Déclaration de types...15

2.1.6 Contraintes...15

2.2 SOUS-TYPES ET TYPES DÉRIVÉS...16

2.2.1 Sous-types...16

2.2.2 Types dérivés...16

2.3 TYPES SCALAIRES...19

2.3.1 Attributs communs à tous les types scalaires...19

2.3.2 Les types discrets...19

2.3.2.1 Attributs spécifiques aux types discrets...19

2.3.2.2 Les types entiers...20

(3)

2.3.2.4 Les types énumérés...21

2.3.3 Les types réels...23

2.3.4 Les types décimaux...24

2.4 TYPES COMPOSÉS PRINCIPAUX (TABLEAUX ET ARTICLES)...25

2.4.1 Types tableaux...25

2.4.1.1 Variables tableaux...25

2.4.1.2 Types tableaux non contraints...25

2.4.1.3 Contraintes d'indices...26

2.4.1.4 Agrégats...26

2.4.1.5 Attributs de tableaux...26

2.4.1.6 Tableaux non contraints et Passage de paramètres...27

2.4.1.7 Types tableaux contraints...27

2.4.1.8 Sous-types de tableaux...28

2.4.1.9 Opérations sur un type tableau...28

2.4.1.10 Caractères et Chaînes de Caractères...29

2.4.1.11 Conversions explicites entre types tableaux...30

2.4.2 Types articles...31

2.4.2.1 Exemple...32

2.4.2.2 Ensemble des opérations...32

2.4.2.3 Sélection d'un composant : notation pointée...32

2.4.2.4 Agrégats...32

2.4.2.5 Valeur par défaut d'un composant...32

2.4.2.6 Articles à partie discriminante...32

2.4.2.7 Articles à variantes...33

2.4.2.8 Sous-types d'articles...33

2.4.2.9 Défauts et contraintes...34

2.4.2.10 Types dérivés d'articles à discriminant...35

2.4.3 Composition de types...35

2.5 LES TYPES ACCESS...36

2.5.1 Type access spécifiques à un pool...36

2.5.2 Quand utiliser les types access...37

2.5.3 Comment créer une structure arborescente...38

2.5.4 Utilisation "historique" de l'arborescence...38

2.5.5 Contraintes sur les types access...38

2.5.6 Types access généralisés...40

2.5.7 Paramètres access de sous-programmes...41

2.5.8 Accès à un sous-programme...41

3.

UNITÉS DE BIBLIOTHÈQUE...43

3.1 SOUS-PROGRAMMES...43

3.1.1 Modes de passage de paramètres...43

3.1.2 Paramètres nommés et paramètres par défauts...44

3.1.3 Sous-programmes de type procédure...45

3.1.4 Sous-programmes de type fonction...45

3.1.5 Notion de surcharge...46

3.1.6 Applications des sous-programmes ADA...47

3.1.6.1 Programmes principaux...47

(4)

3.1.6.3 Opération primitives sur des Types de Données Abstraits...48

3.2 PAQUETAGES (PACKAGE ADA)...49

3.2.1 Définition et généralités...49

3.2.1.1 Définition...49

3.2.1.2 Spécification et corps de package...49

3.2.2 Types classiques de packages...50

3.2.2.1 Collection de déclarations communes...50

3.2.2.2 Exemple de bibliothèque de sous-programmes...50

3.2.2.3 Exemple de définition de package exportant un nouveau type ( !! /= TDA)...51

3.2.2.4 Exemple de définition de machine abstraite...51

3.2.3 Clients de packages...52

3.2.4 Résumé...54

3.2.5 Types privés...54

3.2.6 Types limités...55

3.2.7 Types limites privés...56

3.3 SURNOMMAGE...57 3.4 COMPILATION SÉPARÉE...58 3.4.1 Dépendances...58 3.4.2 Sous-unités...59 3.4.3 Bibliothèque hiérarchique...59 3.4.3.1 Règles de visibilité...59 3.4.3.2 Exemple...60 3.4.4 Impact méthodologique...62

4.

EXCEPTIONS...63

4.1 DÉCLARATION D'UNE EXCEPTION...63

4.1.1 Exceptions prédéfinies...63

4.2 RÉCUPÉRATION ET TRAITEMENT D'UNE EXCEPTION...64

4.3 INSTRUCTION RAISE...64

4.4 OCCURRENCES D'EXCEPTIONS...66

4.4.1 Récupération...66

4.4.2 Levée d'une occurrence d'exception...67

4.5 PACKAGE ADA.EXCEPTIONS...67

5.

LA GÉNÉRICITÉ...68

5.1 SYNTAXE...69

5.2 PARAMÈTRE GÉNÉRIQUE "VARIABLE"...70

5.2.1 Passage noté : in...70

5.2.2 passage noté : in out...70

5.3 PARAMÈTRE GÉNÉRIQUE "TYPE"...70

5.4 QUELQUES TYPES FORMELS GÉNÉRIQUES...71

5.5 EXEMPLE :ADA.TEXT_IO...72

5.6 PARAMÈTRES GÉNÉRIQUES "SOUS-PROGRAMMES"...73

5.7 PARAMÈTRE GÉNÉRIQUE "PAQUETAGE"...73

5.8 UNE APPLICATION DES UNITÉS GÉNÉRIQUES...74

5.9 UNITÉS DE BIBLIOTHÈQUE HIÉRARCHIQUES ET GÉNÉRIQUES...75

(5)

7.

ANNEXE : L'ENVIRONNEMENT DE PROGRAMMATION...77

7.1 PROGRAMME SOURCE...77

7.2 COMPILATION ET ÉDITION DE LIENS...77

7.2.1 Unité de Bibliothèque et programme objet...77

7.2.2 Compilation dans le cas où le programme est composé de plusieurs unités...78

7.3 ORGANISATION DES FICHIERS...78

7.3.1 Répertoires...79

7.3.2 Utilisation d'un Makefile...79

(6)

1.

Introduction

1.1Origines de ADA

1.1.1Historique

DOD : Budget logiciel du DOD 3 milliards de dollars pour supporter 400 langages et dialectes ... =>

nécessité d'améliorer les outils de programmation et la méthodologie.

En janvier 1975, le Ministère Américain de la Défense (DOD) a convoqué un groupe d'experts, le High

Order Language Working Group (HOLWG), pour trouver une solution problèmes de qualité et de

coût des logiciels militaires. Les plus gros frais de maintenance, était pour les systèmes temps-réels embarqués.

• Objectif : langage de haut niveau permettant le développement de très grosses applications industrielles, ainsi que d'applications critiques (avionique, signalisation des chemins de fer, contrôle de processus et d'applications médicales…).

• Au printemps de 1977, une équipe française, dirigée par Jean Ichbiah, remporta l'appel d'offre. Le langage fut alors baptisé Ada, du nom d'Augusta Ada Byron, Comtesse Lovelace (1815-1852), le premier programmeur sur la "machine analytique" de Babbage.

Après quelques modifications de moindre importance, le langage fut standardisé par l'ANSI1 en 1983. Il fallut attendre 1987 pour obtenir une norme française satisfaisante et la standardisation isO2.

• En 1988 le processus de révision de la norme a été relancé par, encore une fois le ministère américain de la Défense (DoD), et la révision a été conduite par l'Ada 9X Project Office, dirigé par Chris Anderson pour aboutir à l'approbation simultanée par l'ANSI et l'ISO au tout début de 1995.

1.1.2Objectifs du langage

Ada a été conçu pour répondre à un cahier des charges précis, dont l'idée directrice était de diminuer le coût des logiciels, en tenant compte de tous les aspects du cycle de vie :

Privilégier la maintenance par rapport à la facilité d'écriture: le coût de codage représente environ 6%

de l'effort de développement d'un logiciel; la maintenance représente plus de 60%.

Typage fort : Plus une erreur est diagnostiquée tôt, moins elle est coûteuse à corriger. Le langage fournit

des outils permettant de diagnostiquer beaucoup d'erreurs de cohérence dès la compilation.

Offrir un support à une industrie du composant logiciel: en fournissant des interfaces standard et des

garanties de portabilité des applications indépendamment des machine.

Permettre une programmation intrinsèquement sûre: ceci signifie qu'un programme doit être capable de

traiter toutes les situations anormales, y compris celles résultant d'erreurs du programme

(auto-vérifications, fonctionnement en mode dégradé).

Permettre des implémentations efficaces et des accès de bas niveau: exigence indispensable à la

réalisation notamment de systèmes "temps réel".

Ada dispose d'outils d'aide à la conception et à la mise en œuvre d'applications en permettant la

programmation modulaire: paquetages, compilation séparée, généricité, exceptions.

1.2Eléments de base d'un programme ADA 95

1.2.1Eléments lexicaux, séparateurs et délimiteurs

Délimiteurs simples : & ' ( ) * + , - . / : ; < = > | Délimiteurs composés : => .. ** := /= >= <= << >> <>

Séparateurs : espace tabulation fin de ligne

Caractères autres :

• # " _

• a..z A..Z

• 0..9

1American National Standard Institute

(7)

1.2.2Littéraux numériques

1.2.2.1Littéraux entiers 1900 = 19E2 = 190e1 1_000_000 2#100110010# = 256+32+16+2 = 306 16#1FAB# = 11 + 10*16 + 15*16*16 + 1*16*16*16 = 8107 1.2.2.2Littéraux réels

98.4 = 98.4e0 = 0.984e2 = 984.0e+2

984e-2 est illégal !

1.2.3Mots réservés

Attention à ne pas déclarer des identificateurs égaux à un de ces termes :

abort else new return

abs elsif not reverse

abstract end null

accept entry select

access exception separate aliased exit of subtype all or

and for others tagged array function out task

at terminate

generic package then begin goto pragma type body private

if procedure

case in protected until constant is use raise

declare range when

delay limited record while delta loop rem with digits renames

do mod requeue xor

1.2.4Identificateurs

Remarque : Règles d'écritures pour les identificateurs

• Seuls les noms de types commencent par une majuscule

• Les noms de variables et de sous programmes commencent toujours par une minuscule

• Un paquetage porte le nom du type qu'il contient pré ou postfixé par _p

Contrairement au C, Ada n'est pas "case sensitive" : pour les identificateurs, majuscules et minuscules sont identiques.

L'identificateur obéit à des règles de construction usuelles : il est composé de caractères alphabétiques, de chiffres et du caractère "_". La vérification est effectuée à la compilation.

Exemples d'identificateurs corrects :

CENTIMES = centimes = Centimes /= cent_imes

COMPTER_LA_MONNAIE TABLE_128_A

Ada UMO164g

Exemples d'identificateurs incorrects :

fich&chips "&"

RATE-of-FLOW "-"

(8)

77E2 "7"___

X_ "_ "

tax rate " " => deux identificateurs

with mot réservé

1.2.5Commentaires et Pragmas

Les commentaires sont indiqués par les caractères : ---- ceci est un commentaire

-- un commentaire commence aux doubles tirets et se poursuit jusqu'à la fin de la ligne -- les commentaires sont destinés aux lecteurs

-- les pragmas sont destinés au compilateur

Il peut être nécessaire de donner des avis au compilateur. Ces remarques ne font pas partie du programme, mais sont faites à partir de constructions appelées pragma.

Un certain nombre de pragmas sont prédéfinis : Exemples de pragma :

pragma optimize (identificateur); -- identificateur = time, space ou off,

pragma pack (String); -- incite le compilateur à compresser les données de ce type

1.2.6Opérateurs et expressions

Les opérateurs sont classés selon six niveaux de précédence : Du plus faible :

opérateurs logiques : and or xor

opérateurs de relation : = /= < >= > >= in

not in

opérateurs additifs : + - &

opérateurs unaires : + -

opérateurs multiplicatifs : * / mod rem

Au plus fort : ** not abs

1.2.7Evaluation des expressions

On applique les opérateurs dont le niveau de précédence est le plus élevé. Les parenthèses imposent un ordre déterminé.

• Cas général : l'ordre d'évaluation des deux opérandes d'un opérateur n'est pas défini. Tous les opérandes d'une expression sont évalués.

• Il existe deux formes dévaluation (and then et or else) où le premier opérande est évalué et le second ne le sera que si c'est nécessaire pour obtenir le résultat du test.

Exemple : ensoleillé and then chaud

où chaud ne sera évalué que si ensoleille est vrai ensoleillé or else chaud

où chaud ne sera évalué que si ensoleillé est faux p.genre = f and then p.enfants >= 2

où le nombre d'enfants n'est testé que pour une personne de genre féminin p.genre = m or else p.enfants = 0

où le nombre d'enfants n'est testé que si la personne n'est pas de genre masculin

1.2.8Instructions (sauf return et raise)

1.2.8.1Instruction d'affectation

variable := expression; -- où type de la variable = type de l'expression

(9)

null;

Cette instruction qui semble ne faire absolument rien est obligatoire dans chaque bloc vide d'instruction.

1.2.8.3Instruction bloc

Une instruction bloc permet d'englober textuellement une séquence d'instructions accompagnée de déclarations locales et d'un traitement d'exception si on le désire. Un bloc peut être nommé ou non :

declare échange:

-- déclaration de variables ou de types locaux declare

-- au bloc temp : Float;

begin begin

-- instructions manipulant temp := v1;

-- les variables locales et v1 := v2;

-- les variables globales accessibles v2 := temp;

[exception end échange;

traite exception] end; 1.2.8.4Instructions composées 1. Instruction if : if condition_1 then

elsif condition_2 then

elsif condition_n then

else

end if;

Exemples d'utilisation d'instruction if : if discriminant > 0 then

-- 2 racines réelles ...

elsif discriminant < 0 then -- 2 racines imaginaires ...

else -- discriminant = 0

-- une racine réelle double ...

end if;

On peut avoir 0, 1, ou plusieurs parties elsif. On peut avoir 0 ou 1 partie else.

2. Instruction case

case expression discrète is -- les types discrets seront détaillés ultérieurement

when choix_1 => -- ils sont constitués des types entiers et énumérés ... when choix_2 =>when choix_n =>when others =>end case;

Les choix doivent couvrir toutes les valeurs du type ou du sous-type de l'expression. Les expressions doivent être statiques pour pouvoir être évaluées à la compilation.

(10)

l'ensemble des valeurs dans les choix. Exemple d'utilisation d'instruction case :

type jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); aujourdHui : Jour;

case aujourdHui is

when lundi | mardi | mercredi | jeudi => -- ou lundi .. jeudi

travail;

when vendredi =>

travail; sortie;

when samedi | dimanche =>

null; -- instruction null dans le cas d'un choix où rien n'est à faire end case;

Il est possible d'avoir comme expression discrète un appel de fonction qui retourne une valeur discrète :

with Ada.Text_io, Ada.Integer_Text_io; -- la clause with permet l'import d'autres unités

procedure essai is i : Integer;

function carre (i : Integer) return Integer is begin return i*i; end; begin Ada.Text_io.put("entrer un entier : "); Ada. Integer_Text_io.get(i);

ada;text_io;put("le carré est : "); case carre(i) is

when 0..1 => Ada.Integer_Text_io.put(i);

when others => Ada.Integer_Text_io.put(carre(i));

end case; end essai;

3. Instructions de boucle

Une boucle simple se présente sous la forme :

loop

...

end loop;

L'instruction exit permet la sortie de boucles simples, imbriquées ou non. recherche:

loop

exit -- sortie de la boucle recherche ...

loop

exit -- sortie de la boucle interne ...

exit recherche when trouve; -- sortie conditionnelle de la boucle

recherche

...

exit when temp > max; -- sortie conditionnelle de la boucle interne

...

exit recherche; -- sortie inconditionnelle de la boucle recherche

end loop;

...

(11)

D'autres instructions de boucle permettent d'effectuer un traitement : - tant qu'une condition est vraie,

- pour un nombre fini de fois.

Ces deux instructions sont les boucles while et for.

while encoreAFaire loop

-- faire le traitement ...

...

-- faire évoluer la condition encoreAFaire ...

end loop;

for i in Integer range 1 .. 10 loop

-- le traitement sera effectué 10 fois -- pour des valeurs de i allant de 1 à 10 -- i constante dans la boucle

...

end loop;

for i in reverse Integer range 1 .. 10 loop

-- le traitement sera effectué 10 fois -- pour des valeurs de i allant de 10 à 1 ...

end loop;

for suivant in table'range loop -- suivant est constant dans la boucle if table (suivant) = ' ' then

... end if; ...

end loop;

1.2.9Sous-programmes

Les sous-programmes (procédures et fonctions) sont constitués d'une en-tête, suivie de déclarations, puis d'instructions.

procedure identificateur [(p1 : in TypeP1; p2 : out TypeP2; p3 : in out TypeP3)] is

déclarations

begin

instructions -- chaque instruction se termine par un ";" [exception

traite-exceptions]

end identificateur;

function identificateur[(p1 : in TypeP1; p2 : [in] T ypeP2)]return TypeRetourné is

déclarations

begin

instructions -- chaque instruction se termine par un ";" [exception

traite-exceptions]

end identificateur;

Le mode de passage de paramètres fait référence à la manière dont on y accède, et non à la méthode physique de passage: les paramètres peuvent ainsi être déclarés in (lecture seulement), out (écriture seulement), ou in out (lecture et écriture).

(12)

1.2.10Instruction return

L'instruction return termine l'exécution d'une procédure ou d'une fonction. Toute fonction doit comprendre au moins un return et peut en contenir plusieurs. Dans le cas d'une procédure, l'instruction prend la forme suivante :

return; -- et stoppe l'exécution de la procédure

Pour une fonction, l'utilisation du return permet en plus de retourner une valeur du type de la fonction. Exemple :

function impaire (val : Integer) return Boolean is begin

if (val mod 2 ) = 0 then return false; else

return true; end if;

end impaire;

Si l'expression retournée n'est pas du sous-type demandé, une erreur CONSTRAINT_ERROR sera levée. Si une fonction se termine sans avoir atteint de return ==> PROGRAM_ERROR.

La fonction impaire pourra être écrite de la façon suivante : function impaire (val : Integer) return Boolean is begin

return ((val mod 2) /= 0); end impaire;

Il est préférable de n'avoir qu'un return par fonction. Mais il est parfois nécessaire, pour des conditions de lisibilité d'en avoir plusieurs.

1.2.11Paquetages

Un Type de Données Abstrait est mis en œuvre en Ada grâce à un paquetage (package) comprenant les deux parties suivantes :

• la spécification du package qui contient les types, données et signatures des opérations (sous-programmes) visibles depuis l'extérieur,

• le corps du package (body) qui contient le code des opérations (sous-programmes) déclarées dans la

partie spécification du package et le code des opérations (sous-programmes) non visibles depuis l'extérieur.

Un paquetage, une fois compilé, pourra être référencé et utilisé par un programme appelé : client. Exemple :

package Point2D_p is

-- le package exporte le type Point2D

-- ATTENTION, déclaré ainsi le type Point2D n'est pas protégé

type Point2D is array (1..2) of Float; -- tableau de deux flottants

-- Le Point2D est créé en (0,0)

procedure creerPoint (p : out Point2D); -- Le Point2D est translaté de (x,y)

procedure translater (p : in out Point2D; x, y : Float); -- retourne une chaîne ce caractères représentative du Point2D function toString(p : Point2D) return String;

end Point2D_p;

package body Point2D_p is -- Le Point2D est créé en (0,0)

procedure creerPoint (p : out Point2D) is begin

p(p'first) := 0.0; -- utilisation d'attributs

p(p'last) := 0.0; end creerPoint;

-- Le Point2D est translaté de (x,y)

(13)

begin

p(p'first) := p(p'first) + x; p(p'last) := p(p'last) + y; end translater;

-- retourne une chaîne ce caractères représentative du Point2D function toString(p : Point2D) return String is

begin

return Float'image(p(p'first)) & " , " & Float'image(p(p'last)); end toString;

end Point2D_p; Client :

with Ada.Text_io, Point2D_p; -- la clause with permet l'import d'autres unités procedure essai is p1, p2 : Point2D_p.Point2D; begin Point2D_p.creerPoint(p1); Point2D_p.creerPoint(p2); Point2D_p.translater(p1, 2.0, 3.0);

Ada.Text_io.put_line("p1 = " & Point2D_p.toString(p1)); end essai;

Résultat de l'exécution :

p1 = 2.00000E+00 , -3.00000E+00

1.2.12Variable et Type d'une variable

Chaque variable est constituée d'un quadruplet :

• son nom,

• son Type, -- obligatoire et inchangé tout au long de l'exécution

• sa valeur,

• l'espace mémoire où est stockée sa valeur.

Le typage est strict, ainsi la valeur affectée à une variable doit être strictement du type de la variable. Tout manquement déclenchera une erreur (appelée exception).

Déclaration d'une variable d'un type simple :

nomVariable : nomType [:= valeurInitiale]; Exemple :

année : Integer := 2001; -- année est une variable entière de valeur 2001.

Le type peut être un type prédéfini ou construit par l'utilisateur. Parmi les types simples prédéfinis on trouvera : • Integer, • Float, • Character, • String, • Boolean.

1.2.13Caractères et chaînes littérales

1.2.13.1Caractères littéraux

Il y a deux jeux de caractères en Ada 95:

• Character, basé sur la norme isO 8 bits 8859-1communément appelée Latin-1

• Wide_Character, basé sur la norme isO 10646 et dont les 256 premières positions sont celles de Character.

Ces deux types sont définis dans le paquetage Standard de Ada.

Exemple de caractères : 'A' la lettre A

' ' espace

1.2.13.2Chaînes littérales

(14)

String (tableau de Character) et Wide_String (tableau de Wide_Character) Exemple de chaînes de caractères

"ceci est un message"

"première partie d'une chaîne" & "qui continue à la ligne suivante" "" chaîne vide

1.2.14Paquetages de manipulation des caractères et des chaînes

Ada.Characters qui est en fait vide mais est la racine de l'arborescence

Ada.Characters.Handling méthodes de conversion de caractères et de chaînes

function To_Lower (Item : in Character) return Character; function To_Upper (Item : in Character) return Character;

function To_Lower (Item : in String) return String;

function To_Upper (Item : in String) return String;

Ada.Characters.Latin_1 consiste en la déclaration de constantes donnant les noms de

la plupart des caractères. Exemple : Ada.Characters.Latin_1.Exclamation

De nombreux paquetages fournissent des fonctionnalités pour la manipulation de chaînes. Les deux principaux sont :

Ada.Strings.Fixed pour des String de taille fixe.

Ada.Strings.Unbounded pour des String non bornées (en fait seulement par

integer'last) exporte le type Unbounded_String

1.2.15Exemple d'un programme simple en ADA

with Ada.Text_io, Ada.Float_Text_io, Ada.numerics; -- import de paquetages préexistants

dans Ada

procedure essai is x, y : Float; i, j, k : Integer; begin

Ada.Text_io.put (Item => "Hello ");

Ada.Text_io.put (Item => "We hope you enjoy studying Ada!"); Ada.Text_io.new_Line;

-- affectation de valeurs flottantes x := 3.14;

x := Ada.numerics.pi;

Ada.Float_Text_io.put(x);

y := 2.0 * x;

-- affectation de valeurs entières i := 3; j := 4; k := (i+j) * 3; end essai;

2.

Les types

2.1Notion de type

2.1.1Définition

• A un type T correspond : - un nom,

- un ensemble fini de valeurs,

- un ensemble d'opérations primitives agissant sur les valeurs.

• Les opérations peuvent être :

(15)

- attributs,

- sous-programmes (ayant un paramètre ou un type de retour égal au type T) à condition que T soit déclaré dans un paquetage et que les sous-programmes soient déclarés dans la spécification du même paquetage.

• A chaque instruction, le compilateur vérifie si l'opération, effectuée sur une variable d'un type donné, est permise. Dans le cas contraire, il n'y aura pas compilation du programme.

• Les types prédéfinis (tels que Integer, Float, Character, String) sont déclarés dans le paquetage Standard dont l'importation est automatique (pas de clause with).

2.1.2Règles

• Chaque entité doit être déclarée : une entité est, soit une variable, soit une constante. • La déclaration de chaque entité spécifie son type.

• Toute opération sur une entité doit en préserver le type => le type d'une entité est invariant durant l'exécution. C'est le type spécifié à la déclaration.

2.1.3Classification de types ADA

Les types sont classés hiérarchiquement selon leurs propriétés :

les types access => pointeurs

scalaires => un élément <--> une valeur

composite => un élément <--> une ou plusieurs valeurs

Tous Types

|

+---+---+

| |

Types Elémentaires Types Composites

| |

+---+--+ +---+---+---+ | | | | | | access scalaires array record protected task | +---+---+ | | | +====================================+ discrets | real | | | | | +---+ +---+---+ | | | | | | | Numeric enumeration | integer float fixed | Types | | | |

| +--+----+ +----+--+ | | | | | | | |signed modular decimal ordinary | +====================================+

Hiérarchie des types Ada

2.1.4Les modèles de types

Il y a plusieurs manières de construire un type :

- à partir d'une spécification qui définit un modèle de type,

- à partir d'un type déjà existant au moyen d'une opération dite de dérivation. - On peut également construire un sous-type d'un type déjà existant.

2.1.5Déclaration de types

(16)

type NomType is {spécification d'un modèle}; Exemple :

type Couleur is (rouge, vert, bleu);

Exemple de type énumératif de nom Couleur ayant comme ensemble de valeurs les littéraux : rouge, vert et bleu.

type MyInt is range -100 .. 100;

range -100 .. 100 définit l'ensemble des entiers pour le type MyInt. Les opérations applicables sont identiques à celles du type Integer.

Le mot clé range définit par défaut les entiers.

digit définit les réels en virgule flottante.

delta définit les réels en virgule fixe.

array définit les tableaux.

...

2.1.6Contraintes

Une contrainte restreint l'ensemble des valeurs d'un type, mais ne change pas l'ensemble des opérations applicables.

Contraintes d'intervalles :

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche);

début : Jour range lundi .. jeudi; -- range définit également les contraintes d'intervalle fin : Jour range jeudi .. vendredi;

code : Character range 'a' .. 'f';

==> range borneInférieure .. borneSupérieure

Début est de type jour mais ne peut prendre toutes les valeurs définies dans l'ensemble des valeurs de jour. Début est considéré comme étant d'un sous-type non explicité du type jour.

2.2Sous-types et types dérivés

ATTENTION : Il y a compatibilité entre un sous-type et le type origine : on peut affecter une variable du sous-type avec une variable du type origine et réciproquement (à condition de respecter l'ensemble de valeurs). Par contre, il y a incompatibilité entre un type dérivé et le type origine !!!

Aussi, créer des types uniquement quand c'est sémantiquement justifié.

2.2.1Sous-types

Un sous-type caractérise un ensemble de valeurs provenant d'un type donné. Le sous-ensemble est défini à partir d'une contrainte qui prennent des formes différentes selon la nature du type.

Le sous-type possède toutes les opérations primitives du type dont il est issu.

En Ada 95 un type est considéré comme un sous-type sans contrainte. Il n'y a donc que des sous-types ! Exemple :

subtype JourDeMois is Integer range 1..31; Une variable déclarée :

jdm : JourDeMois;

pourra prendre des valeurs entre 1 et 31; La déclaration et l'affectation suivante est autorisée à condition que la valeur contenue dans i soit bien dans la contrainte de JourDeMois.

i : Integer; …

jdm := i; -- peut lever une exception de type CONSTRAINT_ERROR

Autre exemple :

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); Il n'est pas toujours nécessaire d'introduire un sous-type pour imposer une contrainte :

(17)

k :Jour range lundi .. vendredi; -- mais si cette contrainte doit être

l :Jour range lundi .. vendredi; -- utilisée plusieurs fois :

Il vaut mieux faire :

subtype JourOuvrable is Jour range lundi .. vendredi; j, k, l : JourOuvrable;

Un sous-type n'est pas un nouveau type : maintenant : Jour;

terme : JourOuvrable; -- maintenant et terme sont compatibles

maintenant := terme; -- est une assignation légale.

Exemples construits à partir de caractères :

subtype Minuscules is Character range 'a'..'z'; -- sous type

2.2.2Types dérivés

But : A partir d'un type explicitement défini, l'utilisateur peut définir un nouveau type par dérivation.

Exemple : type B is new A;

| |

| type parent

type dérivé Le type dérivé B a :

. les mêmes opérations primitives, . le même ensemble de valeurs, . les mêmes notations,

. les mêmes attributs (au sens Ada) que A. Mais A et B sont de type différent. Exemple :

procedure essai is

TAUX_ECHANGE : constant Integer := 7; TAUX_ECHANGE_WE : constant Integer := 8;

-- Ici, les types dérivés se montrent utiles.

-- En effet, on ne peut mettre des francs dans des dollars sans une opération de change !

type Franc is new Integer range 0 .. Integer'last; -- types dérivés

type Dollar is new Integer range 0 .. Integer'last;

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); subtype JourOuvrable is Jour range lundi .. vendredi;

subtype WeekEnd is Jour range samedi .. dimanche; -- sous-types

f1, f2, f3 : Franc := 0; d1, d2, d3 : Dollar := 0; jr : Jour; congé : WeekEnd; i : Integer; begin f2 := 100; f1 := f2 + f3; jr := samedi;

if jr in lundi .. vendredi then

d1 := Dollar (f1) * Dollar(TAUX_ECHANGE); else congé := WeekEnd (jr); d1 := Dollar (f1) * Dollar(TAUX_ECHANGE_WE); end if; end essai;

(18)

Attention : Les types dérivés ne sont pas compatibles avec le type origine : l'instruction i := f2; est illégale. Les types dérivés montreront toute leur utilité quand on voudra introduire des notions d'encapsulation et d'abstraction de données avec les types privés et les packages.

Exemple :

type B is (rouge, vert, bleu, cyan, magenta, jaune); type B is new A;

x : A; y : B;

function f (x : A) return A is ... end f; function f (x : B) return B is ... end f; begin

x := A'first;

--x := rouge;

--x := f(--x); -- aucun problème car résolution des homonymes

x := f(rouge); -- x donne le type de la fonction

y := f(rouge);

---- par contre :

x := y; -- illégal car x et y de type différent

x := A(y);

y := B(x); -- => conversion explicite

x := A(f(y)); end;

Des ambiguïtés peuvent se produire et obligent à qualifier :

if f(rouge) = rouge then -- type A ou B ?

=> if f(rouge) = B'(rouge)

ou if f(B'(rouge)) = rouge

ou if B'(f(rouge)) = rouge

ATTENTION :

A(f(...)) convertit le résultat dans le type A

A'(f(...)) le résultat est déjà de type A

Autre exemple :

On dispose d'un type numérique quelconque : type Scalaire is ...;

D'où sont dérivés d'autres types :

type Longueur is new Scalaire; type Surface is new Scalaire; x, y : Longueur; s : Surface; On peut écrire : x := 1.5; y := x + 10.0; Mais :

s := x; est illégal (on ne peut affecter des longueurs à des surfaces)

s := s + x; idem

On écrit les fonctions suivantes :

function sommeLongueurs (x, y : Longueur) return Longueur; function sommeSurfaces (x, y : Surface) return Surface; On peut définir :

function produitLongueurs (x, y : Longueur) return Longueur; qui est peu utile bien que syntaxiquement correct.

Il vaut mieux définir :

(19)

begin

return (Surface (Scalaire(x) * Scalaire (y))); end "*";

Ainsi on peut écrire :

s := produitLongueurs(x, y); On peut également obtenir des sous-types dérivés :

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); type JourOuvrable is new Jour range lundi .. vendredi;

Le nouveau type hérite des propriétés de jour mais toutes les valeurs sont contraintes dans l'intervalle lundi .. vendredi. Exemple : j : Jour; k : JourOuvrable; alors j := mercredi; k := mercredi; -- correct if j < samedi then ... if k < samedi then... mais k := j; -- illégal

k := dimanche; -- erreur d'intervalle

k := JourOuvrable (j); -- peut lever CONSTRAINT_ERROR mais est autorisé

2.3Types scalaires

2.3.1Attributs communs à tous les types scalaires

Les attributs suivants sont définis pour tous les types scalaires :

• S'first dénote la borne inférieure de l'ensemble de valeurs du type S. La valeur retournée est du type de S (c'est toujours le cas sauf si un autre type de retour est précisé).

• S'last dénote la borne supérieure de l'ensemble de valeurs du type S. • S'range est équivalent à S'first .. S'last.

• S'base dénote le type non contraint (de base) parent du type S. Utile pour des calculs qui peuvent momentanément dépasser la contrainte du type S.

• S'min (a, b : S) return S, dénote une fonction qui retourne le minimum de a et b. • S'max (a, b : S) return S, dénote une fonction qui retourne le maximum de a et de b.

• S'succ (a : S) return S renvoie la valeur qui succède à a dans l'ensemble des valeurs. CONSTRAINT_ERROR peut arriver si on essaye de faire S'succ(S'last). Pour un type entier, S'succ(a) retourne a+1. Pour une type à virgule fixe retourne a+ delta. Pour un type à virgule flottante, retourne le nombre machine représentable suivant immédiatement a.

• S'pred (a : S) return S retourne le prédécesseur immédiat à a en suivant les mêmes règles que S'succ.

• S'image (a : S) return String, retourne la représentation sous forme de chaîne de caractère de a. Si a est un nombre, la chaîne commencera par le signe.

• S'width retourne la longueur maximale que peut prendre la chaîne de caractères retournée par S'image.

• S'wide_image (a : S) return Wide_string, idem que S'image mais retournant un Wide_string

• S'wide_width retourne la longueur maximale que peut prendre la chaîne de caractères retournée

par S'wide_image.

• S'value (im : String) return S renvoie une valeur dans le type S dont l'image est im. S'value(S'image(a)) = a.

• S'wide_value (s : Wide_String) return S, idem que S'value mais avec en entrée un Wide_string.

S'wide_value(S'wide_image(a)) = a

2.3.2Les types discrets

(20)

Un type discret est un type scalaire. Donc, à chaque élément correspond une seule valeur.

Un type discret est caractérisé par le fait que chaque valeur de l'ensemble a une position. On pourra donc repérer la position d'une valeur, la valeur d'une position, la valeur précédente, suivante...

Exemple :

type MyInt is range -100 .. 100; L'ensemble des valeurs est :

-100, -99, -98, -97, ..., 97, 98, 99, 100

Les positions respectives des valeurs sont :

0 1 2 3 197 198 199 200

La position de la valeur 0 est 100, et la valeur en position 4 est -96.

2.3.2.1Attributs spécifiques aux types discrets

• S'pos (a : S) return Integer, retourne la position de la valeur dans l'ensemble de valeurs de S.

• S'val (I : Integer) return S, retourne la valeur de l'ensemble de valeurs de S qui se trouve à la position donnée par i.

Les types discrets comprennent deux sous-ensembles : les types énumérés et les types entiers, partagés entre • entiers signés Signed qui sont les Integer standards et

• entiers non signés appelés Modular.

2.3.2.2Les types entiers

Spécification : range début .. fin

début et fin peuvent être des expressions statiques évaluées à l'exécution. L'ensemble des valeurs autorisées est :

type Integer is range Integer'first .. Integer'last;

'first et 'last sont des attibuts associés au type. Leur valeur dépend de l'implémentation. Sur une machine 16 bits : Integer'first = -32768

Integer'last = +32767

Ces valeurs seront différentes avec une autre implémentation : utiliser les attributs et les contraintes pour assurer la portabilité !

Ensemble des opérations (sans opérations "mixtes") :

l'affectation :=

op relationnelles = /= < <= > >=

op binaires + - * / mod (signe de la 2eme op) rem (signe de la 1ere op) **

op unaires + - abs

Il ne faut pas confondre rem et mod :

rem : remainder est le reste de la division entière

a rem b a le signe de a et satisfait : abs (a rem b) < abs (b)

mod : modulo

a mod b a le signe de b et satisfait : abs (a mod b) < abs (b)

Pour effectuer une addition entre Float et Integer il faudra convertir explicitement l'une des deux opérandes. La conversion de Float vers Integer arrondit :

• 1.4  1

• 1.6  2

Dans le paquetage standard, on trouve des sous-types prédéfinis tels : subtype Natural is Integer range 0 .. Integer'last; subtype Positive is Integer range 1 .. Integer'last; Natural'first = 0;

Positive'first = 1; Exemple d'utilisation :

with Ada.Text_io, Ada.Integer_Text_io; procedure essai is

(21)

i1, j1, k1 : Int1; i, j, k : Integer := 3; begin i1 := 2_000; j1 := 10; k1 := i1 + j1; if k1 in 10 .. Int1'last then k1 := 0; end if;

if k1 not in 0 .. Int1'last then k1 := -1; end if; k := Integer(k1); k1 := int1(k); k1 := i1*2 - j1**3; k1 := k1 mod 2; if i1 = k1 then -- /= < <= >= > k1 := j1; end if;

Ada.Text_io.put("entrer une valeur : "); Ada.Integer_Text_io.get (j);

Ada.Text_io.new_line;

Ada.Text_io.put("entrer une valeur : "); Ada.Integer_Text_io.get (Integer(j1)); Ada.Text_io.new_line; Ada.Text_io.put_line("Integer'first : "); i := Integer'first; Ada.Integer_Text_io.put (i); Ada.Text_io.new_line; Ada.Text_io.put_line("Integer'last : "); j := Integer'last; Ada.Integer_Text_io.put (j); Ada.Text_io.new_line; Ada.Integer_Text_io.put (Integer(i1)); end essai;

2.3.2.3Les types Modular (dits types modulo)

Correspondent à des types entiers non signés avec une arithmétique cyclique et donc jamais de dépassement de capacité.

Ils sont utilisés pour gérer des données naturellement cycliques : heure, longitude.

type OctetNonSigné is mod 256; -- ensemble de valeurs 0..255

x : OctetNonSigné := 128; …

x := x + x; -- x vaut 0

Ada.Integer_Text_io.put(OctetNonSigne'modulus); Ensemble des opérations :

• opérations arithmétiques, identiques aux entiers signés mais modulo le type'modulus

• opérations logiques binaires (and, or, xor) agissent comme sur une suite de bits.

• opération logique unaire (not) soustrait la valeur maximale et se comporte vraiment comme

le not bit à bit pour les puissances de deux

• opérations provenant du paquetage Interfaces appliquées à des types tels que Unsigned_16 :

o function Shift_Left (Value : Unsigned_16; Amount : Natural) return Unsigned_16; o function Shift_Right (Value : Unsigned_16; Amount : Natural) return Unsigned_16; o function Shift_Right_Arithmetic (Value : Unsigned_16; Amount : Natural) return

Unsigned_16;

o function Rotate_Left (Value : Unsigned_16; Amount : Natural) return Unsigned_16;

o function Rotate_Right (Value : Unsigned_16; Amount : Natural) return Unsigned_16;

(22)

2.3.2.4Les types énumérés

2.3.2.4.1Caractéristique des types énumérés

Les types énumérés sont des ensembles de valeurs ordonnées. Chaque valeur est appelée un littéral énumératif et doit être donnée par l'utilisateur.

Déclaration de type énuméré :

type NomType is <ensemble de littéraux>; Exemples :

type Direction is (nord, est, sud, ouest);

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche); L'ensemble des valeurs est :

lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche L'ensemble des opérations applicables à ces valeurs est :

affectation :=

test d'(in)égalité = /=

test d'ordre > >= < <= lundi < jeudi

if demain >= mardi then ... Déclaration d'entités :

but : Direction; -- variable

demain : Jour := lundi; -- variable initialisée

milieu : constant Jour := jeudi; -- constante

hier, avantHier : Jour := unAutreJour; -- déclaration multiple

-- équivaut à hier : Jour := unAutreJour; avantHier : Jour := unAutreJour; Affectations correctes : but := ouest; demain := mardi; demain := milieu; Affectations incorrectes : but := dimanche; but := 8; but := demain; but := but + sud; milieu := mer;

2.3.2.4.2Types énumératifs prédéfinis

type Boolean is (false, true);

Ensemble de valeurs : false true Ensemble d'opérations :

:=

= /=

> >= < <=

not and or xor

exemples :

function "and" (left, right : Boolean) return Boolean; type Character is <ensemble de tous les caractères Latin_1>;

Ensemble de valeurs : Tous les caractères ASCII

Ensemble d'opérations : Identiques aux types énumératifs

type Wide_Character is <ensemble de caractères étendus>;

2.3.2.4.3Exemple d'utilisation de type énumératifs

(23)

procedure essai is

type Couleur is (rouge, vert, bleu, cyan, magenta, jaune); type Feu is (rouge, orange, jaune);

c1, c2 : Couleur; c3 : Couleur := bleu; fr1, fr2, fr3 : Feu; begin

if c1 in cyan .. jaune then c1 := rouge; end if;

fr1 := rouge;

for coul in Couleur'first .. Couleur'last loop

Ada.Integer_Text_io.put (couleur'pos(coul)); end loop;

end essai;

2.3.2.4.4Exemple d'utilisation de booléens

Boolean_io n'est pas standard mais est à définir par instanciation de Ada.Text_io.Enumeration_io. with Ada.Text_io, Boolean_io, Ada.Integer_Text_io;

procedure essai is b1, b2, b3 : Boolean; begin b1 := true; b2 := false; if b1 = b2 then -- /= < > <= >= boolean_io.put(b1); else

boolean_io.put(b2); -- affiche FALSE

end if; if b1 and b2 then b1 := true; else b2 := false; end if;

if b1 and then 1>2 then b1 := false; end if; if b1 or else b2 then b2 := false; end if; end essai;

2.3.3Les types réels

Spécification :

1_ Virgule fixe : delta d [range début .. fin]

d : intervalle minimum entre deux valeurs successives de l'ensemble des valeurs. Exemple : type Del is delta 0.05 range 0.0 .. 10.0;

L'ensemble des valeurs est : 0.0, 0.05, 0.10, 0.15, ... 2_ Virgule flottante : digits n [range début .. fin]

n : nombre de chiffres décimaux significatifs. Exemple : type Reel is digits 7;

Le type prédéfini Float est :

type Float is digits Float'digits range Float'first .. Float'last; L'ensemble des valeurs autorisées est :

(24)

Tous les réels ne sont pas représentables en machine. Un ensemble de nombre modèles est défini à l'élaboration d'un type réel. Ces nombre sont des multiples entiers de puissance de deux qui peuvent être représentés excatement. Les valeurs stockées pour les réels sont définies à partir de ces nombres modèles. Le nombre de digits assure la précision minimale dont on peut être sûr.

Les littéraux réels sont convertis en nombres modèles avec une précision qui dépend de l'implémentation. Les types à virgule fixe permettent une déclaration avec une erreur absolue donnée par la valeur en delta, ce qui spécifie exactement les nombres modèles.

Ada permet de contrôler l'erreur admissible dans un calcul numérique mais ceci ne sera pas abordé dans ce cours. Pour plus de renseignements, se référer au manuel de référence. Notamment, en ce qui concerne les attributs relatifs aux types réels.

L'ensemble des opérations comprend :

l'affectation :=

op relationnelles = /= < <= > >=

op binaires + - * / mod rem **

op unaires + - abs

Exemple d'utilisation de types réels en virgule fixe et virgule flottante : with Ada.Text_io, Ada.Float_Text_io, Ada.Integer_Text_io; procedure essai is

subtype Reel is Float digits 5 range -10000.0 .. 10000.0; type VirgFixe is delta 0.1 range -10.0 .. 10.0;

r1, r2, r3 : Reel; v1, v2 : VirgFixe; x: Float := 123.45E-1; begin r1 := 0.1E2; r2 := -1.2E2; r3 := (r1*r2)/3.0 + 2.2; if r3 in 1.0 .. 10000.2 then Ada.Text_io.put_line (" in "); end if; r3 := r1; x := Float (r1); if r1 = r2 then r3 := r1; end if; r1 := r1**2; x := Float (r1); v1 := 1.0; v2 := 2.2; v1 := v1 + v2; v1 := VirgFixe(v1*v2); Ada.Float_Text_io.put (Float'first); Ada.Float_Text_io.put (Float'last); Ada.Integer_Text_io.put (Float'digits); Ada.Float_Text_io.put (Float(r1)); Ada.Float_Text_io.put (x); end essai;

Attention : La conversion de Float vers Integer arrondit au lieu de tronquer !

1.4 devient 1

1.6 devient 2

1.5 devient 2

-1.5 devient -2 -- la valeur à mi-chemin est arrondie en s'éloignant de 0

(25)

Un type décimal est une forme de réel à virgule fixe. La déclaration fournit une valeur pour delta mais qui doit obligatoirement être une puissance de 10 et donne le nombre de chiffres décimaux significatifs.

Exemple : type Euro is delta 0.01 digits 14;

Ce qui permet 12 chiffres pour les Euros et 2 pour les centimes d'Euros.

Soit au maximum : 999 999 999 999.990 Euros

Les opération des types à virgule fixe s'appliquent aux types décimaux (sauf quelques règles particulières d'arrondi (destinées aux comptables ! Voir manuel de référence)

2.4Types composés principaux (tableaux et articles)

Une variable de type composé contient un certain nombre de composants.

Ces composants sont de même type pour les tableaux (arrays) et peuvent être de type différent pour les articles (records)

2.4.1Types tableaux

Une entité de type tableau est un ensemble indicé d'éléments de même type. La spécification d'un type tableau permet d'indiquer :

- le nombre d'indices,

- le type des indices qui doit être Discret

- le type des éléments.

-2.4.1.1Variables tableaux

Déclaration basique :

a : array (Integer range 1..10) of Integer

a est une variable ayant 10 composants de type Integer. L'accès à un composant se fait par :

a(3) := 1000; ou encore

j := a(i); avec 1<= i <= 10 et j : Integer

Pour un tableau à 2 dimensions on donnera un intervalle discret pour les indices de chaque dimension : a : array (Jour range lundi .. vendredi, Integer range -10..5) of Float;

accès aux composants par : a(lundi,1) := 0.0

r := a(j, -10); avec j : Jour et lundi <= j <= vendredi Le format général de la spécification est le suivant :

array (TypeDiscret1 range début .. fin [, TypeDiscret2 range début .. fin]) of TypeComposant; début et fin pouvant être des littéraux, des constantes ou des variables qui seront évaluées au moment de la déclaration.

Sauf cas particulier (usage unique et pas de passage de paramètres) il est pratiquement toujours préférable de déclarer des types tableaux et des variables de ces types là.

2.4.1.2Types tableaux non contraints

Les tableaux non contraints permettent une certaine généralisation des types tableaux en permettant de fixer les contraintes d'indices uniquement à la déclaration des variables de type tableau.

Exemple de type de tableau prédéfini non contraint :

type String is array (Positive range <>) of Character; -- <> se dit "boite" Exemples de déclarations de type tableau non contraint:

type Vecteur is array (Integer range <>) of Float; Ensemble de valeurs du type Vecteur:

Chaque Vecteur :

- a des composants de type Float,

- est indexé par des valeurs de type Integer. Mais tous les Vecteurs n'auront pas les mêmes bornes. On peut introduire un sous-type intermédiaire :

(26)

subtype Vecteur5 is Vecteur(1..5); v : Vecteur5;

Tableau bi-dimensionnel :

type Matrice is array ( Integer range <>,

Integer range <>) of Float; subtype Matrice3_3 is Matrice(1..3, 1..3);

m3 : Matrice3_3;

2.4.1.3Contraintes d'indices

Les types d'indices sont toujours de type Discret.

Une contrainte d'indice sert à spécifier les bornes des indices d'une entité tableau non contraint. Exemple :

v : Vecteur(2 .. 15); w : Vecteur (1 .. 1000);

v et w sont de même type et peuvent s'échanger des valeurs, à condition de respecter les contraintes d'indices.

m : Matrice(1 .. 1000, 15 .. 100);

mn : Matrice (1..n, 1..n); -- n sera évalué dynamiquement à la déclaration de mn

2.4.1.4Agrégats

Un agrégat est la forme littérale d'une valeur de type tableau (un élément de l'ensemble de valeurs du type tableau).

Il existe deux formes d'agrégats qui ne doivent pas être mélangées :

• l'agrégat positionnel où les valeurs sont donnés dans l'ordre où elles seront stockées dans la variable de type tableau. Les valeurs sont séparées par des virgules.

v : Vecteur5 := (1.0, 2.0, 3.0, 4.0, 5.0);

• l'agrégat par nom où chaque valeur est précédée de la valeur de l'indice et du symbole =>, ainsi l'ordre des indices n'a plus à être respecté.

v : Vecteur5 := (1 => 1.0, 2 =>2.0, 4 => 4.0, 5 => 5.0, 3 => 3.0);

Les règles de formation des agrégats ressemblent à celles utilisées pour l'instruction case. Chaque valeur peut être donnée pour un seul indice ou un intervalle ou plusieurs indices donnés explicitement.

La clause others permet de donner une valeur à tous les éléments non spécifiés mais doit toujours être placée à la fin de l'agrégat :

w : Vecteur(1..1000) := (1 => 1.0, 2 | 4 =>2.0, 3 => 3.0, 7..15 => 5.0, others => 0.5); m3 : Matrice3_3 := ( 1=> (1=>1.0, others=>0.0),

2=> (2=>1.0, others=>0.0), 3=> (3=>1.0, others=>0.0));

v : Vecteur(1 .. 15) := (1.0, 2.0, 3.0, 4.0, 5.0, others => 0.0);

La déclaration multiple suivie d'une affectation provoque l'initialisation des deux tableaux : v51, v52 : Vecteur5 := (others => 1.0);

initialise toutes les valeurs de v51 et de v52 à 1.

2.4.1.5Attributs de tableaux

Les bornes des indices sont dénotées par des attributs de tableaux : v : Vecteur (2 .. 15);

for n in v'first.. v'last loop ...

end loop;

v'first -- 2

v'last -- 15

v'range -- 2 .. 15

(27)

De même, pour un tableau multi-dimensionnel : m : Matrice (1 .. 10, 1 .. 100); m'last(1) -- 1000 m'last(2) -- 100 m'range(1) -- 1..1000 m'length(1) -- 1000

Les attributs first, last, length et range peuvent s'appliquer aux variables de type tableau ainsi qu'aux type et aux sous-types tableaux eux mêmes à condition qu'ils soient contraints.

Vecteur'first -- illégal

Vecteur5'first -- 1

Les bornes des indices peuvent provenir de la valeur initiale :

message : String := "combien de caractères ? "; -- message'first = 1, message'last = 24 v1 : Vecteur := (1.0, 2.5, 3.0);

Attention aux bornes de v1 : v1'first = Integer'first et v1'last = Integer'first + 3 – 1 !

Attention : others ne doit pas être utilisé dans un agrégat qui est utilisé pour initialiser une variable de type tableau non contraint !

On peut déclarer un tableau comme étant constant, auquel cas il doit être initialisé à la déclaration : messageContant : constant String := "message constant";

type Semaine is array (Jour) of Boolean;

jourOuvrés : constant Semaine := (true, true, true, true, true, false, false); ou alors jourOuvrés : constant Semaine := (lundi .. vendredi =>true, others => false);

2.4.1.6Tableaux non contraints et Passage de paramètres

Un paramètre formel de type tableau non contraint est contraint par les bornes du paramètre effectif correspondant :

procedure essai (s : String) is -- String non contraint

begin

for x in s'range loop -- s a les contraintes du paramètre effectif

... end loop; end essai; chaîne : String (1 .. 20); begin ...

Ada.Text_io.get(chaîne); -- il faudra saisir exactement 20 caractères !

essai (chaîne); -- s'range 1..20

Ada.Text_io.get_line(chaîne, l); -- la longueur réellement saisie donnée par l

essai (chaîne(1..l)); -- s'range 1..l

...

essai("Dupont"); -- s'range 1..6

end;

2.4.1.7Types tableaux contraints

Une contrainte d'indice peut être donnée dans la définition de type tableau : type VecteurFixe is array (1..4) of Float;

v1, v2 : VecteurFixe; -- ont tous les mêmes indices et des valeurs de type Float

type Jour is (lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche);

type Durée is Integer range 0..8;

type Horaire is array (Jour range lundi .. dimanche) of Durée; Equivaut à :

type HoraireStandard is array (Jour range <>) of Durée; suivi de :

(28)

on peut écrire aussi :

type Horaire is array (lundi .. dimanche) of Durée; ou encore :

type Horaire is array (Jour) of Durée;

En fait, sauf dans le cas où les indices sont bien établis et toujours identiques, il est préférable de déclarer des types tableaux non contraints.

L'utilisation des attributs dans les sous-programmes ayant des types tableaux en paramètres formels rend l'usage des types tableaux non contraints très performant.

2.4.1.8Sous-types de tableaux

Un sous-type d'un type tableau (contraint ou non contraint) doit spécifier explicitement toutes les bornes. subtype Rangée is Vecteur (0 .. 100);

subtype Ligne is String (1 .. 80);

Il est possible aussi de ne pas modifier les bornes, ce qui permet de donner un autre nom tout en restant compatible avec le type d'origine.

subtype semaine is jour.

2.4.1.9Opérations sur un type tableau

Affectation := : pour pouvoir affecter un tableau à un autre il est nécessaire qu'ils soient de même type, et de même taille pour chaque dimension, sans avoir forcément les mêmes indices. Quand les premiers indices ne sont pas identiques, on parle de glissement d'un tableau dans l'autre :

v : Vecteur (1..5); w : Vecteur (0..4); …

v := w;

Un non respect du nombre d'éléments à affecter provoquera un CONSTRAINT_ERROR.

Egalité, inégalité = /= : il est possible de comparer des tableaux de même type et de même longueur

pour chaque dimension, sans avoir forcément les mêmes indices. Les tests d'égalité suivent les mêmes règles de glissement que l'affectation sauf que si les tableaux n'ont pas la même longueur, la valeur retournée sera false. Les deux tableaux seront égaux s'ils possèdent le même nombre d'éléments et que ceux-ci soient égaux (en respectant l'ordre des éléments).

if v = w then … end if;

Opérateurs de comparaison <, <=, >, >= : s'appliquent uniquement à des tableaux à une dimension et contenant des éléments de type Discret. La comparaison suit la règle suivante, la position relative de chaque couple d'éléments dans le type permet de les ordonner.

type VecteurInt is array (Integer range 1..5) of Integer ; -- pas de type anonyme!!

vi1, vi2 : VecteurInt := (others => 0); vi1 (1) := 1;

if vi1 > vi2 then … end if; -- sinon la comparaison ne compile pas

s1 : String := "chat"; s2 : String := "chien"; if s1 < s2 then … end if;

• Tranches : une tranche de tableau à une dimension s'écrit en donnant le nom de la variable (ou constante) suivi de l'intervalle entre parenthèses.

s : String(1..10) := "1234567890";

t constant String := s(3..8); -- t'first = 3, t'last = 8, t = "345678" s(1..4) := "bara";

s(4..7) := := s(1..4); -- s(1..7) = "barbara" car s(1..4) est évalué avant l'affectation • Concaténation & : la concaténation s'effectue sur deux tableaux de même type. Le tableau résultat

doit avoir autant d'éléments que la somme des élément des deux tableaux à concaténer. w : Vecteur (-10..10) := (others => 0.0);

x : Vecteur (1..10) := (1.5, 2.5, 3.5, others => 4.0);

(29)

v2 : Vecteur := w & x; -- v aura la borne inférieure de w et 31 éléments

Les opérations and, or, xor et not s'appliquent aux tableaux à une dimension de booléens :

with Ada.Text_io, Boolean_io; procedure essai is

type Primaire is (r, v, b);

type Couleur is array (Primaire) of Boolean;

-- les 8 valeurs possibles peuvent être représentées -- pour plus de facilité par les constantes suivantes :

blanc : constant couleur := (true, true, true);

rouge : constant couleur := (true, false, false);

vert : constant couleur := (false, true, false);

bleu : constant couleur := (false, false, true);

cyan : constant couleur := (false, true, true);

magenta : constant couleur := (true, false, true);

jaune : constant couleur := (true, true, false);

noir : constant couleur := (false, false, false);

c1 : Couleur;

-- c peut prendre 2x2x2 valeurs différentes.

-- c est un tableau de trois composantes dont les indices sont : r, v, b. -- ces trois composantes sont c(r), c(v), c(b)

-- prenant chacune la valeur true ou false. begin

-- ce qui permet d'écrire l'expression suivante :

c1 := rouge or vert; -- qui équivaut au jaune,

Boolean_io.put(c1(r)); Boolean_io.put(c1(v)); Boolean_io.put(c1(b)); Ada.Text_io.new_line;

c1 := not noir; -- qui est blanc.

Boolean_io.put(c1(r)); Boolean_io.put(c1(v)); Boolean_io.put(c1(b)); Ada.Text_io.new_line;

end essai;

2.4.1.10Caractères et Chaînes de Caractères

Les types Character et Wide_Character sont des types énumérés prédéfinis. A partir de Character on peut créer d'autres types énumérés :

type ChiffreRomain is ('I', 'V', 'X', 'L', 'C', 'D', 'M'); -- tel que : chiffre : ChiffreRomain := 'D'; … ChiffreRomain'first = 'I' ChiffreRomain'succ('C') = chiffre ChiffreRomain'pos('M') = 6 chiffre > 'L' = true

Noter que le test 'X' < 'L' est illégal car ambigu : on ne sait pas si on compare des Character, des Wide_Character ou même des ChiffreRomain. Pour lever l'ambiguïté il faut qualifier au moins un des deux littéraux :

ChiffreRomain'('X') < 'L' = true Character'('X') < 'L' = false ! Attention :

• ChiffreRomain('X') ou ChiffreRomain(c) est une conversion de type.

• ChiffreRomain'('X') permet de qualifier une donnée littérale dans un type donné.

Pour utiliser les caractères non imprimables du type Character il faut donner leur chemin d'accès : with Ada.Character.Latin_1;

Ada.Character.Latin_1.nul -- pour la caractère nul par exemple

Les types String et Wide_String sont eux aussi prédéfinis comme des tableaux non contraints, respectivement de Character et de Wide_Character.

Ils obéissent donc à toutes les règles concernant les tableaux non contraints, avec quelques notations supplémentaires :

(30)

oie : constant String := ('o', 'i', 'e'); -- peut aussi s'écrire oie : constant String := "oie";

Pour placer le caractère '"' dans un chaîne :

('A', '"', 'B') = "A""B" -- avec doublement du guillemet Une chaîne vide sera :

""

On peut définir le type NombreRomain comme étant un tableau de ChiffreRomain contenant au moins un ChiffreRomain (ils ne connaissaient pas le 0 !) :

type NombreRomain is array (Positive range <>) of ChiffreRomain; -- puis

DeuxMilleUn : constant NombreRomain := "MMI"; -- constant pour ne pas être changé! function addition (nb1, nb2 : NombreRomain) return NombreRomain is

nb : NombreRomain(1..Integer'max(nb1'length, nb2'length)*2); -- ? fin : Positive;

begin

-- convertir nb1 et nb2 en Positive, les additionner et -- reconvertir en NombreRomain dans la variable nb return nb(1..fin);

end addition,

nbr : NombreRomain := addition("X", DeuxMilleUn); -- avec la fonction addition

nbr : nombreRomain(1..5) := "CCL" & "IV"; -- opération effectuée sur des NombreRomain s : String (1..5) := "CCL" & "IV"; -- opération sur des String, pas d'ambiguïté mais b : Boolean := "CCL" < "IV"; -- illégal car ambigu (String ou NombreRomain ?) Contrairement au C, pour faire un tableau de chaînes de caractères, il ne faut surtout pas faire un tableau en 2 dimensions de caractères, car sinon, toutes les sous-chaînes doivent absolument la même longueur et de plus, on perd la possibilité de manipuler des String.

En faisant un tableau de String, on garde l'obligation de chaînes de taille identique mais on manipule vraiment des String :

type TableauDeChaines is array (Integer range <>) of String(1..5); ferme : TableauDeChaines := ("poule", "chien", "vache");

Ada.Text_io.put(ferme(2));

Une solution pour créer un tableau de chaînes de longueurs quelconques sera d'utiliser les pointeurs (type accès). Une autre solution est d'utiliser le type Ada.Strings.map.Ubounded_String :

with Ada.Text_io, Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure essaiChaines is

type TableauDeChaines is array (Integer range <>) of Unbounded_String;

ferme : TableauDeChaines (1..5) := (to_unbounded_string("poule",

to_unbounded_string("cheval"), to_unbounded_string("chat"), to_unbounded_string("chien"), to_unbounded_string("brebis")); begin

for i in ferme'range loop

Ada.Text_io.put_line(to_string(ferme(i))); end loop;

end essaiChaines;

2.4.1.11Conversions explicites entre types tableaux

with Ada.Text_io, Ada.Integer_Text_io; procedure essaiConversion is

type VecteurI is array (Integer range <>) of Integer; type Table is new VecteurI;

-- type Table is array (Integer range <>) of Integer;

-- méthode retournant une chaîne de caractères représentative -- de l'état de la table (version non récursive)

function toString2 (t : Table) return String is i : Integer:= 1;

(31)

s : String(1 .. t'length*(Integer'image(i)'length));

begin

for j in t'range loop declare im : String := Integer'image(t(j)); begin s(i..i+im'length-1) := im; i := i + im'length; end; end loop; return s(1..i-1); end toString2;

-- méthode retournant une chaîne de caractères représentative -- de l'état de la table (version récursive)

function toString (t : Table) return String is begin

if t'length >0 then declare

im : String := Integer'image(t(t'first)); begin

return im & toString(t(t'first+1..t'last)); end;

else return ""; end if; end toString;

-- tri deux boucles simple

procedure trier (v : in out VecteurI) is tmp : Integer;

begin

for i in v'first..v'last-1 loop for j in i+1..v'last loop if v(i) > v(j) then tmp := v(i); v(i) := v(j); v(j) := tmp; end if; end loop; end loop; end trier; t : Table(1..10) := (2,8,6,4,3,5,1,7,9,0); v : VecteurI := (2,8,6,4,3,5,1,7,9,0); begin

Ada.Text_io.put("t avant le tri : "); Ada.Text_io.put_line(toString2(t)); trier(VecteurI(t));

Ada.Text_io.put("t après le tri : "); Ada.Text_io.put_line(toString2(t)); Ada.Text_io.put("v avant le tri : "); Ada.Text_io.put_line(toString(Table(v))); trier(v);

Ada.Text_io.put("v après le tri : "); Ada.Text_io.put_line(toString(Table(v))); end essaiConversion;

2.4.2Types articles

Une entité de type article est un ensemble de composants pouvant être de type différent.

La spécification d'un type article doit d'indiquer son nom, la liste des composants (et leur type). Contrairement aux tableaux, il ne peut y avoir de type article anonyme (une variable ne peut être déclarée si le type n'existe pas). Le format général de la spécification est le suivant :

type Article is record déclaration_entité 1; déclaration_entité 2; ...

Références

Documents relatifs

On fait coïncider l'origine des deux vecteurs en utilisant la notion mathématique de &#34;vecteur équipollent&#34; c'est-à-dire que à partir de l'origine d'un des

Le vecteur de Darboux est alors la vitesse angulaire instantanée du trièdre de Frenet lorsque le point courant se déplace avec une vitesse

[r]

Ses équipes du CEA-Liten et du CEA-DAM se positionnent en effet à tous les niveaux de développements : électrolyseur pour la production d’hydrogène, réservoir pour son stockage,

[r]

dans la condition de jauge de Coulomb en écrivant que la circulation du potentiel vecteur le long d’un parcours fermé est égale au flux du champ d’induction

Les méthodes de formation de voies sont des techniques qui permettent d’obtenir la contribution des sources qui se trouvent dans un domaine sélectionné de l’espace

[1.5 pts] Ecrire une fonction ´ Reel minimum(Arbre* A) qui ´ etant donn´ e un arbre binaire de recherche non vide A retourne le plus petit r´ eel pr´ esent dans A. Que faut-il