MIAGE CRÉTEIL: Algorithmique & Génie Logiciel avec ADA 1999 – 2008
Algorithmique & Génie Logiciel avec ADA
Une Introduction
Version
1.0 , le
La version la plus récente est disponible à http://www.dgaudry.com
Pour toute correspondance, utiliser : daniel@dgaudry.com
Download :
HTTP download open office version: VERSION OPEN OFFICE
HTTP download pdf version: Version PDF
Table des matières
1 Introduction au langage 8
2 La structure de base 8
2.1 ce qu'il faut savoir 8
2.1.1 Les mots réservés 8
2.1.2 Les attributs 8
2.1.3 Des noms créés par l'utilisateur 9
2.1.4 Des lignes de code 9
2.1.5 Des structures 9
2.1.6 Des opérateurs 9
2.2 création d'un programme 10
2.2.1 Le fichier texte contenant le code source 10
2.2.2 Zone 1 10
2.2.3 Zone 2 10
2.2.4 Zone 3 10
2.3 Le code source et sa traduction en exécutable 10
2.3.1 La compilation 10
2.3.2 Le link 10
2.3.3 L'éditeur 11
2.3.4 Un premier exemple 11
3 Les variables 11
3.1 Les différents types de variables 12
3.2 Les déclarations de type de variables et les déclarations de variables à partir de leur type 12
3.2.1 Les nombres constants 12
3.2.2 Les entiers 12
3.2.2.1 Integer ou long_integer 12
3.2.2.2 Range 13
3.2.2.3 Modulo 13
3.2.2.4 Positive 13
3.2.2.5 Natural 14
3.2.2.6 Les attributs applicables aux types entier ou dérivés 14
3.2.3 Les options pour les types 14
3.2.3.1 Subtype 15
3.2.3.2 New 15
3.2.3.3 Private 15
3.2.3.4 Limited private 15
3.2.3.5 Aliased 16
3.2.4 Les réels 16
3.2.4.1 Digits 16
3.2.4.2 Float ou long_float 16
3.2.4.3 Delta 16
3.2.4.4 Delta et digits 16
3.2.4.5 Les attributs applicables aux types float ou dérivés 17
3.2.5 Les booléens 17
3.2.6 Les caractères 17
3.2.6.1 La table ASCII 18
3.2.7 Les énumérations 19
3.2.8 Les tableaux et matrices 19
3.2.8.1 L'index et le contenu 19
3.2.8.2 Index de limites définies par des nombres 19
3.2.8.3 Index de limites définies par type 19
3.2.8.4 Index de limites définies par les limites d'un subtype 20
3.2.8.5 Exemples de matrices 20
3.2.9 Les chaînes de caractères 21
3.2.10 Les records 21
3.2.10.1 Les records simples 21
3.2.10.2 Les records avec discriminants 22
3.2.10.3 Les records avec des parties variables et des choix discrets 22
3.2.11 Les pointeurs 22
3.2.11.1 Déclaration de base 23
3.2.11.2 Déclarations incomplètes 24
3.2.11.3 Terminaison d'une déclaration incomplète 24
3.2.11.4 Pointeurs vers des objets composés (records et tableaux) 24
3.2.11.5 Assignation et test avec pointeurs 25
3.2.11.6 Effacement d'un pointeur et des données associées 25
3.2.12 Programmation objet 25
3.2.13 Abstract 25
3.2.14 Tagged 26
3.2.15 Extension et héritabilité des types "tagged" 26
3.2.16 Le type task (tâche) 26
3.3 Domaine d'existence des variables 26
3.3.1 Domaine d'existence limité à quelques lignes dans un code 26
3.3.2 Domaine d'existence limité à toutes les lignes d'une procédure / fonction 27
3.3.3 Domaine d'existence limité à une partie des procédures / fonctions d'un package 27
3.3.4 Domaine d'existence limité à toutes les lignes d'un package 27
3.3.4.1 Le domaine est limité à toutes les lignes du package avec impossibilité d'accès externe 27
3.3.4.1.1 Déclaration dans le "body" 27
3.3.4.1.2 Déclaration dans la spécification 28
3.3.4.2 Le domaine est limité à toutes les lignes du package avec possibilité d'accès externe 28
3.3.5 Domaine d'existence limité à toutes les lignes de tous les packages en utilisant "with ..." 29
3.3.6 Utilisation de "finalize" pour illustrer le domaine d'existence 29
4 Le découpage des routines externes en Fichier ‘ADS' et ‘ADB' 30
4.1 Le choix entre une fonction et une procédure 30
4.1.1 Le passage de paramètres 30
4.1.1.1 Le mode in 30
4.1.1.2 Le mode out 31
4.1.1.3 Le mode in out 31
4.1.1.4 Access 31
4.1.2 Les fonctions 31
4.1.3 Les procédures 32
5 La syntaxe du code 32
5.1 La syntaxe des affectations 32
5.1.1 Opérateurs logiques 33
5.1.2 Opérateurs de comparaison 33
5.1.3 Opérateurs d'addition 34
5.1.4 Opérateurs de signe 34
5.1.5 Opérateurs de multiplication 34
5.1.6 Opérateurs de rang le plus haut 35
5.1.7 Modification du sens des opérateurs classiques 35
5.1.8 Conversion entre types 35
5.1.8.1 Entier vers réel 35
5.1.8.2 Réel vers entier 35
5.1.8.3 Conversion par utilisation d'une adresse mémoire commune 35
5.2 La syntaxe des tests 35
5.2.1 Le test if 35
5.2.2 Le test case 36
5.3 La syntaxe des boucles 36
5.3.1 Boucle simple 37
5.3.2 Boucle while 37
5.3.3 Boucle for 37
5.4 La syntaxe d'appel des fonctions 37
5.5 La syntaxe d'appel des procédures 38
5.6 La segmentation en parties indépendantes par ‘declare' ou begin 38
5.7 Gestion des erreurs durant l'exécution du programme 39
5.7.1 La notion d'exception 39
5.7.2 Les exception intégrées au langage 39
5.7.3 Définition d'une exception 39
5.7.4 Gestion des exceptions 39
5.7.5 Impression écran du type d'erreur rencontré 40
6 Entrées et Sorties 40
6.1 entrée clavier 40
6.2 Utilisation de read_write 40
6.2.1 Entrée d'un entier 40
6.2.2 Sortie écran d'un entier 40
6.2.3 Entrée d'un entier long 41
6.2.4 Sortie écran d'un entier long 41
6.2.5 Entrée d'un réel 41
6.2.6 Sortie écran d'un réel 41
6.2.7 Entrée d'un réel long 41
6.2.8 Sortie écran d'un réel long 41
6.2.9 Entrée d'une chaîne de caractères 42
6.2.10 Sortie écran d'une chaîne de caractères 42
6.3 Utilisation de Ada.Text_Io 42
6.3.0.1 Lecture de nombre après instantiation: 42
6.3.0.1.1 Entier: 42
6.3.0.1.2 Réel: 42
6.3.0.1.3 Chaîne de caractères 43
6.3.1 Sortie écran 44
6.3.1.1 Principes généraux 44
6.3.1.2 Chaîne de caractères 44
6.3.1.3 Entier 44
6.3.1.4 Réels 45
6.3.1.4.1 Sans mise en forme 45
6.3.1.4.2 Avec mise en forme 45
6.3.1.5 Énumération / booléens 46
6.4 Lecture et écriture de fichiers 46
6.4.1 Lecture de fichier texte 46
6.4.2 Écriture de fichier texte 47
7 Les routines: procedures et fonctions 47
7.1 Premier exemple: la somme d'un tableau d'entiers 47
7.2 Second exemple: Manipulation des bits, application à la lecture d'un fichier texte par bloc pour codage 48
7.2.1 Utilisation des opérateurs logiques 48
7.2.1.1 Masque 49
7.2.2 Assignation 49
7.2.3 Décalage gauche et droite 49
7.2.3.1 Rotation 49
7.2.4 Détails du stockage des entiers, caractères et réels dans un mot de 32 bits 50
7.2.4.1 Partage de zone de mémoire 50
7.2.4.2 Transformation little endian big endian 51
7.2.5 La lecture d'un fichier texte par bloc pour codage 53
7.3 programmation d'un code à exécuter au début et la fin du domaine de validité d'une variable 56
8 Générique 57
8.1 Un cas d'école 57
8.1.1 La solution (ici elle est plus longue que la duplication mais c'est un exemple simpliste!) se compose de trois fichiers: 57
8.1.1.1 Une partie générique, fichier s.ads 57
8.1.1.2 Une partie générique, fichier s.adb 57
8.1.1.3 Un exemple de procédure d'utilisation, fichier d.adb 58
8.2 L'Intérêt du générique 58
8.3 Les variables génériques 58
8.3.1 Les variables de type private 59
8.3.2 Les variables de type discrètes 59
8.3.3 Les variables de type entier 59
8.3.4 Les variables de type Modulo 59
8.3.5 Les variables de type Réel 59
8.4 Les procédures et fonctions génériques 59
8.4.1 Les changements dans le code 59
8.4.2 La spécification 59
8.4.3 Le body 60
8.5 Le programme principal 61
8.5.1 Premier type de données 61
8.5.2 Instantiation du générique pour le premier type de données 61
8.5.3 Second type de données 62
8.5.4 Instantiation du générique pour le second type de données 62
8.5.5 Le code du programme principal 63
8.5.6 Le résultat du programme principal 64
8.6 Les fonctions et procédures dont un générique a besoin 65
8.7 Les fonctions Logiques: égal supérieur et inférieure 65
8.7.1 La fonction < 65
8.7.2 La fonction > 65
8.7.3 Instantiation du générique 66
8.7.4 programme principal 66
8.7.5 résultat du programme principal 68
8.8 Autres exemples 68
8.8.1 Utilisation de générique déjà écrits: Un exemple de tri commenté 68
8.8.2 Fichier t.adb 69
8.8.3 fichier sort.ads 69
8.8.4 fichier sort.adb 69
8.8.5 Le résultat écran 70
9 programmation objet 70
9.1 Le type 'tagged' 70
9.2 l'extension d'un type 'tagged' et héritabilité 71
9.3 Le polymorphisme en utilisant le concept de 'dispatching operations' 72
9.4 Exemple de dispatching pour le calcul des surfaces de triangles 74
9.5 Calcul d'une expression arithmétique postscript en utilisant un arbre d'expressions et un stack 77
10 Task 79
10.1 Introduction 79
10.2 Un premier exemple: écriture SUR L'ÉCRAN PAR DEUX TÂCHES NON COORDONNÉES 79
10.2.1 Le programme principal 79
10.2.1.1 Définition des tâches 79
10.2.1.2 Activation des tâches 79
10.2.1.3 Démarrage et arrêt des tâches 80
10.2.2 La tâche numéro 1 80
10.2.2.1 Les différentes parties d'une tâche 80
10.2.2.2 La partie s'exécutant seule 81
10.2.2.3 La partie s'exécutant simultanément avec le programme principal 81
10.2.3 La tâche numéro 2 81
10.3 Second exemple : utilisation d'un sémaphore pour COORDONNER LES DEUX TÂCHES 81
10.3.1 Introduction 81
10.3.2 But d'un sémaphore 82
10.3.2.1 Seize 82
10.3.2.2 Release 82
10.3.3 Le sémaphore écrit en protected type 82
10.3.4 Fonctionnement du sémaphore 83
10.3.5 Le même problème avec synchronisation par sémaphore 83
10.4 Troisième exemple: utilisation de Ada.Finalization pour la gestion du sémaphore 83
10.4.1 Introduction à Ada.Finalization 84
10.4.1.1 Fichier final.ads 84
10.4.1.2 Fichier final.adb 84
10.4.1.3 Fichier final-protects.ads 84
10.4.1.4 Fichier final-protects.adb 85
10.4.1.5 Fichier t_final.adb 85
10.4.2 Utilisation pour la gestion du sémaphore 85
10.4.3 Le code avec Ada.Finalization 85
10.5 Quatrième exemple: création dynamique de plusieurs tâches 85
10.5.1 Le discriminant d'une tâche 86
10.5.2 La déclaration d'une tâche en vue de sa création dynamique 86
10.5.3 L'appel d'une tâche avec discriminant 86
10.5.4 Création dynamique d'une tâche 86
10.5.5 Transmission de la variable d'appel à la partie de la tâche s'exécutanten parallèle et Utilisation du discriminant 87
10.5.6 Résultat du fonctionnement 88
10.6 Cinquième exemple: le dinner des philosophes 88
10.7 Les codes complets 92
10.8 Premier exemple 92
10.8.1 Task_1.adb 92
10.8.2 Task_1_a.ads 93
10.8.3 Task_1_a.adb 93
10.9 second exemple 95
10.9.1 Task_2.adb 95
10.9.2 Task_2_a.ads 96
10.9.3 Task_2_a.adb 96
10.10 troisième exemple 98
10.10.1 Fichier semaph.ads 98
10.10.2 Fichier semaph.adb 99
10.10.3 fichier Semaph-Protects.ads 100
10.10.4 fichier Semaph-Protects.adb 101
10.10.5 fichier task_3_a.ads 102
10.10.6 fichier task_3_a.adb 102
10.10.7 fichier task_3.adb 104
10.11 quatrième exemple 105
10.11.1 Fichier a.adb 105
10.11.2 Fichier call_task.ads 105
10.11.3 Fichier call_task.adb 105
10.11.4 Fichier task_global.ads 106
10.11.5 Fichier task_global.adb 106
10.12 Cinquième exemple 106
10.12.1 Fichier Philosopher_Main.adb 106
10.12.2 Fichier Philosopher_Data.ads 107
10.12.3 Fichier Philosopher_Task.ads 107
10.12.4 Fichier Philosopher_Task.adb 107
10.12.5 Fichier Random_Normal.ads 110
10.12.6 Fichier Random_Normal.adb 111
11 Algorithmique 115
11.1 introduction 115
11.1.1 Grammaires et langage formel, automates à états finis 115
11.1.2 Table de hashing 115
11.1.3 Stack 115
11.1.4 Queue 115
11.1.5 Arbre binaire 115
11.1.6 Arbre dictionnaire 115
11.1.7 Graphe 115
11.1.8 Tris 115
11.1.9 Ensemble 116
11.1.10 Anneau 116
11.2 grammaires et langage formel, automates à états finis 116
11.2.1 Les abréviations 116
11.2.2 Fonction 116
11.2.3 Procédure 116
11.2.4 L'algorithme 116
11.2.5 La déclaration 116
11.2.6 L'instruction 117
11.2.7 L'expression multiple 117
11.2.8 Le test 117
11.2.9 L'itération 117
11.2.10 Le block d'instruction 117
11.3 Invariant de boucle et validation 117
11.3.1 Utilisation de l'invariant de boucle 117
11.4 Complexité des algorithmes 118
11.5 Temps d'exécution des algorithmes 118
12 Les structures Classiques 118
12.1 L' itérateur 118
12.2 Introduction 119
12.3 La table de hashing 119
12.3.1 Vitesse de traitement 120
12.3.2 Traduction de la clé en un nombre 120
12.3.2.1 Numérisation par addition 120
12.3.2.2 Partage d'emplacement mémoire 120
12.3.2.3 Attribution d'une valeur numérique à chaque caractère 121
12.3.2.4 Numérisation par multiplication 122
12.3.2.5 Numérisation par division 123
12.3.2.6 Numérisation quadratique 123
12.3.2.7 Numérisation par décalage et manipulation des bits 123
12.3.3 Les collisions et leur résolution 124
12.3.4 Table de Hashing: Implémentation avec un tableau simple 124
12.3.4.1 Données et Types de données: 124
12.3.4.2 Initialiser 126
12.3.4.3 Ajout 126
12.3.4.4 Recherche 126
12.3.4.5 Suppression 127
12.3.4.6 itérateur 127
12.3.4.6.1 Initialiser 127
12.3.4.6.2 Suivant 128
12.3.4.6.3 Valeur 128
12.3.4.6.4 Fini 128
12.3.5 Table de Hashing: Implémentation avec un tableau Chaîné et des pointeurs 128
12.3.5.1 Données et Types de données: 129
12.3.5.2 Initialiser 131
12.3.5.3 Ajout 131
12.3.5.4 Recherche 132
12.3.5.5 Suppression 133
12.3.5.6 itérateur 134
12.3.5.6.1 Initialiser 134
12.3.5.6.2 Suivant 134
12.3.5.6.3 Fini 134
12.3.5.6.4 Valeur 134
12.4 Le stack 134
12.4.0.1 Données et Types de données: 135
12.4.1 Le stack (pile) : Implémentation avec un tableau 136
12.4.1.1 Ajout (push) 136
12.4.1.2 Délétion (pop) 136
12.4.1.3 Dernière valeur (top) 136
12.4.1.4 Initialiser 137
12.4.1.5 itérateur 137
12.4.2 Le stack: Implémentation avec des pointeurs 137
12.4.2.1 Données et Types de données: 137
12.4.2.2 Ajout (push) 139
12.4.2.3 Délétion (pop) 139
12.4.2.4 Dernière valeur (top) 139
12.4.2.5 Initialiser 140
12.4.2.6 Itérateur 140
12.4.2.6.1 Initialiser 140
12.4.2.6.2 Valeur 140
12.4.2.6.3 Suivant 140
12.4.2.6.4 Fini 141
12.5 La queue 141
12.5.1 La queue: Implémentation avec un tableau 141
12.5.1.1 Données et Types de données: 141
12.5.1.2 Ajout 142
12.5.1.3 Délétion 143
12.5.1.4 Dernière valeur 143
12.5.1.5 Initialiser 143
12.5.1.6 Itérateur 143
12.5.1.6.1 Initialiser 143
12.5.1.6.2 Valeur 144
12.5.1.6.3 Suivant 144
12.5.1.6.4 Fini 144
12.5.2 La queue: Implémentation avec des pointeurs 144
12.5.2.1 Données et Types de données: 145
12.5.2.2 Ajout 146
12.5.2.3 Délétion 148
12.5.2.4 Dernière valeur 149
12.5.2.5 Initialiser 149
12.5.2.6 Itérateur 149
12.5.2.6.1 Initialiser 149
12.5.2.6.2 Valeur 149
12.5.2.6.3 Suivant 149
12.5.2.6.4 Fini 150
12.6 L'anneau 150
12.6.1 L'anneau: Implémentation avec des pointeurs 150
12.6.1.1 Ajout 152
12.6.1.2 Délétion 156
12.6.1.3 Valeur 157
12.6.1.4 Initialiser 158
12.6.1.5 Rotation 158
12.6.1.6 Itérateur 158
12.6.1.6.1 Initialiser 158
12.6.1.6.2 Valeur 158
12.6.1.6.3 Suivant 159
12.6.1.6.4 Fini 159
12.7 L'arbre binaire 159
12.7.1 L'arbre binaire: Implémentation avec un tableau 160
12.7.1.1 Données et Types de données: 163
12.7.1.2 Ajout 163
12.7.1.3 Délétion 164
12.7.1.3.1 La recherche de l'élément à supprimer 164
12.7.1.3.2 Suppression: le choix entre les trois cas 164
12.7.1.3.3 Le cas où il n'y a aucun fils 164
12.7.1.3.4 Le cas où il n'y a qu'un seul fils 164
12.7.1.3.5 Le cas où il y a deux fils. 165
12.7.1.3.6 Remplacement par le sous arbre 165
12.7.1.4 Recherche 169
12.7.1.5 Initialiser 170
12.7.1.6 itérateur (pre-order, in-order & post-order) 170
12.7.1.6.1 Initialiser 170
12.7.1.6.2 Valeur 171
12.7.1.6.3 Suivant 171
12.7.1.6.4 Fini 172
12.7.2 L'arbre binaire: Implémentation avec des pointeurs 172
12.7.2.1 Données et Types de données: 172
12.7.2.2 Ajout 174
12.7.2.3 Délétion 176
12.7.2.3.1 Recherche avec direction depuis le parent 176
12.7.2.3.2 Suppression: le choix entre les QUATRE cas 176
12.7.2.4 Initialiser 180
12.7.2.5 Recherche 180
12.7.2.6 Itérateur (pre-order, in-order & post-order) 181
12.7.2.6.1 Initialiser 181
12.7.2.6.2 Valeur 181
12.7.2.6.3 Suivant 182
12.7.2.6.4 Fini 184
13 Les algorithmes de tri classiques 184
13.1 Le tri bulle (bubble sort) 184
13.2 Le tri par tas (heap sort) 185
13.2.1 Algorithme 185
13.2.2 Pseudo code 190
13.2.2.1 Swap 190
13.2.2.2 transformation d'un sous arbre en tas 190
13.2.2.3 Construction du tas à partir d'un arbre quelconque 191
13.2.2.4 Tri à partir d'un tas 192
13.2.2.5 Programme principal 192
13.3 Le tri fusion (melt sort) 192
13.3.1 Le Rôle de la double récursion 192
13.3.2 La partie tri fusion 192
13.3.3 Le code en Ada 197
13.4 Introduction aux grammaires et langage formel, automates à états finis 198
13.4.1 Implémentation par un automate 198
13.4.1.1 Le problème 200
13.4.1.2 La solution 201
13.4.2 Implémentation par une série de fonctions d'un exemple récursif 202
13.4.2.1 Le problème 202
13.4.2.2 La solution 202
13.4.2.2.1 Les types de données 202
13.4.2.2.2 L'objet token et les opérations associées 202
13.5 L'arbre dictionnaire 204
13.5.0.1 Données et Types de données: 204
13.6 Tirage au hasard 205
13.6.1 Utilisation des packages inclus dans le langage 205
13.6.2 Utilisation d'un algorithme particulier: "mersenne twister" 207
14 Annexes: les codes complets 211
14.1 Le package read_write 211
14.2 Le code de la table de hashing en tableau simple 220
14.3 Le code de la table de hashing en tableau chaîné 225
14.4 Le code du stack (tableau) 231
14.5 Le code du stack (pointeurs) 235
14.6 Le code de la queue (tableau) 240
14.7 Le code de la queue (pointeurs) 244
14.8 Le code de l'anneau 249
14.9 Le code de l'arbre binaire (tableau) 260
14.10 Le code de l'arbre binaire (pointeurs) 268
14.11 Le code du tri bulle 277
14.12 Le code du tri en tas 277
14.13 Le code du tri fusion 280
14.14 Le code du test de l'expression algébrique: automate 282
14.15 Le code du test de l'expression algébrique: Fonctions 283
14.16 Le code de l'arbre dictionnaire 287
15 L'algorithmique en action, exemple 1: un calculateur simple 293
15.1 Analyse 293
15.1.1 Première approche du découpage 293
15.1.2 Entrée du calcul 293
15.1.3 Test de la validité 293
15.1.4 Traduction du découpage précédent en postfix pour le calcul 293
15.1.5 Calcul à partir de la traduction postfix. 293
15.1.6 Affichage du résultat 293
15.1.7 Découpage en tâches indépendantes 294
15.1.8 Dépendances des modules 294
15.2 Définitions des données 294
15.2.1 Identification des données 294
15.2.2 Le stockage des données 294
15.2.3 Les types de données 294
15.2.3.1 Chaîne de caractères 294
15.2.3.2 Tableau de chaînes de caractères 294
15.2.4 Les données dans la spécification d'un « package » commun 295
15.2.4.1 Le code 295
15.3 Relations entre les modules 295
15.3.0.1 Nom du module 295
15.3.0.2 Données en sortie 295
15.3.1 Le module principal 295
15.3.1.1 Nom du module 295
15.3.1.2 Données à l'entrée 295
15.3.1.3 Traitement des données 296
15.3.1.4 Données en sortie 296
15.3.2 Le module entrée utilisateur 296
15.3.2.1 Nom du module 296
15.3.2.2 Données à l'entrée 296
15.3.2.3 Traitement des données 296
15.3.2.4 Données en sortie 296
15.3.3 Le module test de la validité 296
15.3.3.1 Nom du module 296
15.3.3.2 Données à l'entrée 296
15.3.3.3 Traitement des données 296
15.3.3.4 Données en sortie 296
15.3.3.5 Le Module découpage en opérateur opérande 297
15.3.3.5.1 Nom du module 297
15.3.3.5.2 Données à l'entrée 297
15.3.3.5.3 Traitement des données 297
15.3.3.5.4 Données en sortie 297
15.3.4 Le module traduction du découpage précédent en postfix 297
15.3.4.1 Nom du module 297
15.3.4.2 Données à l'entrée 297
15.3.4.3 Traitement des données 297
15.3.4.4 Données en sortie 297
15.3.5 Le module Calcul à partir du postfix 297
15.3.5.1 Nom du module 297
15.3.5.2 Données à l'entrée 297
15.3.5.3 Traitement des données 297
15.3.5.4 Données en sortie 298
15.3.6 Le module affichage du résultat 298
15.3.6.1 Nom du module 298
15.3.6.2 Données à l'entrée 298
15.3.6.3 Traitement des données 298
15.3.6.4 Données en sortie 298
15.3.7 Le code : le point de départ 298
15.3.7.1 data.ads 298
15.3.7.2 main.adb 298
15.3.7.3 calcul.ads 299
15.3.7.4 calcul.adb 299
15.3.7.5 data_io.ads 299
15.3.7.6 data_io.adb 299
15.3.7.7 decoupage.ads 300
15.3.7.8 decoupage.adb 300
15.3.7.9 traduction.ads 300
15.3.7.10 traduction.adb 300
15.3.7.11 validation.ads 300
15.3.7.12 validation.adb 301
15.4 Le Traitement des données 301
15.4.1 Le module définition des données 301
15.4.1.1 Définition des procédure et fonctions 301
15.4.1.2 Traitement 301
15.4.2 Le module principal 301
15.4.2.1 Définition des procédure et fonctions 301
15.4.2.2 Traitement 301
15.4.3 Le module entrée utilisateur 301
15.4.3.1 Définition des procédure et fonctions 301
15.4.3.2 Traitement 302
15.4.3.2.1 Lecture de la commande 302
15.4.3.2.2 Analyse du problème 302
15.4.3.2.3 Lecture de la ligne de commande 302
15.4.3.2.4 Entrée par l'utilisateur s'il n'y a pas d'arguments sur la ligne de commande. 304
15.4.3.3 Le code complet 304
15.4.4 Le module test de la validité 305
15.4.4.1 Définition des procédure et fonctions 305
15.4.4.2 Traitement 305
15.4.4.2.1 Introduction: la grammaire d'une expression arithmétique 305
15.4.4.2.2 Traduction de la grammaire dans un automate à états finis 305
15.4.4.2.3 Les transitions 305
15.4.4.2.4 La table de traduction caractères ==> transitions 305
15.4.4.2.5 La matrice états transitions 307
15.4.4.2.6 Le fonctionnement de l'automate 308
15.4.4.3 Le code complet 308
15.4.5 Le Module découpage en opérateur opérande 309
15.4.5.1 Définition des procédure et fonctions 309
15.4.5.2 Traitement 309
15.4.5.2.1 Le résumé 309
15.4.5.2.2 Les détails 310
15.4.5.2.3 Parties copiées et collées du module précédent 310
15.4.5.2.4 Les variables 310
15.4.5.2.5 La table de vérité 311
15.4.5.2.6 Les détails du déroulement 311
15.4.5.3 Le code complet 312
15.4.6 Le module traduction du découpage précédent en postfix 313
15.4.6.1 Définition des procédure et fonctions 313
15.4.6.2 Traitement 314
15.4.6.3 Nature des données d’entrée de l’algorithme 314
15.4.6.4 Algorithme de Traduction 314
15.4.6.4.1 Le stack 314
15.4.6.4.2 Priorité Des Opérateurs 315
15.4.6.5 Partie principale 316
15.4.6.6 L'algorithme plus détaillé 316
15.4.6.7 Analyse de l'algorithme détaillé 317
15.4.6.8 Le code du stack 317
15.4.6.8.1 Les variables 317
15.4.6.8.2 Le code 317
15.4.6.8.3 Push 317
15.4.6.8.4 Pop 317
15.4.6.8.5 Value 317
15.4.6.8.6 Clear 318
15.4.6.9 le code du test de priorité des opérateurs 318
15.4.6.9.1 Les variables 318
15.4.6.9.2 Le code 318
15.4.6.10 Le code du test opérande 319
15.4.6.11 Le code du programme principal 319
15.4.7 Calculs à partir du postfix 322
15.4.7.1 Définition des procédure et fonctions 322
15.4.7.2 Traitement 322
15.4.7.2.1 Résumé 322
15.4.7.2.2 Exemple 322
15.4.7.2.3 Détails des opérations 324
15.4.7.2.4 Le stack de réels 325
15.4.7.3 Le code complet 325
15.4.8 Le module affichage du résultat 326
15.4.8.1 Définition des procédure et fonctions 326
15.4.8.2 Traitement 326
15.4.8.2.1 Exploration des possibilités du langage 327
15.4.8.2.2 Utilisation de « image » 327
15.4.8.2.3 Utilisation du générique « Float_Io » 327
15.4.8.2.4 Utilisation du générique « Float_Io » avec une chaîne de caractères 328
15.4.8.2.5 Traduction du réel en une chaîne de caractères. 329
15.4.8.2.6 Élimination des espaces au début. 329
15.4.8.2.7 Remplacement de tous les chiffres non significatifs par des zéros. 329
15.4.8.2.8 Élimination des zéros non significatifs. 329
15.4.8.2.9 Affichage de la chaîne de caractères sur l'écran. 329
15.4.8.2.10 La gestion d'exception avec affichage en format scientifique. 330
15.4.8.3 Le code complet 330
16 L'algorithmique en action, exemple 2: Le codage et décodage "lzw" 331
16.1 Introduction 331
16.1.1 L'algorithme de compression 331
16.1.1.1 Initialisation de l'algorithme de compression 332
16.1.1.2 Détails de l'algorithme de compression 332
16.1.2 L'algorithme de décompression 332
16.1.2.1 Initialisation de l'algorithme de décompression 332
16.1.2.2 Détails de l'algorithme de décompression 332
16.1.3 L'algorithme LZW, détails supplémentaires 333
16.1.3.1 Exemple de codage 333
16.1.3.2 Exemple de décodage 334
16.2 Structures et opérations nécessaires 335
16.2.1 Chaînes de caractères de longueur variable 335
16.2.2 Table de hashing et opérations associées au codage 335
16.2.2.1 Initialisation de la table de hashing du codage 336
16.2.2.2 Ajout à la table de hashing du codage 336
16.2.2.3 Existe dans la table de hashing du codage 336
16.2.2.4 Lecture de la table de hashing du codage 337
16.2.3 Tableau de chaînes de caractères de longueur variable et opérations associées au décodage 337
16.2.3.1 Initialisation du tableau de chaînes de caractères du décodage 337
16.2.3.2 Ajout au tableau de chaînes de caractères du décodage 337
16.2.3.3 Existe dans le tableau de chaînes de caractères du décodage 338
16.2.3.4 Lecture du tableau de chaînes de caractères du décodage 338
16.2.4 Gestion de variables, codées sur 8, 9, 10, 11 ou 12 bits sans signe 338
16.2.5 Lecture d'un fichier par bytes, envoi au décodage et écriture codée sur 8, 9, 10, 11 ou 12 bits sans signe dans un fichier 338
16.2.5.1 Lecture des caractères depuis le fichier à coder et mise à disposition pour la routine de codage. 338
16.2.5.2 Réception des codes depuis la routine de codage, traduction en bytes et écriture dans un fichier. 340
16.2.6 Réception de variables codées sur 8, 9, 10, 11 ou 12 bits sans signe, envoi au décodage et écriture dans un fichier par bytes 341
16.2.6.1 Lecture des codes depuis le fichier à décoder et mise à disposition pour la routine de décodage. 341
16.2.6.2 Réception des caractères depuis la routine de décodage et écriture dans un fichier. 343
16.2.7 Le codage 343
16.2.8 Le décodage 344
16.3 Le code complet 345
16.3.1 Fichier Lzw_1.ads 345
16.3.2 Fichier Lzw_1.adb 346
16.3.3 Fichier Lzw_2.ads 349
16.3.4 Fichier Lzw_2.adb 350
16.3.5 Fichier Lzw_Data.ads 352
16.3.6 Fichier Lzw_Debug.ads 352
16.3.7 Fichier Lzw_Debug.adb 353
16.3.8 Fichier Lzw_Io_Compress.ads 353
16.3.9 Fichier Lzw_Io_Compress.adb 354
16.3.10 Fichier Lzw_Io_Expand.ads 358
16.3.11 Fichier Lzw_Io_Expand.adb 359
16.3.12 Fichier Lzw.adb 361
16.3.13 Fichier Linked_Hash.ads 363
16.3.14 Fichier Linked_Hash.adb 365
Ce langage a été développé pour permettre la traduction directe de l'analyse intellectuelle d'un problème dans un code sans étape intermédiaire. Il permet de coder depuis l'application la plus simple jusqu'au code orienté objet multi-tâche. Il permet la capture complète au niveau de chaque élément du code des contraintes issues de l'analyse du problème. Ada permet également un traitement efficace et élégant des erreurs survenues pendant le déroulement du programme.
La structure de base d'un langage est la grammaire (avec sa ponctuation) et le vocabulaire. La grammaire gouverne l'ordre des mots dans la ‘phrase' et le vocabulaire est défini par:
Des mots, déjà créés et connus du compilateur, dits réservés:
Leur signification est déjà fixée par le langage
Il ne peuvent pas servir de nom de variable ou de fonction / procédure :
abort else new return
abs elsif not reverse
abstract end null
accept entry select
access exception separate
aliased exit of subtype
all or synchronized
and for others tagged
array function out task
at overriding terminate
generic package then
begin goto pragma type
body private
if procedure
case in protected until
constant interface raise use
declare is range when
delay limited record while
delta loop rem with
digits renames
do mod requeue xor
Des ‘Attributs' qui définissent les propriétés des variables:
liste des principaux attributs :
access address adjacent aft
alignment all base bit_order
body_version callable caller ceiling
class component_size compose constrained
copy_sign count definite delta
denorm digits exponent external_tag
first first_bit floor fore
fraction identity image input
last last_bit leading_part length
machine machine_emax machine_mantissa machine_overflows
machine_radix machine_rounds max max_size_in_storage_elements
min model_emin model_epsilon model_mantissa
model_small modulus new output
pos pred range read
remainder round rounding_safe_first safe_last
scale scaling signed_zeros size
small storage_size succ tag
terminated truncation unbiased_rounding unchecked_access
val valid value version
wide_image wide_value wide_width width
write
Il suivent une règle bien précise (voir plus loin), et servent à identifier les variables, le noms de procédure et les noms de fonctions.
Quelques exemples (en ada il n'y a pas de différence entre majuscule et minuscule):
A
bonjour
Ma_feuille
a_1
Chaque 'phrase' se termine par ;
a := 1;
dsort(a => b);
x := 1 + X;
Ces structures (procédure, boucle, test if, ….) récursives qui s'emboîtent un peu à la façon des poupées gigognes, elles se terminent SYSTÉMATIQUEMENT par END (loop, if, ….);
Assignation :=
Addition +
Soustraction -
multiplication *
Division /
Puissance **
Et and
Ou or
Non not.
Ou exclusif xor
Supérieur >
Inférieur <
Supérieur ou égal. >=
Inférieur ou égal. <=
égal =
Différent /=
Concaténation (de tableaux, de chaîne de caractères) &
Choix multiple. (dans les «case» et les initialisation de tableaux) |
Explicitation de paramètres dans un appel fonction/procédure =>
Pour créer un programme il faut:
Créer un fichier texte résidant sur le disque de votre ordinateur, par exemple aaa.adb (noter l'extension adb, et le nom aaa) dont voici un exemple valable (qui ne fait rien !). Il est divisé en plusieurs zones:
--zone 1
With bbb;
Procedure aaa
Is
--zone 2
Begin
--zone 3
Null;
End aaa;
Les lignes de code commençant par – sont des commentaires non pris en compte.
C'est ici que l'utilisateur déclare les procédures, fonctions et packages (qui sont des collections de procédures et de fonctions) contenues dans D'AUTRES FICHIERS ou déjà incorporés au langage. Cette zone s'arrête au mot clef «procedure» (ou «package» ou «function»).
Dans cette zone on déclare:
Les types de variables.
Les variables à partir de leur type.
Des procédures et/ou fonctions à l'usage exclusif de la procédure aaa.
Les limites d'utilisation des variables et des types de variables déclarés dans des packages extérieurs en ce qui concerne la comparaison.
Les zones de validité des variables dans le cas d'usage de procédures et / ou fonctions à l'usage exclusif de la procédure aaa.
C'est là que se trouve le code source qui D O I T être segmenté en parties indépendantes. Ce code doit être ABONDAMMENT COMMENTÉ pour rendre sa logique lisible et facile à suivre.
Une fois le code écrit il faut le traduire dans un programme par un procédé qui s'effectue par:
Le compilateur vérifie d'abord que les règles de grammaire et de syntaxe sont respectées et traduit le code source en un fichier ‘objet' qui représente, pour simplifier, le code source en un code machine intermédiaire.
Le linker transforme le fichier objet et tous les fichiers objet associés dans la zone 1 (et ceux implicitement associes) en un programme que l'on peut exécuter. Il faut, dés le début, prendre comme habitude de créer des structures complètes et de demander SYSTÉMATIQUEMENT AU COMPILATEUR DE TESTER LA SYNTAXE.
L'utilisation d'un éditeur spécialisé, et de conception moderne, permet en outre une productivité accrue, et facilite beaucoup l'apprentissage.
Les possibilités de l'éditeur doivent comprendre:
Copier/coller par raccourci clavier.
Macro pour automatiser les tâches d'édition répétitives.
Codage coloré pour les différentes partie de la syntaxe.
Sauvegarde automatique de l'état d'avancement du travail.
Retour au même endroit après fermeture et ouverture de l'éditeur.
Mise en forme automatique du code pour faciliter la lecture.
Une grammaire élémentaire pour faciliter la recherche des erreurs de syntaxe.
Tous les détails seront expliqués plus tard, voici le texte du code stocké dans le fichier premier.adb et le résultat après compilation, link et exécution:
with Ada.Text_Io;
procedure Premier
is
Resultat : Integer;
begin
Resultat := 2;
Resultat := Resultat ** 10;
Ada.Text_Io.Put_Line("2 puissance 10 = " & Integer'Image(Resultat));
end Premier;
La compilation se fait directement depuis l'éditeur ou en ouvrant un fenêtre «dos» ou «xterm» selon l'O.S. employé, puis en utilisant la commande
gnatmake premier
Utilisation du compilateur GNAT (fenêtre DOS sous windows)
F:\ada\current\w\miage>gnatmake premier
gcc -c premier.adb
gnatbind -x premier.ali
gnatlink premier.ali
F:\ada\current\w\miage>
et le résultat:
F:\ada\current\w\miage>premier
2 puissance 10 = 1024
F:\ada\current\w\miage>
Utilisation du compilateur GNAT (fenêtre xterm sous linux)
[daniel@localhost miscellaneous]$ gnatmake premier
gcc -c premier.adb
gnatbind -x premier.ali
gnatlink premier.ali
[daniel@localhost miscellaneous]$ ./premier
2 puissance 10 = 1024
[daniel@localhost miscellaneous]$
D'autres compilateurs peuvent aussi être utilisés (Janus, Aonis,……)
Quelques détails pour les curieux:
Sauf entre " et " les espaces et les retours à la ligne sont ignorés et sont seulement utilisés pour rendre le code lisible.
Tous les langages de programmation modernes imposent une déclaration préalable des variables (basic n'est pas, de ce point de vue, un langage moderne).
ADA impose les règles suivantes pour la création de nom (variable, type de variable, nom de fonction de procédures de package, …….)
Les règles pour inventer un nom valable (identifier) sont décrites comme suit:
Identifier ::= identifier_letter { [ underline ] letter_or_digit }
letter_or_digit ::= identifier_letter | digit
identifier_letter ::= upper_case_identifier_letter | lower_case_identifier_letter
digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
underline ::= '_'
Les abréviations (généralement utilisées en algorithmique pour décrire une grammaire) sont les suivantes:
{ xxxxxx } veut dire 0 à n répétitions de xxxxxx
[ yyyyyyy ] veut dire yyyyyy est optionnel
| veut dire ou
::= veut dire défini par
upper_case_identifier_letter veut dire toutes les lettres majuscules de A à Z
lower_case_identifier_letter veut dire toutes les lettres Minuscules de a à z
Cette définition servira, dans un exercice, à illustrer la capacité du langage à exprimer directement un problème posé (illustration simple d'un automate à états finis contre des fonctions récursives avec test if imbriqués et boucles ) .
Ada permet de représenter tous les objets que l'on peut rencontrer dans l'écriture d'un programme.
Certains types de variables sont déjà prédéfinis: les nombres, les tableaux et matrices, les caractères, les chaînes de caractères.
D'autres sont crées par l'utilisateur selon ses besoins.
Nous allons voir systématiquement comment effectuer ces déclarations.
Chaque type a des attributs qui lui sont associés et décrivent ses propriétés. C'est un point important du langage car ces ‘attributs' participent à l'écriture du code en facilitant sa compréhension et sa sûreté de fonctionnement
Dans ce cas on sait que la valeur ne changera pas (d'ailleurs le compilateur y veillera)
Charge_de_rupture : constant := 8.0 * 1024.0;
Max : constant := 500;
Taille_de_tableau : constant := Max / 6
memory : constant := 2 ** 16;
a, b ,c : constant := 1;
Le mot clé constant sera utilisé dans toutes les déclarations si le contenu de l'objet ne doit pas être modifié dans le programme.
Un type entier est prédéfini et s'appelle integer (ou long_integer)
Ce sont deux types prédéfinis avec les limites suivantes (peuvent varier d'un compilateur à l'autre et aussi en fonction du CPU)
Integer varie de -232 à 232 -1 (par exemple)
Long_integer varie de -264 à 264 -1 (par exemple);
L'instruction:
Z: integer;
Défini une variable dont le nom est Z pour stocker des valeurs de -232 à 232 -1 (par exemple). Cette variable n'a pas de valeur stockée connue (ATTENTION!!!!!!! selon les compilateurs elle peut contenir une valeur au hasard ou 0). La base mathématique utilisée pour représenter le nombre peut être différente de 10.
X : Long_integer := 15; -- Notation Décimale
Y : Long_integer := 2#1111#; -- Notation Binaire (base 2)
W : Long_integer := 8#77#; -- Notation Octale (base 8)
Z : Long_integer := 16#F#; -- Notation Hexadécimale (base 16)
Défini des variables dont le nom est X, Y et Z pour stocker des valeurs de -264 à 264 -1 (par exemple). X, Y, W et Z ont la même valeur 15. Observez bien la notation dans des bases autres que 10.
On peut aussi définir un nouveau type d'entier, non compatible directement avec les deux types prédéfinis précédents.
Type my_integer_8 is range 1 .. 8;
Défini un NOUVEAU type contenant des entiers dont le nom est my_integer_8 pour stocker des valeurs de –1 à 8.
Ce type est différent du type integer prédéfini par le compilateur!!!!!!
A_1: my_integer_8:= my_integer_8'first;
La variable A_1 est du type my_integer_8 (entier différent de integer!!! car créé spécialement). Elle a comme valeur 1 noter l'utilisation de l'attribut FIRST qui équivaut à la plus petite valeur possible.
C'est un type d'entier sans signe avec une arithmétique appropriée (qui comprend les opérateurs logiques or, and, et xor bit à bit):
Type my_modulo is mod 4;
Les variables de ce type peuvent avoir les valeurs 0, 1, 2 et 3. Avec les particularités suivantes:
3 + 1 donne 0
0 – 1 donne 3
de même pour * et /
mod_4: my_modulo:= my_modulo'last;
Cette instruction définit une variable dont le nom est Mod_4 pour stocker des valeurs de 0 à 3. Cette variable a la valeur 3, noter l'utilisation de l'attribut Last qui équivaut à la plus grande valeur possible pour les variables de type my_modulo.
Modular := 2 and 3; -- modular est égal à 2
Modular := 2 or 3; -- modular est égal à 3
Modular := 2 xor 3; -- modular est égal à 1
Modular := 3 + 1; -- modular est égal à 0
l'utilisation d'un masque permet de travailler au niveau des bits.
Modular := 2#10# and Mod_4 ; -- modular est égal à 2
Les opérations suivantes sont utilisées de façon courante:
Décalage de N bits à gauche.
Modular := Modular * 2**N;
Décalage de N bits à droite.
Modular := Modular / 2**N;
Effacement de bits avec le masque 02#0110#.
Modular := Modular and 02#0110#;-- ici les bits 1 et 4 sont effacés (remplacés par des zéros).
Mise à 1 de bits d'un mot avec le masque 02#0110#.
Modular := Modular or 02#0110#;-- ici les bits 1 et 4 sont remplacés par des uns).
Permutation circulaire des bits d'un mot de N fois à droite.
Modular := (Modular *2**N) or (Modular /2**(Modular'size-N));-- Modular'size = 4 étant le nombre de bits de modular
L'utilisation de l'hexadécimal permet une lecture plus simple si le nombre de bits est élevé:
Type modulo_64 is mod 2**64 ; - donne une variable avec 64 bits soit 16 chiffres hexadécimal.
Z: modulo_64;
.....
z:= z and 16#F000000000000000#;--ne garde que les 4 bits de poids le plus élevé de Z;
z := (z *2**N) or (z /2**(z'size-N));-- permutation circulaire
C'est un subtype (voir plus loin) prédéfini avec les limites suivantes (peuvent varier d'un compilateur à l'autre et aussi en fonction du CPU)
Positive varie de 1 à 232 -1 (par exemple)
A_M_0: Positive := Positive'First;
Cette instruction définit une variable dont le nom est A_M_0 pour stocker des valeurs de 1 à 232 -1 (par exemple). Cette variable a la valeur 1, noter l'utilisation de l'attribut First qui équivaut à la plus petite valeur possible pour les variables de type Positive.
C'est un subtype (voir plus loin) prédéfini avec les limites suivantes (peuvent varier d'un compilateur à l'autre et aussi en fonction du CPU)
Natural varie de 0 à 232 -1 (par exemple)
M_0: Natural := Natural'First;
Cette instruction définit une variable dont le nom est M_0 pour stocker des valeurs de 1 à 232 -1 (par exemple). Cette variable a la valeur 0, noter l'utilisation de l'attribut First qui équivaut à la plus petite valeur possible pour les variables de type Natural.
En plus des attributs first et last il existe aussi, entre autres, des attributs qui peuvent entre autre s'appliquer aux entiers :
Subtype test_type is integer range 1..5;
X: Test_Type:=2;
Y: Test_Type:=5;
Z: Test_Type;
Image
Transforme le contenu de la variable en un chaîne de caractères (principalement pour l'impression).
Test_type'image(x) renvoie le caractère 2 (avec un espace devant pour le signe +). Bien entendu, il existe dans le langage des méthodes permettant de contrôler le format des impressions.
Value
C'est l'inverse de la précédente, il transforme des caractères en un entier.
Z:= Test_type'Value(5);
Z contient maintenant 5.
Min
Permet de garder le minimum de deux variables
Z:= Test_type'Min(X,Y);
Z contient maintenant 2.
Max
Permet de garder le Maximum de deux variables
Z:= Test_type'Max(X,Y);
Z contient maintenant 5.
Range
C'est un attribut très utile qui équivaut à l'étendue des valeurs du type, il sert pour la logique, les tests et les boucles.
2 in Test_type'range renvoie vrai
Il est peu pratique (les types sont en effet incompatibles entre eux voir 3.2.3.2) de créer autant de types différents d'entiers que nécessaire pour couvrir les différences de nature des objets que l'on veux représenter. C'est pourquoi il existe un mécanisme pour limiter les bornes à partir d'un type (prédéfini ou créé).
Subtype eee is my_integer_8 range 2 .. 5;
Cela donne un type dont le nom est eee compatible avec my_integer_8 mais limité à une plage de valeurs plus petite.
M_0: eee:= eee'last;
Cette instruction définit une variable de type eee dont le nom est M_0 pour stocker des valeurs de 2 à 5. Cette variable a la valeur 5, noter l'utilisation de l'attribut LAST qui équivaut à la plus grande valeur possible pour les variables de type eee.
Type fruit_number_type is range 1..100;
[RAPPEL]
Cette instruction défini un NOUVEAU type contenant des entiers dont le nom est fruit_number_type pour stocker des valeurs de 1 à 100
Ce type est différent du type integer prédéfini par le compilateur!!!!!!
Vous ne pouvez pas les mélanger dans une même instruction, le compilateur vous donne un message semblable à:
xxx.adb:12:18: expected type "fruit_number_type" defined at line …
xxx.adb:12:18: found type "Standard.Integer"
gnatmake: "premier.adb" compilation error
Ce type peut servir à définir des type dérivés:
Type apple_number_type is new fruit_number_type;
Type grapefruit_number_type is new fruit_number_type;
Ces instructions définissent DEUX NOUVEAU types contenant des entiers dont les nom sont apple_number_type et grapefruit_number_type pour stocker des valeurs de 1 à 100. Ces deux types sont différent du type fruit_number_type mais ont conservé ses limites de valeurs de 1 à 100.
apple_number: apple_number_type:= apple_number_type'first;
Cette instruction définit une variable de type apple_number_type dont le nom est apple_number pour stocker des valeurs de 1 à 100. Cette variable a la valeur 1, noter l'utilisation de l'attribut First qui équivaut à la plus petite valeur possible pour les variables de type apple_number_type.
grapefruit_number: grapefruit_number_type:= grapefruit_number_type;
Cette instruction définit une variable de type grapefruit_number_type dont le nom est grapefruit_number pour stocker des valeurs de 1 à 100. Cette variable n'a pas de valeur connue!!..
Les types de variables définis dans un package (c'est une collection de fonctions et de procédures)
peuvent et sont utilisés dans les procédures et fonctions qui leur font référence dans la ZONE 1 (voir plus haut). Le concepteur du programme DOIT décider de l'utilisation par l'extérieur des variables des types qu'il définit.
Type example is private;
……
Private
……
Type example is integer range 1..2;
Les variables de ce type, déclarées à l'extérieur du «package» ne pourront PAS y être manipulées, seuls l'opérateur d'assignation (:=) est autorisé.
Les types de variables définis dans un package (c'est une collection de fonctions et de procédures)
Peuvent et sont utilisés dans les procédures et fonction qui leur font référence dans la ZONE 1 (voir plus haut). Le concepteur du programme DOIT décider de l'utilisation par l'extérieur des variables des types qu'il définit.
Type example is limited private;
……
Private
……
Type example is integer range 1..2;
Les variables de ce type, déclarées à l'extérieur du «package» ne pourront PAS y être manipulées. L'utilisateur ne connaîtra pas les détails internes à ce type. Les objets de ce type sont en quelque sorte des mot de passe . Leur manipulation PASSERA OBLIGATOIREMENT par les procédures déclarées dans ce «package».
type rr: aliased integer:=0;
Les variables de ce type pourront avoir leur adresse mémoire accessible par un pointeur.
Les réels sont utilisée pour représenter les nombres avec décimales.
type Real is digits 2;
Cette instruction défini un NOUVEAU type contenant des réels dont le nom est real pour stocker des valeurs avec deux chiffres après la virgule la limite minimum et maximum ne sont pas précisées.
Le format de stockage des nombres dans la mémoire limite dans la réalité entre -3.40 10+38 et 3.40 10+38.
type Coefficient is digits 10 range –1.0 .. 1.0;
Cette instruction défini un NOUVEAU type contenant des réels dont le nom est real pour stocker des valeurs avec dix chiffres après la virgule. la limite minimum et maximum est -1.0000000000 et 1.0000000000 .
AAA: Real;
BBB: Coefficient := Coefficient'last;
Ces instruction déclarent deux variables correspondant aux type ci-dessus:
AAA peut représenter des nombres réels à deux décimales et n'est pas initialisée.
BBB peut représenter des nombres réels à dix décimales, est initialisée à 1.0000000000 et est limitée entre –1 et 1.
Trois type prédéfinis Float, Long_Float et Long_Long_Float existent avec les spécifications suivantes
X: float := 0.0;
X peut varier entre -3.40282E+38 et 3.40282E+38 avec 7 chiffres significatifs (stockage mémoire sur 32 bits).
Y: long_float := 0.0;
X peut varier entre -1.79769313486232E+308 et 1.79769313486232E+308 avec 16 chiffres significatifs (stockage mémoire sur 64 bits).
type Volt is delta 0.125;
Cette instruction défini un type contenant des réels dont le nom est volt pour stocker des valeurs arrondi à 0.125 prés la limite minimum et maximum ne sont pas précisées.
Par exemple 1 1.125 1.250 1.275 etc. …. Ce peut être utilisé pour stocker des valeurs de mesure physique pour lesquelles la précision de mesure est connue ( le voltmètre a une précision de 0.125 V dans cet exemple).
type Money is delta 0.01 digits 15;
Cette instruction défini un type contenant des réels dont le nom est Money pour stocker des valeurs arrondi à 0.01 près la limite minimum et maximum ne sont pas précisées. Par exemple 1 1.125 1.250 1.275 etc. …. Ce peut être utilisé pour stocker des valeurs de physique pour lesquelles la précision de mesure est connue (le solde de votre compte en banque). 15 chiffres significatifs sont exigés pour les calcul (financiers dans cet exemple);
subtype Salary is Money digits 10;
C'est un exemple pour gérer les salaires des employés en modifiant une des caractéristiques.
En plus des attributs applicables aux type entiers , il existe des attributs plus spécifiques:
C'est un type prédéfini qui peut avoir deux valeurs: True ou False.
D: Boolean:= False;
C'est un type prédéfini qui peut stocker un caractère;
A_char: character:= ' ';
A_char est initialisé avec un ESPACE!!!
Tous les caractères sont stockés en binaire par groupe de 8 bits selon un tableau. Ce tableau est prédéfini en ada . Par exemple:
A_char := Ascii.bs;
Par exemple ^A signifie la clé ctrl et A en même temps
Dec Hex Char clavier Dec Hex Char clavier Dec Hex Char Dec Hex Char
--- --- ---- ------ --- --- ---- ------- --- --- ---- --- --- ----
000 00 NUL ^@ 018 12 DC2 ^R 036 24 $ 054 36 6
001 01 SOH ^A 019 13 DC3 ^S 037 25 % 055 37 7
002 02 STX ^B 020 14 DC4 ^T 038 26 & 056 38 8
003 03 ETX ^C 021 15 NAK ^U 039 27 ' 057 39 9
004 04 EOT ^D 022 16 SYN ^V 040 28 ( 058 3A :
005 05 ENQ ^E 023 17 ETB ^W 041 29 ) 059 3B ;
006 06 ACK ^F 024 18 CAN ^X 042 2A * 060 3C <
007 07 BEL ^G 025 19 EM ^Y 043 2B + 061 3D =
008 08 BS ^H 026 1A SUB ^Z 044 2C , 062 3E >
009 09 HT ^I 027 1B ESC ^[ 045 2D - 063 3F ?
010 0A LF ^J 028 1C FS ^\ 046 2E . 064 40 @
011 0B VT ^K 029 1D GS ^] 047 2F / 065 41 A
012 0C FF ^L 030 1E RS ^^ 048 30 0 066 42 B
013 0D CR ^M 031 1F US ^_ 049 31 1 067 43 C
014 0E SO ^N 032 20 SP 050 32 2 068 44 D
015 0F SI ^O 033 21 ! 051 33 3 069 45 E
016 10 DLE ^P 034 22 " 052 34 4 070 46 F
017 11 DC1 ^Q 035 23 # 053 35 5 071 47 G
Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char
--- --- ---- --- --- ---- --- --- ---- --- --- ----
072 48 H 090 5A Z 108 6C l 126 7E ~
073 49 I 091 5B [ 109 6D m 127 7F
074 4A J 092 5C \ 110 6E n 128 80
075 4B K 093 5D ] 111 6F o 129 81
076 4C L 094 5E ^ 112 70 p 130 82
077 4D M 095 5F _ 113 71 q 131 83
078 4E N 096 60 ` 114 72 r 132 84
079 4F O 097 61 a 115 73 s 133 85
080 50 P 098 62 b 116 74 t 134 86
081 51 Q 099 63 c 117 75 u 135 87
082 52 R 100 64 d 118 76 v 136 88
083 53 S 101 65 e 119 77 w 137 89
084 54 T 102 66 f 120 78 x 138 8A
085 55 U 103 67 g 121 79 y 139 8B
086 56 V 104 68 h 122 7A z 140 8C
087 57 W 105 69 i 123 7B { 141 8D
088 58 X 106 6A j 124 7C | 142 8E
089 59 Y 107 6B k 125 7D } 143 8F
Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char
--- --- ---- --- --- ---- --- --- ---- --- --- ----
144 90 162 A2 ¢ 180 B4 Ž 198 C6 Æ
145 91 163 A3 £ 181 B5 µ 199 C7 Ç
146 92 164 A4 € 182 B6 ¶ 200 C8 È
147 93 165 A5 ¥ 183 B7 · 201 C9 É
148 94 166 A6 Š 184 B8 ž 202 CA Ê
149 95 167 A7 § 185 B9 ¹ 203 CB Ë
150 96 168 A8 š 186 BA º 204 CC Ì
151 97 169 A9 © 187 BB » 205 CD Í
152 98 170 AA ª 188 BC Œ 206 CE Î
153 99 171 AB « 189 BD œ 207 CF Ï
154 9A 170 AA ª 188 BC Œ 206 CE Î
153 99 172 AC ¬ 190 BE Ÿ 208 D0 Ð
155 9B 173 AD 191 BF ¿ 209 D1 Ñ
156 9C 174 AE ® 192 C0 À 210 D2 Ò
157 9D 175 AF ¯ 193 C1 Á 211 D3 Ó
158 9E 176 B0 ° 194 C2 Â 212 D4 Ô
159 9F 177 B1 ± 195 C3 Ã 213 D5 Õ
160 A0 178 B2 ² 196 C4 Ä 214 D6 Ö
161 A1 ¡ 179 B3 ³ 197 C5 Å 215 D7 ×
Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char
--- --- ---- --- --- ---- --- --- ---- --- --- ----
216 D8 Ø 234 EA ê 252 FC ü
217 D9 Ù 235 EB ë 253 FD ý
218 DA Ú 236 EC ì 254 FE þ
219 DB Û 237 ED í 255 FF
220 DC Ü 238 EE î
221 DD Ý 239 EF ï
222 DE Þ 240 F0 ð
223 DF ß 241 F1 ñ
224 E0 à 242 F2 ò
225 E1 á 243 F3 ó
226 E2 â 244 F4 ô
227 E3 ã 245 F5 õ
228 E4 ä 246 F6 ö
229 E5 å 247 F7 ÷
230 E6 æ 248 F8 ø
231 E7 ç 249 F9 ù
232 E8 è 250 FA ú
233 E9 é 251 FB û
Il arrive souvent que l'on désire représenter dans un projet, des objets définis par leur nom. Par exemple: des fruits peuvent être de oranges, des pommes, des poires, des cerises, etc. Dans les langages anciens, il fallait coder ces espèces de fruits par des nombres: c'est peu pratique, sujet à des erreurs et ne respecte pas les principes de la programmation orientée objet. Ada propose un type spécial:
Type fruit_type is (orange, apple, pea, cherry);
A_fruit:fruit_type :=fruit_type'last ;
A_fruit est une variable pouvant avoir seulement les valeurs déclarées { orange ou apple ou …} dans le type et initialisé à cherry.
Les tableaux (les matrices étant une généralisation des tableaux) permettent de stocker de nombreuses données de nature identique (de même type).
Chaque donnée individuelle est affectée d'un indice qui permet de les différencier de toutes les autres.
La notation traditionnelle s'écrit comme suit: X(i) ou x est le nom du tableau et i l'indice affecté à la valeur représenté par X(i).
Il faut IMPÉRATIVEMENT distinguer :
Le type de variable contenu dans le tableau (la matrice).
Le type de l'indice utilisé. Dans le cas des matrices les 2 indices peuvent être de nature différente !!
Le type de variable contenu dans le tableau et le type de l'indice utilisé peuvent (ou non) être les mêmes. La limitation pour les indices est que leur nombre soit limité (pas de flottants par exemple).
L'opérateur de concaténation noté & permet de nombreuses opérations sur des fragments de tableaux. Il faut donc décider lors de l'analyse intellectuelle si ces possibilités seront utilisées (uniquement possible sur plusieurs tableaux de même type) car la déclaration pourra être différente ;
A cet effet il existe plusieurs méthode de déclaration de type de tableau. Il faut TOUJOURS commencer par déclarer les types de contenu et d'indice (sauf emploi des types prédéfinis par le langage).
Un tableau
contenant des entiers de 1 à 100
indice entier de -2 à +3
Type U_Type is array (- 2 .. 3) of Integer range 1 .. 100;
U : U_Type := (others => 1);
C'est la plus directe: le type de tableau est déclaré de façon explicite. Le tableau: variable de type tableau_type initialisé a 0 pour toutes les valeurs des indices{ -2, -1, 0, 1, 2 et 3}. L'inconvénient est qu'aucun type ne correspond ni au contenu ni à l'indice! Il n'est donc pas possible de déclarer des variables pour les représenter sans courir le danger de dépasser les limites des valeurs.
Un tableau
contenant des booléens
indice entier de -2 à +3
Type indice_type is new integer range –2..3;
Type tableau_type is array(indice_type'range) of boolean;
Tableau : tableau_type := (others=>true);
Indice : indice_type;
Contenu : boolean;
On défini
le contenu
l'indice
le type de tableau
On déclare le tableau: variable de type tableau_type initialisé à «true» pour toutes les valeurs des indices{ -2, -1, 0, 1, 2 et 3}.
Autre exemple:
Un tableau
contenant des booléens
indice fruit_type
Type fruit_type is (orange,apple,pear, cherry);
Type tableau_type is array(fruit_type'range) of boolean;
Tableau : tableau_type:=(others => true);
Indice : indice_type;
Contenu : boolean;
Autre exemple
Un tableau
contenant des fruit_type
indice booléen
Type fruit_type is (orange,apple,pear, cherry);
Type tableau_type is array (boolean) of fruit_type;
Tableau : tableau_type:= (others => apple);
Indice : boolean;
Contenu : fruit_type ;
Un tableau
contenant des entiers de 1 à 2
indice entier de -2 à +3
Subtype contenu_type is integer range 1..2;
Subtype indice_type is integer range –2..3;
Type tableau_type is array(indice_type'range) of contenu_type;
Tableau : tableau_type:=(others => 1);
Indice : indice_type;
Contenu : contenu_type;
On défini le contenu, l'indice, le type de tableau , le tableau: variable de type tableau_type initialisé à 0 pour toutes les valeurs des indices{ -2, -1, 0, 1, 2 et 3}.
L'un des inconvénients est que la concaténation est limitée aux tableaux dont l'indice varie entre –2 et +3.
Autre exemple avec le même résultat
Un tableau
contenant des entiers de 1 à 2
indice entier de -2 à +3
Subtype contenu_type is integer range 1..2;
Subtype indice_type_a is integer range –2..3;
Subtype indice_type_b is integer range 10..20
Type tableau_type is array(integer range <>) of contenu_type;
Tableau_a : tableau_type(indice_type_a'range) := (others => 1);
Tableau_b : tableau_type(indice_type_b'range) := (others => 1);
Maintenant tableau_a ET tableau_b sont de MÊME TYPE.
De cette façon on pourra écrire:
tableau_a(-2..0) := tableau_b(15..17);
Il y a aussi d'autres avantages que nous verrons plus loin.
Dans la programmation avec des tableaux à N dimensions {matrices} il est particulièrement facile de mélanger lignes et colonnes si les deux indices sont de même type.
Les méthodes pour déclarer les tableaux se généralisent aux matrices.
Nous nous limiterons à quelques exemples:
Une matrice
contenant des entiers de 1 à 200
premier indice entier de -20 à +39
second indice entier de 10 à +20
Subtype contenu_type is integer range 1..200;
type indice_type_a is NEW integer range –20..39;
type indice_type_b is NEW integer range 10..20;
Type matrice_type is array(integer range <>,
integer range <>) of contenu_type;
Matrice:matrice_type(indice_type_a'range,indice_type_b'range):=(others =>(others => 1));
indice_a : indice_type_a;
indice_b : indice_type_b;
La généralisation des écritures permet d'écrire
x := matrice(indice_a,indice_b);
La ligne de code
x := matrice(indice_b,indice_a);
Sera REJETÉE par le compilateur.
Une matrice
contenant des fruits
premier indice entier de -20 à +39
second indice booléen
initialisée à apple
Type fruit_type is (orange,apple,pear, cherry);
type indice_type is NEW integer range –20..39;
Type matrice_type is array(indice_type range <>,boolean range <>) of fruit_type;
Matrice: matrice_type(indice_type'range,boolean):= (others => (others => apple));
Une chaîne de caractères est un type prédéfini par le langage: le type string. C'est un tableau de caractères avec un indice entier.
Sa définition POURRAIT s'écrire: array (positive range <>) of character;
Il possède toutes les caractéristiques des tableaux et en particulier, utilise l'opérateur &.
Toutes sortes de fonctions et procédures prédéfinies par le langage permettent de rechercher un mot, de transformer en minuscule, en majuscule, de traduire certaines lettres en d'autre lettres …….
Les bornes s'écrivent avec "
Chine: string (1..10);
R : constant string :="bonjour"
V : string (1..10) :="01234456789";
-- attention!!! le compilateur compte les caractères il en faut 10
Subtype my_string_type is string(1..50);
my_string : my_string_type := (others => ' ‘);--initialisée avec 50 espaces
A_string : my_string_type := (others => 'X‘);--initialisée avec 50 X
Décrivons un exemple de variables associées (par exemple appartenant au même objet). Le nom d'un article et son prix.
Type nom_type is (chaise,table,lampe,assiette);
Type prix_type is delta 0.10 digits 10 range 0.0 .. 1000.0;
Type article_type is record
Nom : nom_type := nom_type'first;
Prix : prix_type:= prix_type'first;
End record;
Article: article_type;
Le record 'capture' les propriétés de l'objet de l'exemple. La variable article a deux composants qui peuvent être d'un accès individuel:
article.Nom := 10.51;
article.article := Lampe;
ou collectif:
article := (nom => table, prix => 20.0);
Bien que pratique dans les cas simples, il n'est pas adapté à des problèmes plus sophistiqués.
Il est possible d'inclure un tableau et sa taille dans un record:
type Storage_Array_Type is array (Long_Integer range <>) of integer;
type Storage_Type(Size : Long_Integer := Initial_Array_Size)
is
record
Storage_Array : Storage_Array_Type(0 .. Size);
Current_Data_Position : Long_Integer := 0;
Current_Last_Position : Long_Integer := 0;
Bookmark_Position : Long_Integer := 0;
end record;
tableau: Storage_Type(Size => 10); -- initialisé avec un tableau d'indice 0 à 10;
Souvent les propriétés détaillées des articles sont différentes: il n'ont pas tous une couleur, se vendent au cageot, à la pièce ,….
Type nom_type is (chaise,table,pomme,poire,banane);
Type prix_type is delta 0.10 digits 10 range 0.0 .. 1000.0;
Subtype stock_type is integer range 1..100;
Type couleur_type is (blanc, brun, noir);
Type article_type (Article : nom_type := nom_type'first )
is
record
--c'est la partie commune à tous les articles :
stock: stock_type := stock_type'first;
-- partie spéciale pour table chaise et lampe qui sont compatibles : vente à l'unité et couleur
case Article
is
when chaise | Table | Lampe=>
prix_unitaire : prix_type := prix_type'first;
couleur : couleur_type := couleur_type'first;
when Pomme | poire =>
prix_au_kilo : prix_type := prix_type'first;
prix_pour_un_cageot : prix_type := prix_type'first;
when banane =>
prix_a_la_piece : prix_type := prix_type'first;
prix_pour_un_regime : prix_type := prix_type'first;
End case;
End article_type;
Type banana_type is article_type (Article=>banana);
Banana : banana_type;
.....
Banana. prix_pour_un_regime := …….;
Ici on distingue pour les différents articles la façon de les vendre: à l'unité, au kilo, par cageot ou par régime.
On se rapproche de la programmation ‘objet' mais il manque l'héritabilité (entre autres).
On peut aussi, comme illustré plus haut définir un «subtype» limité au bananes.
L'un des concepts utilisés lors de la création du langage ADA a été de garder une représentation statique du problème aussi longtemps que possible. La conséquence est que l'analyse de la robustesse et de la validité du code peut être faite par le compilateur: si un programme compile, et si l'analyse du problème a été bien faite il fonctionnera sans problème. De plus, pendant son exécution, les idées incorporées dans le code seront testées (valeur des variables, indice de tableau compris dans les limites déclarées …). En effet certains langages concurrents, basés sur une utilisation préalable importante de l'assembleur utilise les pointeurs et l'arithmétique sur ceux–ci à un très grand degré. Cela même si ce n'est pas utile.
Les pointeurs en ada permettent un accès indirect à un «objet» en représentant l'adresse de cet objet qui peut être une variable, une procédure, une fonction ou une tâche. Il permettent, et c'est leur fonction la plus importante, la création dynamique d'objets n'existant pas quant le programme commence son exécution. Par exemple:
Des objets peuvent être crées et détruits d'une manière imprévisible au moment de l'exécution du code (tampons dans un logiciel de messagerie, tâche opérant en parallèle, etc).
Plusieurs variables (de noms différents) peuvent représenter le même objet (pour faciliter l'abstraction et la lecture du code selon le contexte envisagé).
Les relations entre les objets peuvent changer avec le temps (les fils gauche et droits d'un arbre si on ajoute un composant ou on restructure l'arbre).
Stocker des données de nature différente dans la même structure (entiers, réels et chaînes de caractères dans un même tableau).
Dés que l'objet n'est plus utile, il faut libérer la mémoire correspondante en utilisant l'instantiation d'un générique approprié !!!!
par exemple :
procedure Free is new Ada.Unchecked_Deallocation(Object => String,
Name => String_access_type);
Créons un exemple de type de pointeur à partir du type prédéfini string:
Type String_access_type is access all string;
Cet exemple permet d'illustrer quelques emplois possibles, créons une variable qui est le pointeur:
String_access: String _access_type := null;
Le mot clé null indique que le pointeur représenté par la variable string_access est vide et ne contient pas d'information valable. Le test d'un pointeur pour la valeur null est d'emploi courant (if null=string_access then ...).
Another_string_access : String _access_type:= new String'("bonjour");
--Ou alors
Another_string_access : String _access_type := null;
........
Another_string_access := new String'("bonjour");
C'est la création dynamique d'une chaîne de caractères, la variable Another_string_access pointe vers cette chaîne qui est accessible en utilisant l'opérateur de dé-référence .all :
Another_string_access.all est égal à "bonjour".
Ici, on ne défini pas la longueur de la chaîne de caractères à stocker, c'est un avantage primordial, le début et la fin sont accessibles par Another_string_access.all'first et Another_string_access.all'last:
Type String_array_type is arrray (1 .. 3) of String_access_type;
String_array : String_array_type := (1 => new'("01"),
2 => new'("01234"),
3 => new'("1"));
Cet autre exemple permet de construire un tableau de chaînes de caractères de longueurs différentes.
Il existe également la possibilité de gérer le stockage associé à ces pointeurs.
Par example:
with Ada.Unchecked_Deallocation;
…
…
procedure x …
Type String_access_type is access all string;
X : String_access_type := null;
procedure Free is new Ada.Unchecked_Deallocation(Object => String,
Name => String_access_type);
…
Begin
………
X := new string'("ee");
……
Free (x);
……
L'une des applications les plus intéressantes des pointeurs est l'utilisation de records dont un des éléments est un pointeur sur ce même record. Cela pose le problème de l'œuf et de la poule: Chaque déclaration de l'un des deux types (le pointeur ou le record) dépend de l'autre déclaration qui est à la ligne suivante et donc encore inconnue du compilateur. Inverser les deux lignes ne change rien au problème: la poule précède l'œuf ou l'œuf précède la poule la question n'est pas résolue!!.
Le langage ADA résous le problème en ne précisant pas tout de suite la nature du type vers lequel pointeront les variables du type pointeur.
type a_record;
La nature exacte de ce type n'est pas encore définie …..
type record_access is access a_record;
Cette déclaration est incomplète!
type A_record
is
record
Value : Integer;
Pointeur_to_next_record : record_access:=null;
end record;
La déclaration du record est complétée et contient un pointeur sur lui-même qui permettra d'accrocher dynamiquement des nouveaux records selon les besoins. Par exemple:
Record:A_Record;
..
Record := new A_Record'(Value => Data,
Pointeur_to_next_record => Record);
Examinons un exemple de déclaration:
type B is record
Plus : integer :=0;
Moins: integer :=0;
Egal : integer :=0;
end record;
type Access_To_B is access B;
X: Access_To_B;
Cela indique que tout nouvel objet de type Access_To_B créé par 'new B' sera initialisé tel que Plus, Moins et Egal seront à 0.
L'allocation peut aussi avoir une initialisation explicite:
X:= new B'(Plus => 2,
Moins => 3,
Egal => 5);
Supposons l'utilisation d'un tableau dynamique tel que :
type Tableau_Type is array (integer range <>) of integer;
type Access_To_Tableau is access Tableau_Type;
pendant la création aucune borne n'est nécessaire:
V: Access_To_Tableau;
W: Access_To_Tableau;
Au moment de stocker des valeurs, la longueur doit être précisée:
V:= new Tableau_Type(3..19);
V:= new Tableau_Type(1..7);
le dé-référencement s'écrit alors
V.all(i);
X.Plus.all
Attention!! les assignations n'ont pas le même sens et ne conduisent pas au résultat à priori évident:
les valeurs deviennent identiques
X.all:=Y.all;
et
les pointeurs vont au même endroit
X:=Y;
sont différentes!!!
Les test peuvent s'écrivent:
if X = null -- aucun objet
then
.....
end if;
if X.all = Y.all -- valeurs pointées égales
then
.....
end if;
Quant le pointeur et son stockage associé ne sont plus nécessaires, il faut restituer la mémoire correspondante pour éviter les "fuites de mémoire". Par exemple:
déclaration du pointeur
type access_to_string is access all string;
instantiation de la procédure de restitution de la mémoire correspondante
procedure Free_Node is new Ada.Unchecked_Deallocation(Object => string,
Name => access_to_string);
déclaration du pointeur
A: access_to_string;
assignation du contenu du stockage associé
A:=new string'("abc");
restitution de la mémoire correspondante
Free_Node(A);
assignation à une nouvelle valeur
A:=new string'("cde");
Il n'est pas possible d'écrire de grandes applications sans utiliser les techniques modernes de programmation. Nous allons prendre l'exemple du magasin qui vend:
Des tables, des chaises, des oranges et des bananes.
Plusieurs opérations s'appliquent à ces objets (au sens propre et programme) vendus dans ce magasin. Mais, leurs différences sont suffisante (durée de stockage, unité de vente, unité de stockage ….) pour justifier un traitement différent:
La durée de stockage ne s'applique pas aux tables et chaises
L'unité de vente peut être au kilo pour les oranges et les bananes
Une option d'unité de vente: au cageot pour les oranges, au régime pour les bananes avec des prix différents du prix au kilo;
La durée maximum de stockage est très différente entre les oranges (durée longue) et les bananes (durée courte).
Le délai de commande peut être significatif pour l'ameublement mais n'a pas de sens pour les fruits.
Propriétés communes à tous ces objets:
Ils sont vendus dans le magasin et c'est tout !! on capture cette analyse (on dit abstraction) par une déclaration:
Type objet_vendus_dans_le magasin_type is abstract tagged with private;
L'utilisation du mot clé abstract indique que à ce stage, je ne désire pas de possibilité de traitement des données. Il sont vendus dans le magasin, ce qui peut se traduire par:
Procedure vendre(quoi: in out objet_vendus_dans_le magasin_type) is abstract;
A nouveau, a ce stade aucune action avec le mot clé abstract.
Private a son sens habituel de restriction d'usage.
Bien sûr, je vais différencier par étapes successive entre les différents objets qui vont hériter des propriétés communes introduites dans les étapes précédentes .
Tous ces objets sont stockés par unité de stockage (cageot, régime ou à la pièce)
Type unite_de stockage_type is (cageot, regime, par_piece);
Type objets_en_stock_type is new objet_vendus_dans_le magasin_type
With record
Unite_de_stokage: Unite_de_stokage_type:= Unite_de_stokage_type'first;
End record;
Et ainsi de suite pour les autres propriétés.
Ce type permet la création et l'exécution en parallèle de plusieurs parties du projet de manière indépendante et simultanée. Il possède de nombreuses possibilités telle que rendez-vous, délais variés.
L'accès simultané de plusieurs tâches aux mêmes données présente la possibilité d'erreurs. Ada a prévu ce problème grâce à un «type particulier de package» :
protected type Protected_Storage_Type
is
…..
end Protected_Storage_Type;
Toutes ces possibilités seront discutées dans la suite de ce cours.
Il est très important de prévoir expressément :
Le domaine d'existence pour chaque variable utilisée dans un programme
Le domaine de visibilité des opérations pour chaque variable utilisée dans un programme.
Ces domaines peuvent être très variés:
C'est le cas, par exemple, pour l'indice d'une boucle for:
Dummy existe uniquement dans l'intervalle loop .. end loop
...
...
For Dummy in 1..Er'Last loop
...
z:=2*I;
...
end loop;
...
L'utilisation de "declare" permet aussi une limitation du domaine d'existence:
procedure r
is
....
begin
....
....
x existe entre sa déclaration et end
declare
X:Integer;
begin
X:=Z+X;
end;
...
...
....
end r;
...
C'est le plus classique:
....
....
procedure z
is
x:integer;
y existe entre sa déclaration et end z
y:integer;
begin
....
....
y:=y+1;
....
end z;
....
...
Aucune garantie n'est donnée pour la conservation de la valeur stockée entre deux appels à une procédure / fonction du package (en général elle est conservée).
Package body e
is
procedure One (...)
is
begin
...
end one;
Str "existe" entre sa déclaration et end E
str: string (1..10);
procedure two (...)
is
begin
...
end two;
...
end E;
Il y a plusieurs cas possibles:
Aucune garantie n'est donnée pour la conservation de la valeur stockée entre deux appels à une procédure / fonction du package (en général elle est conservée).
Package body e
is
Str "existe" entre sa déclaration et end E
str: string (1..10);
procedure One (...)
is
begin
...
end one;
procedure two (...)
is
begin
...
end two;
...
end E;
La conservation de la valeur (str) stockée entre deux appels à une procédure / fonction du package est garantie
Fichier e.ads (spécification):
Package e
is
procedure One (...) ;
procedure two (...) ;
après private aucune vue externe des variables
private
str: string (1..10);
...
end E;
Fichier e.adb (body):
Package body e
is
procedure One (...)
is
begin
...
end one;
...
procedure two (...)
is
begin
...
end two;
...
end E;
Dans ce cas la conservation de la valeur stockée entre deux appels à une procédure / fonction du 'package' est garantie.
Fichier e.ads (spécification):
Package e
is
type xyz_type is ........;
xyz: xyz_type;
...
procedure One (...) ;
procedure two (...) ;
end E;
Fichier e.adb (body):
Package body e
is
procedure One (...)
is
begin
...
end one;
...
procedure two (...)
is
begin
...
end two;
...
end E;
C'est une variable globale, chaque fois que "with e;" est présent cette variable est globale pour ce package. Attention: "with e;" n'importe pas les opérateurs associés à cette variable pour cela il faut mettre "use type e.xyz_type;"
with e;
Package body z
is
procedure One (...)
is
Accès aux opérateurs associés au type e.xyz_type
use type e.xyz_type;
begin
Accès à une variable globale associée au package e
my_xyz: e.xyz_type;
...
end one;
...
procedure two (...)
is
begin
...
end two;
...
end E;
Le langage ada à prévu la possibilité d'action à la création et à la destruction d'une variable: voici un exemple simpliste:
Le programme bien que vide donne le résultat suivant:
[dg@newdg miage_2005_2006]$ ./z
Initialize
Finalize
Fichier x.ads:
with Ada.Finalization;
package X
is
Le type example est vide dans ce cas simpliste mais ce n'est pas l'emploi le plus fréquent.
type Example
is new Ada.Finalization.Limited_Controlled
with null record;
procedure Initialize(Object : in out Example);
procedure Finalize(Object : in out Example);
end X;
Fichier x.adb:
with Ada.Text_Io;
with Ada.Finalization;
package body X
is
procédure appelée lors de la création
procedure Initialize(Object : in out Example)
is
begin
Ada.Text_Io.Put_Line("Initialize");
end Initialize;
procédure appelée lors de la destruction
procedure Finalize(Object : in out Example)
is
begin
Ada.Text_Io.Put_Line("Finalize");
end Finalize;
end X;
Fichier z.adb:
with X;
procedure Z
is
création de la variable, appel automatique de Initialize
Dummy : X.Example;
begin
null;
destruction de la variable: appel automatique de Finalize
end Z;
Tout projet, même simple DOIT ÊTRE DÉCOUPÉ EN PLUSIEURS ROUTINES. L'exemple le plus simple est:
Une routine de définition des types de variables.
une routine entrée/sortie.
une routine de calcul.
les fichiers se distinguent par leur nom, leur extension et le début de leur contenu. Tout projet ada contient un programme principal qui est une procédure sans paramètres. Le nom du fichier stocké est celui du nom de la procédure sans paramètre (cela oblige le nom du fichier à être un nom de procédure valide en ada). Par exemple, le fichier a.adb contiendra
-- des commentaires
with ……..;
procedure a
is
……
begin
……
end a;
Il existe 2 choix principaux pour découper un problème en routines indépendantes: la procédure et la fonction. Le choix se fait selon plusieurs critères définis ci-dessous.
Le passage de paramètres (variables, tableaux, pointeurs,…….) se fait selon trois modes différents.
Ces modes permettent de 'capturer votre analyse intellectuelle' du problème et d'assurer un programme conforme, écrit plus rapidement et avec moins d'erreurs.
Dans ce mode, la 'variable' sera en lecture seule dans cette routine (procédure ou fonction). C'est le mode par défaut si rien n'est précisé.
procedure Test(I : in Integer := 2;
A : in Integer;
B : Integer)
Is
begin
…..
end Test;
Remarquez la possibilité d'introduire une valeur par défaut pour la variable I. Toutes les variables I, A et B sont disponibles dans cette procédure avec les valeurs transférées lors de l'appel (pas de déclaration entre is et begin!!!). Par exemple, les trois instructions suivantes sont équivalentes:
Test(I => 1,
A => 2,
B => 3);
Test(1,
2,
3);
Test(1,
A => 2,
B => 3);
Elles appellent la procédure TEST avec les valeurs 1 pour I, 2 pour A et 3 pour B.
Les instructions suivantes appellent la procédure en utilisant la valeur par défaut.
Test(A => 2,
B => 3);
Test(2,
B => 3);
Test(2, 3);
Dans ce mode, la 'variable' sera en écriture seule (lecture permise après une écriture) dans cette routine (procédure ou fonction).
procedure Test(I : in Integer := 2;
A : out Integer;
B : out Integer)
Is
…..
end Test;
Les appels ont la même syntaxe que pour le mode in.
Dans ce mode, la 'variable' sera en lecture écriture dans cette routine (procédure ou fonction)
procedure Test(I : in Integer := 2;
A : in out Integer;
B : out Integer)
Is
…..
end Test;
Les appels ont la même syntaxe que pour le mode in.
Dans cette possibilité la 'variable' sera un pointeur en lecture seule.
procedure Test(I : in Integer := 2;
A : in out Integer;
B : Access Object'Class)
Is
…..
end Test;
La fonction prend plusieurs arguments (in par défaut), tous en lecture seule, et renvoie UNE SEULE VALEUR.
Elle s'apparente beaucoup à une variable dans sa syntaxe d'utilisation.
function A(A : Integer;
B : Float)
return Float
is
begin
return B**A;
end A;
Cette fonction très simple renvoie B puissance A. Notez la syntaxe.
La syntaxe d'appel est simple, il suffit de se souvenir que c'est la même utilisation qu'une variable.
Voici trois exemples équivalents (my_float est un réel déclaré avant):
My_Float:=A(2,3.0);
My_Float:=A(2,
B => 3.0);
My_Float:=A(A => 2,
B => 3.0);
Une procédure peut ne renvoyer aucune donnée (pas de paramètre, ou tous en mode in) mais ce n'est pas le cas général. Les paramètres en mode in peuvent avoir une valeur par défaut dans ce cas le paramètre peut être omis dans l'appel.
procedure A(I : in Integer := 2;
A : in out Integer;
B : out Integer)
is
begin
B := A / I * I;
A := A mod I;
end A;
La syntaxe d'appel à été explicitée auparavant. Voici deux exemples:
Utilisation de la valeur par défaut pour I
A(A =>1,
B =>2);
Appel normal sans utilisation de la valeur par défaut
A(I => 4,
A =>1,
B =>2);
L'opérateur := est utilisé pour l'affectation du résultat à une variable d'un type précis. La syntaxe dépend du type, voici quelques exemples:
x := 4.0;
x := Pi;
x := (1 .. 10 => 0);
x := Sum;
x := indice_type_a'Last;
x := Sine(X);
x := long_integer'(2);
x := Float(M * N);
x := Z * (X + 10);
x := Volume;
x := not Destroyed ;
x := 2 * Line_Count;
x := – 4.0;
x := – 4.0 + A;
x := B ** 2 – 4.0 * A * C;
x := Password(1 .. 3) = "Bwv";
x := Count in indice_type_a;
x := Count not in indice_type_a;
x := Index = 0 or over;
x := (Rouge and vert) or jaune; -- (parenthèses obligatoires)
x := A ** (B ** C); -- (parenthèses obligatoires)
x := (1 => 1,
others => 2);
x:= (un => 1,
deux | trois => 2,
fin => 35);
x.un := 1;
x.deux := 2;
x.trois:= 2;
x.fin := 35;
x := T(1..N) & S(3..5);
x:= Ma_Fonction(A => 2.0,
B => 5.0);
X:= new my_type'(x);
X := new Tagged_Data_Type'Class'(The_Record);
X := new Node'(Previous => null,
The_Item => From_Index.The_Item,
Next => null);
Variable : C.My_Type := C.My_Type'(Ada.Finalization.Controlled with I => 0);
Les opérateurs classiques and or xor sont définis comme d'habitude, ils s'appliquent aussi
A certains entiers.
A B (A and B) (A or B) (A xor B)
True True True True False
True False False True True
False True False True True
False False False False False
OR ELSE ou alors: le second terme n'est évalué qui si le premier terme est faux.
AND THEN et alors : le second terme n'est évalué qui si le premier terme est vrai.
IN compris dans l'intervalle
.. intervalle
Examples:
Rouge or jaune
A(1 .. 10) and B(15 .. 24)
pointeur /= null and then Age > 25
N = 0 or else X(N) = vert
z in 1..5 -- z compris entre 1 et 5
un petit programme:
with Ada.Text_Io;
procedure One
is
type Color_Type is(Yellow, Blue, Red, Cyan, Green, Magenta);
Color : Color_Type;
Test : Boolean;
begin
Color := Red;
Test := Color = Yellow or Color = Red;
Ada.Text_Io.Put("test is " & Boolean'Image(Test));
end One;
et sa sortie écran:
test is true
Ces opérateurs classiques sont définis comme d'habitude:
= égal
/= différent
< inférieur
<= inférieur ou égal
> supérieur à
>= supérieur ou égal
un exemple simple:
with Ada.Text_Io;
procedure Two
is
A : Integer;
B : Integer;
begin
A := 2;
B := 3;
Ada.Text_Io.Put(" 2 > 3 is " & Boolean'Image(2 > 3));
end Two;
Et sa sortie écran:
2 > 3 is true
Ces opérateurs classiques sont définis comme d'habitude. L'opérateur & s'applique seulement au tableaux (dont les chaînes de caractères) et correspond au +.
+ plus
– moins
& concaténation
| ou dans les 'case' et les assignations
x:= (un => 1,
deux | trois => 2,
fin => 35);
case x is
when 1|2 => ....
....
end case;
Rien de spécial.
+ –
Ces opérateurs classiques sont définis comme d'habitude.
* Multiplication
/ Division
mod Modulo
rem Reste de la division
A B A/B A rem B A mod B A B A/B A rem B A mod B
10 5 2 0 0 –10 5 –2 0 0
11 5 2 1 1 –11 5 –2 –1 4
12 5 2 2 2 –12 5 –2 –2 3
13 5 2 3 3 –13 5 –2 –3 2
14 5 2 4 4 –14 5 –2 –4 1
A B A/B A rem B A mod B A B A/B A rem B A mod B
10 –5 –2 0 0 –10 –5 2 0 0
11 –5 –2 1 –4 –11 –5 2 –1 –1
12 –5 –2 2 –3 –12 –5 2 –2 –2
13 –5 –2 3 –2 –13 –5 2 –3 –3
14 –5 –2 4 –1 –14 –5 2 –4 –4
Ces opérateurs classiques sont définis comme d'habitude.
** Élévation à une puissance
abs Valeur absolue
not Négation logique (voir plus haut)
Il est toujours possible, pour permettre une lecture plus facile d'un code, de renommer les opérateurs (ou les fonctions et procédures).
Function mult_plus_un (left : in integer;
right : in integer )
return integer
is
begin
return left*right+1;
end mult_plus_un;
.....
function "*" (left :in integer; right : in integer ) return integer renames mult_plus_un;
....
....
begin
x:= a*b; -- calcule a*b+1
...
Bien que peu utile, Il faut savoir l'utiliser
X := float (i);
I := integer(x);
Cette méthode permet l'accès à un niveau très bas. Elle permet de lire la même adresse mémoire de deux façons différentes si les deux type sont représentés par le même nombre de bits. Son utilisation, un peu complexe, est illustrée dans les tables de hashing.
Le langage permet deux types différents: if et case.
La grammaire ada indique:
if condition then sequence_of_statements
{elsif condition then sequence_of_statements}
[else sequence_of_statements]
end if;
Les abréviations sont les suivantes:
{ xxxxxx } veut dire 0 à n répétitions de xxxxxx
[ yyyyyyy ] veut dire yyyyyy est optionnel
sequence_of_statements veut dire 1 à N instructions
exemples :
If my_test = 1
Then
X:=I;
End if;
If my_test = 1
Then
X:=I;
……
Elsif my_test = 0
Or else My_Test = 5
Then
Y:=I;
……
Elsif my_test in 2..4
Then
W:=I;
……
else –- optionnel!!
z:=I;
…
end if;
Le test case permet beaucoup plus de possibilités et est plus lisible s'il y a beaucoup de 'elsif';
il oblige à traiter tous les cas, si tous les cas ne sont pas pris en compte on termine par others.
Pour le "else" final
Voici le même code que ci-dessus traduit en case:
Case My_Test
Is
when 1 =>
X:=I;
……
when 0 | 5 => -- 0 ou 5
Y:=I;
……
when 2 .. 4 => -- 2, 3 et 4
W:=I;
……
when others => -- doit être le dernier choix
z:=I;
…
end case;
voici un autre exemple
Type jour_type is (lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche);
Jour: Jour_type;
…
Begin
……
case jour
is
when lundi => nouvelle_semaine;
when mardi .. jeudi => travail;
when vendredi => vivement_ce_soir;
when samedi | dimanche => -- peut s'écrire samedi .. dimanche
repos;
end case;
Les boucles sont des outils très importants dans la programmation. La sortie de boucle s'effectue:
Boucle «while» : condition de «while» fausse
Boucle «for» : indice supérieur à la borne
Toutes boucles: exit simple ou exit conditionnel
Nom_de_boucle : -- optionnel mais utile si plusieurs boucles imbriquées
Loop
………
-- boucle infinie si pas de sortie
exit nom_de_boucle when a=0; -- exit conditionnel nom de boucle optionnel
exit nom_de_boucle; -- exit simple nom de boucle optionnel
………
end loop Nom_de_boucle;
While a>0 or else r <1 loop
…………
End loop;
La boucle se répète tant que la condition a>0 or else r <1 est vraie.
Ce type de boucle est souvent utilisé pour les indices de tableaux ou pour répéter le même code plusieurs fois.
Subtype contenu_type is integer range 1..2;
Subtype indice_type_a is integer range –2..3;
Subtype indice_type_b is integer range 10..20;
Type tableau_type is array(integer range <>) of contenu_type;
Tableau_a: tableau_type(indice_type_a'range):=(others=>1);
Tableau_b: tableau_type(indice_type_b'range):=(others=>1);
…
begin
…
x := 0;
for I in 1..2 loop
x:=x + Tableau_a(I);
end loop;
x := 0;
for I in tableau_a'range loop
x:= x + Tableau_a(I);
end loop;
nom_de_boucle :
for I in reverse tableau_a'range loop
for J in tableau_b'range loop
x:= x + Tableau_a(I)* tableau_b(j);
exit nom_de_boucle when 0 = Tableau_a(I);
end loop;
end loop_nom_de boucle;
I et J ne sont PLUS disponibles après la boucle. Si cela est nécessaire, il faut utiliser une boucle while,
et modifier les valeurs des indices par vous même.
………
I: integer ;
J : integer ;
………
Begin
…………
I:= indice_type_a'last;
J:= indice_type_b'first;
nom_de_boucle :
while I in tableau_a'range loop
while J in tableau_b'range loop
x:= x + Tableau_a(I)* tableau_b(j);
exit nom_de_boucle when 0 = Tableau_a(I);
J:=1 + J;
end loop;
I:= I - 1;
end loop_nom_de boucle;
Il suffit de se souvenir qu'une fonction s'utilise comme une variable.
Par exemple, une fonction de spécification:
Function abc(premier: in integer:=1;
Second : in integer) return float;
Peut s'appeler comme suit:
A: integer;
B : integer;
C: float;
...
...
--premier prends la valeur de a et second la valeur de b
c := 2.0 * Abc(a,b);
--premier prends la valeur de a et second la valeur de b
c:= abc(premier => a,
second => b);
--premier prends la valeur par défaut de 1 et second la valeur de b
c:= c + abc(second => b);
Soit une procédure:
Procedure Prod (A_1: in out A_type;
A_2 : in A_Type;
A_3 : out A_type :=a_type'first);
L'appel pourrait se faire comme suit
X: A_Type;
Y: A_Type;
Z: A_Type;
....
.....
.....
prod(x,y,z);
prod(x,y);-- utilisation de la valeur par défault
prod (A_1=> 2 * X - 1,
A_2 => X * y,
A_3 => X * Y / z);
Le programme peut (et doit) être séparé en sections indépendantes. Il en existe toujours une par défaut entre begin et end. Toute erreur pendant l'exécution à l'intérieur de ces parties DOIT être gérée, voir plus bas. Des variables peuvent éventuellement être déclarées dans un nouvelle section (qui peut avoir un nom) et n'être valable que dans cette section. Cette méthode permet de déclarer des tableaux (matrices) dynamiquement: leur longueur n'est pas connue du compilateur mais déterminée pendant le fonctionnement du programme:
Procedure X …..
is
size :integer;
type table_type is array (integer range <>) of integer;
…
begin
… lecture de size ou affectation
size :=..........;
declare -- nécessaire si variable à déclarer
x: integer;
table: table_type(1..size);
begin – début d'une section supplémentaire
x:= .....;
…
table(x):=........;
end; -- fin de cette section supplémentaire
-- X et table n'existent plus !!!!
endX;
Le même exemple avec un nom de section
Procedure X …..
is
…
begin
…
interne : -- nom de la section
declare -- nécessaire si variable à déclarer
x: integer;
begin – début d'une section supplémentaire
x:= .....;
…
end interne; -- fin de cette section supplémentaire
-- X n'existe plus !!!!
endX;
Il faut toujours programmer en prenant en compte que l'utilisateur peut et fera des erreurs!
Pour le gérer on utilise la notion d'exceptions. C'est une réponse à une situation anormale.
…
Par exemple la racine carrée d'un nombre négatif n'est pas définie (sans nombres imaginaires!). On déclarera donc une exception:
Racine_imaginaire: exception;
Elle pourra servir plus loin dans le code par exemple
If x <=0.0
Then
Raise Racine_imaginaire;-- cette instruction renvoie à la gestion d'erreur et va
-- directement dans exception à la ligne correspondante
…..
end if;
.......
......
exception
when Racine_imaginaire =>
...... traitement de l'exception
When ....=>
........
when others =>
... traitement des exceptions non prise en compte ci dessus
end ....; fin de la routines
Les fonctions déjà définies dans le langage ont très souvent des exceptions intégrées.
On utilise le mot réservé exception:
Table_pleine, pointeur_null: exception;
Elle se fait à la fin d'un block défini entre begin et end après la dernière instruction et avant end.
…..
begin
….
If x <=0.0
Then
Raise Racine_imaginaire;-- cette instruction renvoie après exception
else
X:= racine-carrée(y);
…
end if;
exception
when Racine_imaginaire =>
… -- ici on gère les conséquences de l'erreur
when .... => .....
end;
Si on désire imprimer le nom de l'erreur (avec le nom de la procédure):
with Ada.Exceptions;
with Ada.Text_Io;
....
procedure Initialize_Generator(......)
.....
begin
....
exception
when Programing_Error : others =>
Ada.Text_Io.Put("Initialize_Generator"
& Ada.Exceptions.Exception_Information(Programing_Error));
end Initialize_Generator;
....
Principes généraux:
Il y a deux choix possibles: cacher tous les détails dans un package spécial (forcément limité dans ses possibilités voir le «package ou tout expliquer en détail au risque de paraître fastidieux. Des exemples tout prêts seront utilisés.
Ce programme déjà écrit pour vous vous permet de ne pas entrer dans les détails du fonctionnement des entrées sorties.
Voici les possibilités offertes ainsi qu'un exemple pour chaque
Vous pouvez aussi utiliser le code de Read_Write. (Voir Chapitre 14.1 )
with Read_Write;
procedure XX
is
x:Integer;
begin
....
Read_Write.Read(Header => "valeur de x ?",
Value => X);
....
end XX;
with Read_Write;
procedure XX
is
x:Integer;
begin
....
Read_Write.Write(Header => "La valeur de x est ",
Value => x);
....
end XX;
with Read_Write;
procedure XX
is
x:Long_Integer;
begin
....
Read_Write.Read(Header => "valeur de x",
Value => x);
....
end XX;
with Read_Write;
procedure XX
is
x:Long_Integer;
begin
....
Read_Write.Write(Header => "valeur de x",
Value => x);
....
end XX;
with Read_Write;
procedure XX
is
x:Float;
begin
....
Read_Write.Read(Header => "valeur de x",
Value => x);
....
end XX;
with Read_Write
procedure XX
is
x:Float;
begin
....
read_write.Write("valeur de x", x);
....
end XX;
with Read_Write;
procedure XX
is
x:Long_Float;
begin
....
Read_Write.Read(Header => "valeur de x",
Value => x);
....
end XX;
with Read_Write
procedure XX
is
x:Long_Float;
begin
....
read_write.Write("valeur de x", x);
....
end XX;
with Read_Write;
procedure XX
is
x : String(1..50) := (others => ' ');
begin
....
Read_Write.Read(Header => "valeur de x",
Value => x);
....
end XX;
with Read_Write;
procedure XX
is
x : String(1..50) := (others => ' ');
begin
....
Read_Write.Write(Header => "valeur de x",
Value => x);
....
end XX;
L'utilisation de ada.text_io pour la lecture est simple à quelques exceptions prés:
Si l'utilisateur entre 12 la valeur de I sera 12:
Package integer_io is new ada.text_io.integer_io(integer);
....
begin
integer_IO.get (I);
Si on entre 12X3 pendant l'utilisation du code précédent, l'exception data_error sera «générée» et le caractère X ne sera pas effacé du tampon d'entrée. Toute nouvelle lecture d'une nombre est devenue impossible! il faut donc purger les caractères restants avant tout nouvel essai:
Ada.text_io.Skip_Line;
package My_Float_Io is new Ada.Text_Io.Float_Io(Float);
....
begin
My_float_IO.get(a_float);
Utilisation de ada_text_io.get_line
C'est la procédure de lecture d'une chaîne de caractères entrée par l'utilisateur, le code suivant peut paraître être à l'origine de DEUX entrées clavier successives, mais …………
Chaque get_line attend le «return» et renvoie: les caractères dans la chaîne et l'indice du dernier caractère dans la chaîne ( ce qui n'est égal au nombre de caractères entrés qui si le tableau commence à 1!!!).
String_1 : String (1..5);
String_2 : String (1..5);
Last_1 : Natural;
Last_2 : Natural;
Begin
.....
String_1 := (others => ' ‘);-- ne jamais oublier
String_2 := (others => ' ‘);-- ne jamais oublier
ada.text_io.Get_Line (String_1, Last_1);
ada.text_io.Get_Line (String_2, Last_2);
Tous les problèmes disparaissent si la longueur des chaînes est > au nombre de caractères entrés par l'utilisateur par ligne. C'EST CE QU'IL FAUT FAIRE sauf difficultés spéciales. Ici on utilise pour la démonstration des chaînes de 5 caractères
Si il y a moins de 5 caractères pour la première ligne par exemple ABC, string_1 contiendra «ABC »
NOTER bien les deux espaces à la fin!! et last_1 contiendra 3. le programme demandera ensuite la seconde ligne.
Si il y a 5 caractères pour la première ligne par exemple ABCDE, string_1 contiendra «ABCDE »
et last_1 contiendra 5. String_2 contiendra « » (5 espaces)
et last_2 contiendra 0 qui est le flag pour chaîne entrée vide (test facile pour «return» sans caractère avant).
Si il y a plus de 5 caractères pour la première ligne par exemple ABCDEFGH, string_1 contiendra «ABCDE » et last_1 contiendra 5. String_2 contiendra «FGH » NOTER bien les deux espaces a la fin!! et last_2 contiendra 3..
Pour purger entre les deux «get_line» utiliser la ligne de code suivante:
ada.text_io.Set_Col (ada.text_io.Current_Input, 1);
vous pouvez vérifier avec le programme w.adb
with Ada.Text_Io;
procedure W
is
String_1 : String(1 .. 5);
String_2 : String(1 .. 5);
Last_1 : Natural;
Last_2 : Natural;
begin
String_1 := (others => ' ');-- ne jamais oublier
String_2 := (others => ' ');-- ne jamais oublier
Ada.Text_Io.Put("string_1 ? ");
Ada.Text_Io.Get_Line(String_1, Last_1);
Ada.Text_Io.Put("string_2 ? ");
Ada.Text_Io.Get_Line(String_2, Last_2);
Ada.Text_Io.New_Line;
Ada.Text_Io.Put_Line('>'& String_1 & "< last_1 " & Integer'Image(Last_1));
Ada.Text_Io.Put_Line('>'& String_2 & "< last_2 " & Integer'Image(Last_2));
end W;
qui donne les résultats suivants:
2 entrées de moins de 5 caractères le programme demande les deux chaînes
string_1 ? ABC
string_2 ? DE
>ABC < last_1 3
>DE < last_2 2
1 entrée de 5 caractères => le programme demande une seule chaîne et se termine en sautant le second get_line:
string_1 ? ABCDE
string_2 ?
>ABCDE< last_1 5
> < last_2 0
1 entrée de plus de 5 caractères => le programme demande une seule chaîne et se termine en sautant le second get_line:
string_1 ? ABCDEFGH
string_2 ?
>ABCDE< last_1 5
>FGH < last_2 3
Essayez pour vous même en commentant la ligne qui initialise les chaînes avec des caractères. Que se passe-t-il?
La sortie écran d'une variable peut être simple si aucun format précis n'est nécessaire, Dans ce cas on utilise l'attribut image qui transforme en chaîne de caractères facile à utiliser
Chaine :string (1..12);
.....
begin
....
Ada.Text_Io.Put(Item => " La valeur est "
& chaine);
sur l'écran il apparaît:
La valeur est xyz
dans le cas ou vous voulez visualiser les espaces avant et après les autres c