Si vous détectez des erreurs ou si vous avez des suggestions, veuillez communiquer avec Pierre Bélisle

Généralités

           ·À qui s'adresse ce document?

           ·Pourquoi utiliser Ada dans les cours de programmation de base ?

Les types de base
           ·Qu'est-ce qu'un type ?
           ·Qu'est-ce qu'un intervalle ?
           ·Qu'est-ce qu'un sous-type ?
           ·Comment peut-on faire de la conversion de type ?

Le type STRING
           ·Que peut-on faire avec le type STRING ?

Le type CHARACTER

           ·Quels sont les codes ASCII associés aux caractères ?

Les types structurés

            Les enregistrements ou articles (RECORD)
                          ·Quelles sont les opérations permises sur les articles ?

            Les tableaux (ARRAY)
                          ·Comment initialiser un  tableau d'un seul coup(aggrégats) ?

Les entrées/sorties
          Les affichages
                  ·Comment fait-on pour afficher un entier sur moins de 11 colonnes ?
                  ·Comment fait-on pour afficher un reel avec un autre format que 1.00000E38 ?
                  ·Pourquoi lorsque j'affiche une chaîne de caractères, il apparaît des caractères indésirables à l'écran ?
 

        Entrés/sorties avec les types énumératifs
                  ·Comment saisir ou afficher une valeur de type énumératif(enumeration_io) ?

Le SKIP_LINE
          ·Comment "Skip_Line" fonctionne-t-il ?
          ·Pourquoi "Skip_Line" est conditionnel avec "Get_Line" ?

Quelques trucs
          ·Comment faire une pause pour que mon programme attende la touche <entrée> pour continuer ?
          ·Comment positionner le curseur à l'endroit que je désire ?
          ·Peut-on programmer en couleur en mode console ?
          ·Peut-on saisir une touche au clavier comme avec getch() en C ?

Les attributs de types
          ·Qu'est-ce qu'un attribut de type ?

           Sur les types de base
                    ·Comment l'attribut 'POS fonctionne-t-il ?
                    ·Comment l'attribut 'VAL fonctionne-t-il ?
                    ·Comment l'attribut 'VALUE fonctionne-t-il ?
                    ·Comment l'attribut 'IMAGE fonctionne-t-il ?
 

           Sur les tableaux
                    ·Comment l'attribut 'FIRST fonctionne-t-il ?
                    ·Comment l'attribut 'LAST fonctionne-t-il ?
                    ·Comment l'attribut 'RANGE fonctionne-t-il ?
                    ·Comment l'attribut 'LENGTH fonctionne-t-il ?

Les sous-programmes
                    ·Qu'est-ce qu'un sous-programme ?
                    ·Quelle est la différence entre IN, OUT et IN OUT ?
                    ·Quelles sont les règles sur les fonctions ?
                    ·Quelle est la différence entre une variable globale et une variable locale ?
                    ·Quelles sont les règles de visibilité ?

Les Exceptions

 ·Pourquoi ma gestion d'exceptions ne se comporte pas comme je le voudrais ?


Les fichiers
                    ·Qu'est-ce qu'un fichier ?
                    ·Qu'est-ce qu'un fichier texte ou un fichier de texte ?
                    ·Qu'est-ce qu'un fichier typé ou un fichier binaire ?
 

***Si vous ne trouvez pas la réponse à ce que vous cherchez ici, posez votre question sur le forum de discussion.


Qui

Ce document s'adresse principalement aux étudiants du cours de programmation I, et tente de répondre aux questions les plus fréquemment posées dans ce cours.


Ada

Les cours de programmation de base servent à montrer aux étudiants à programmer.  La résolution de problème par la programmation est tout le cheminement que l'on fait pour obtenir une solution à un problème en utilisant un outil qu'est l'ordinateur.   Un langage de programmation est un outil qui permet à un programmeur de commander l'ordinateur pour qu'il effectue les différentes tâches qui sont nécessaires à résoudre le problème.

Ada est un langage de programmation qui est très peu permissif en ce qui a trait aux erreurs sur les types.  Ce qui rend l'outil très intéressant dans un cadre pédagogique. Par exemple, Un langage comme le langage C est très déconseillé pour l'apprentissage de la programmation. La raison est qu'il est très permissif et permet à l'étudiant de faire beaucoup d'erreurs qui ne font pas nécessairement partie des objectifs à atteindre.

De plus, Ada est un langage structuré qui permet l'apprentissage de la programmation modulaire et encore une fois, il est très strict.  Cela permet une fois de plus à l'étudiant de ne pas se mélanger en faisant des erreurs qui ne font pas parties des objectifs de ces cours.

Finalement, Ada est un langage orienté-objet, et permet le parallélisme.   Ce qui permet d'apprendre de nouveaux concepts dès le deuxième cours de programmation, sans avoir à changer de langage.

Il est très clair que n'importe quel langage a ses avantages et ses inconvénients. Un langage comme Ada a plus d'avantages du côté pédagogique que du côté pratique, mail il permettra à l'étudiant de s'adapter à n'importe quel autre langage par la suite.

Ce qu'il faut se rappeler par-dessus tout, est que le langage de programmation n'est qu'une façon de communiquer avec l'ordinateur. L'ordre des instructions et l'organisation des données, c'est le programmeur qui en décide. C'est cela qu'on tente de montrer dans les cours de programmation de base, et non seulement un langage de programmation.


Skip_Line

Skip_Line est un sous-programme du module externe Ada.Text_IO.   Cette procédure permet principalement de sauter par-dessus un symbole de fin de ligne(FL) dans un fichier de texte ouvert en lecture.

Le clavier est considéré comme un fichier de texte qui n'a qu'une ligne de texte possible à la fois.  Donc, passer par-dessus le symbole de fin de ligne se résume à vider le tampon du clavier. Par contre dans un fichier de texte sur disque, on verra "skip_line" comme une instruction permettant de passer à la ligne suivante dans le fichier.

La stratégie employée est :

si le tampon est déjà vide lorsque skip_line est exécuté, l'ordinateur attendra qu'un utilisateur presse la touche <entrée>, pour continuer.  Pour s'assurer du bon fonctionnement du Skip_line en ce qui regarde le clavier, nous dirons que chaque instruction de lecture "GET" qui provient du module externe Ada.Text_IO, sera préférablement suivie d'un Skip_Line.  Ceci nous assurera de sauter par-dessus le symbole de fin de ligne et que le tampon est vide, si nous avons besoin de faire une pause.

ex:    Es_Entiers.Get(Un_Nombre_Entier);
    Ada.Text_IO.Skip_Line;

    Es_Reels.Get(Un_Nombre_Reel);
    Ada.Text_IO.Skip_Line;

    Ada.Text_IO.Get(Un_Car);
    Ada.Text_IO.Skip_Line;

        --ceci fait une pause, si le tampon du clavier est vide
   Ada.Text_IO.Put("appuyer sur <entrée> pour continuer");
   Ada.Text_IO.Skip_Line;


Set_Col

Cette instruction permet de se positionner à l'écran, à une colonne en particulier de la ligne courante.  Cependant, Set_Col ne permet pas de revenir en arrière sur la ligne.  Si vous tentez de revenir sur une colonne qui vient avant la colonne courante, Set_Col fera un saut de ligne pour atteindre la colonne voulue de la ligne suivante.

ex:  Ada.Text_IO.Set_Col(10);               --va à la colonne 10 de la ligne courante

      Ada.Text_IO.Put("Menu principal"); --affiche ce message à la ligne et la colonne courante

      Ada.Text_IO.Set_Col(5);                 --va à la colonne 5 de la ligne suivante, parce qu'il ne revient pas en

--arrière

**Le nombre entre parenthèses doit être de type Positive_Count.   Ce qui signifie que si vous mettez une variable comme paramètre, elle devra être de ce type ou bien il faudra faire une conversion de type.

                         ex:    Ada.Text_IO.Set_Col(Ada.Text_IO.Positive_Count(Un_Nombre_Entier));


Type

Un type est un concept essentiel en informatique.   Chaque donnée à l'intérieur d'un programme Ada doit appartenir à un type.

Les types élémentaires utilisés pour ce cours sont :

      1. la catégorie des  valeurs permises pour ce type
      2. l'intervalle des valeurs permises
      3. les opérations permises sur ces valeurs.

Une des tâches du programmeur est d'apprendre pour chaque type qu'il connaît, quelles sont ces informations.

exemple :  integer


Conversion de type

Il est impossible de faire des opérations sur des types différents en Ada ou d'affecter une valeur à une variable avec une donnée d'un autre type.  Il faut toujours s'assurer que les données sont de mêmes catégories.  Donc il faudra convertir certaines données temporairement pour réussir à faire l'opération voulue.   Pour convertir un type, il suffit de mettre entre parenthèses la donnée à convertir et de nommer le type voulu avant la parenthèse.

ex:

  1. Un_Nombre_Entier : Integer := Integer(2.75);
  2. Un_Nombre_Reel : Float := Float(2);
  3. Es_Reels.Put(2.75 + Float(un_nombre_entier));

Dans le premier exemple, le nombre réel 2.75 sera converti en entier, ce qui signifie qu'il perdra sa partie décimale.  Pour convertir un réel en entier, Ada arrondi selon le premier chiffre après le point.  Ce qui donnera 3, après la conversion dans la variable Un_Nombre_Entier.

Dans le deuxième exemple le nombre entier sera converti en réel.  Ce qui signifie qu'il aura une partie décimale.  Ce qui donnera 2.000000 après la conversion dans la variable Un_Nombre_Reel.

***à noter que la conversion est comme une fonction qui retourne votre nombre dans un autre type, ce qui signifie que le nombre à convertir n'est pas modifié.   Également, vous ne pouvez pas faire de conversion de type entre 2 types qui n'ont pas la même catégorie de base.

Vous ne pouvez pas faire une conversion du genre :

Car : Character := Character(3.78);

Autrement dit, les nombres avec les nombres, les caractères avec les caractères etc.


Attribut de type

Un attribut de type est comme une fonction qui retourne de l'information sur un type.   Soit un type quelconque T, il suffit de séparer le type de son attribut à l'aide d'une apostrophe.  Il existe plusieurs attributs de type différents selon le type de données.

                     ex: 'IMAGE attribut pour le type string

                         'POS     attribut pour les types énumératifs


'POS

Chaque caractère a une correspondance numérique qu'on appelle le code ASCII.   Pour faire référence au code ASCII d'un caractère, à l'intérieur d'un programme ADA, on peut utiliser l'attribut de type 'POS.  Cet attribut est comme une fonction qui retourne un entier qui correspond au code ASCII du caractère voulu.

exemple :   --le code correspondant au caractère 'A' est 65

                ...

                 Car : Character := 'A';

                BEGIN
                        Es_Entiers.Put(Character'POS(Car));          --ceci affichera 65 à l'écran
                END;


'VAL

Chaque caractère a une correspondance numérique qu'on appelle le code ASCII.   Pour faire référence  à un caractère à partir du code ASCII à l'intérieur d'un programme ADA, on peut utiliser l'attribut de type 'VAL.  Cet attribut est comme une fonction qui retourne un caractère qui correspond à la valeur demandée, c'est l'attribut contraire de l'attribut 'POS.

exemple :   --le code correspondant au caractère 'A' est 65

                ...

                BEGIN
                        Es_Entiers.Put(Character'VAL(65));         --ceci affichera le caractère 'A' à l'écran
                END;


'VALUE

Cet attribut de type sert avec les données de type STRING.  Il est impossible de convertir par la conversion de type habituelle, les données qui sont d'ancêtres différents.  L'attribut 'VALUE permettra d'obtenir l'équivalent numérique d'une chaîne de caractères.

                  Ex:  nombre : Integer := Integer'VALUE("12345");

l'attribut 'VALUE retournera sous forme d'entier, la chaîne de caractères entre parenthèses.  ATTENTION, il faut absolument que le contenu de la chaîne puisse se transformer en entier.  On ne pourrait pas faire :

                 Ex:  Nombre : Integer := Integer'VALUE("abcde");


'IMAGE

Cet attribut de type sert avec les données de type STRING.  Il est impossible de convertir par la conversion de type habituelle, les données qui sont d'ancêtres différents.  L'attribut 'IMAGE permettra d'obtenir l'équivalent chaîne de caractères d'une valeur numérique.

                         Ex:  Chaine : String := Integer'IMAGE(12345);

l'attribut 'IMAGE retournera sous forme de chaîne de caractères, le nombre entre parenthèses.


'LENGTH

Cet attribut de type retourne le nombre de caractères maximum possibles de la définition d'une variable de type chaîne de caractères. La formule pour trouver l'intervalle c'est : valeur maximum - valeur minimum + 1.

               Ex:

Chaine : String(5..180);

                      Es_Entiers.Put(Chaine'LENGTH)       --affichera le nombre 176 à l'écran (180-5+1).


Afficher un entier

Pour afficher un entier avec Ada.Text_IO, il faut faire une instanciation de paquetage générique dans la partie déclarative du programme.   Ensuite, on peut utiliser l'instruction "put" dans le corps du programme   en préfixant avec le nom du paquetage.

PACKAGE Es_Entiers IS NEW Ada.Text_IO.Integer_IO(Integer);

Un_Nombre_Entier : Integer := 12;

BEGIN

      Ada.Text_IO.Put("Votre nombre est : ")
      Es_Entiers.Put(Un_Nombre_Entier);

END;

Le problème est qu'un entier par défaut est affiché sur 11 colonnes.  Ce qui fait que le nombre est affiché à l'écran de la façon suivante :

             Votre nombre est :           12

On peut régler ce problème en ajoutant une information à l'instruction "put".  On peut lui dire sur combien de colonnes au minimum afficher le nombre.  Ce qui donnera ce qui suit :

PACKAGE Es_Entiers IS NEW Ada.Text_IO.Integer_IO(Integer);

Un_Nombre_Entier : Integer := 12;

BEGIN

      Ada.Text_IO.Put("Votre nombre est : ")
      Es_Entiers.Put(Un_Nombre_Entier,1); --la deuxième valeur est le nombre de colonnes minimum

END;

--Le nouveau résultat sera :

                    Votre nombre est : 12

***à noter qu'il n'y a jamais de valeur qui ne sont pas affichée parce que le nombre de colonne est plus petit que le nombre à afficher.


Afficher un reel

Pour afficher un réel avec Ada.Text_IO, il faut faire une instanciation de paquetage générique dans la partie déclarative du programme.   Ensuite, on peut utiliser l'instruction "put" dans le corps du programme   en préfixant avec le nom du paquetage.

PACKAGE Es_Reels IS NEW Ada.Text_IO.Float_IO(Float);

Un_Nombre_Reel : Float := 12.;

BEGIN

      Ada.Text_IO.Put("Votre nombre est : ")
      Es_Reels.Put(Un_Nombre_Reel);

END;

Le problème est qu'un réel  par défaut est affiché de la façon suivante :

              Votre nombre est : 12.575000E+00

On peut régler ce problème en ajoutant une information à l'instruction "put".  On peut lui dire combien de nombre pour la partie entière, combien de nombre pour la partie décimale et combien de nombre pour la partie exposant.  Ce qui donnera ce qui suit :

PACKAGE Es_Reels IS NEW Ada.Text_IO.Float_IO(Float);

Un_Nombre_Reel : Float := 12.575;

BEGIN

      Ada.Text_IO.Put("Votre nombre est : ")
     Es_Reels.Put(Un_Nombre_Reel,4,2,0); --le format sera 4 entiers avant le point, 2 chiffres après le
                                                                    --point et pas de chiffre pour l'exposant.

END;

--Le nouveau résultat sera :

                Votre nombre est :  12.58
 
 

***à noter qu'il y aura arrondissement si le nombre de colonne de la partie décimale est plus petit que la partie décimale à afficher.


Les types énumératifs

Un type énumératif, est un type dont on définit toutes les valeurs possibles.   Le type "Boolean" et le type "character" en font parties.   Pour définir nous même un type énumératif, il suffit de faire l'énumération des valeurs permises pour ce type.

ex:  TYPE T_Jour_Semaine IS (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi);

Les opérations permises sur les données d'un type énumératif, sont : l'affectation et les opérateurs de relations (<, >, >=, <=, =, /=, :=).  Il faut comprendre que les valeurs fournies lors de la définition du type sont utilisables tel quel.  Ce qui signifie que les valeurs ne doivent pas être entre guillemets lors de leur utilisation.

ex:  Jour : T_Jour_Semaine := lundi;

Le compilateur ne fait pas de différence entre les majuscules et les minuscules pour les valeurs d'un type énumératif( à l'exception du type "character").   Finalement, pour faire des entrées/sorties sur une donnée d'un type énumératif, il faut instancier le paquetage enumeration_io ou utiliser l'attribut de type  'image.


Enumeration_io

Pour pouvoir utiliser directement les instruction "put" et "get" sur des types énumératifs, il faut instancier le module externe enumeration_io avec le type pour lequel on veut faire des entrées/sorties.

ex:  TYPE T_Jour_Semaine IS (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi);

       PACKAGE ES_Jour IS NEW Ada.Text_IO.Enumeration_IO(T_Jour_Semaine);
 
 

Par la suite il suffit d'utiliser le nom du paquetage comme préfixe pour afficher ou saisir une donnée de ce type.

ex:  ES_Jour.Put(lundi);

**l'affichage se fera toujours en majuscule et la saisie ne fera pas de différence entre majuscules et minuscules.


Get_Line

Get_Line est l'instruction de Ada.Text_IO qui permet de lire une donnée de type chaîne de caractères en provenance du clavier.  Get_Line nécessite 2 informations, la chaîne à lire et une variable de type entier pour recevoir la longueur de la chaîne lue.

ex:   Chaine : String(1..20);

       Longueur : Natural;

       Get_Line(Chaine,Longueur);

La variable "Chaine" contiendra la chaîne que l'utilisateur a entré et "Get_Line" mettra dans la variable "Longueur",  la longueur de cette chaîne.  "Longueur" ne dépassera jamais le nombre de caractères maximum possibles de la chaîne(20 dans le dernier exemple).  Cette instruction retourne une chaîne lorsque l'une ou l'autre des conditions suivantes sera remplies.  Soit la touche <entrée> a été pressée ou le nombre de caractères maximum possibles de la chaîne à été lu.  Si le premier cas est atteint un "Skip_Line" est fait automatiquement.  Dans le deuxième cas,  il n'est pas fait, il faudra alors en faire un.  Donc la seule information que nous possédons pour savoir si un "Skip_Line" a été fait, c'est "Longueur", Si elle est égale au nombre de caractères maximum possibles, on fera un "Skip_Line".

IF Longueur = Chaine'LENGTH THEN

    Skip_Line;

END IF;


STRING

Voici les affectations légales avec le type STRING.  La seule vrai contrainte est que lors de l'affectation, la longueur des chaînes doivent être égales.
 
 

ex:   Chaine1 : String(1..40);
       Chaine2 : String(1..150);

       Chaine1 := Chaine2(25..64); --pour compter le nombre de cases, la valeur max - la valeur min + 1.

 --Ce qui donne ici 64-25+1 = 40 et c'est légal. Si aucun intervalle n'est donné,                                                     --c'est de toute la chaîne dont on parle.

--C'est aussi légal de prendre une portion d'une chaîne de  la déplacer dans la même chaîne

                             ex:  Chaine2(20..30) := Chaine2(1..11);



Les fichiers

Un fichier est une façon de conserver de façon permanente sur un support physique, des données qui sont en mémoire.  Cela permet de communiquer des informations d'un programme à un autre et/ou de communiquer des informations à un utilisateur, et/ou de faire une sauvegarde des données.

Les 2 grandes catégories de fichiers sont les fichiers de texte et les fichiers binaires.  Peu importe la catégorie, il existe certaines généralités pour l'utilisation de fichier, en voici quelques unes :
 



Fichier de texte(en Ada)

Un fichier de texte est comme un grand tableau de caractères sur un support physique(disque dur, disquette ...).  Chaque ligne de texte est séparée par un symbole qu'on appelle "fin de ligne" (FL).  Un fichier de texte se termine par un symbole appelé "fin de fichier"(FF).

Pour une question d'efficacité, l'ordinateur a besoin d'un espace mémoire pour chaque fichier qu'on appelle "tampon" ou en anglais "buffer".  Ce tampon peut contenir plusieurs caractères et est rempli chaque fois qu'il est vide.  Le mécanisme est que si une instruction de lecture est faite dans un fichier de texte et que le tampon est vide, il sera rempli par les caractères du fichier et ensuite l'affectation se fera à partir du tampon.  Si par contre lors d'une lecture il y a déjà des caractères dans le tampon, il n'y aura pas de lecture dans le fichier, l'affectation se fera quand même, mais à partir du tampon.

L'ordinateur s'occupe de savoir où il est rendu à lire dans le tampon et dans le fichier.  Vous n'avez pas à vous en préoccuper avec Ada.  Pour associer un tampon avec un fichier, il faut déclarer une variable qui servira de tampon.

                          ex:  fichier_texte : Ada.Text_IO.File_Type;

C'est lors de l'ouverture du fichier que le lien se fera entre le fichier réel et le tampon en mémoire.

                          ex: Ada.Text_IO.Create(Fichier_Texte,Ada.Text_IO.Out_File,"a:toto.txt");

Cette dernière instruction, créera un fichier appelé "toto.txt" sur l'unité de disquette "a:", et associera un tampon appelé "fichier_texte".  Dorénavant, toutes les instructions dans ce fichier se feront via le tampon.

                           ex: Ada.Text_IO.Put(Fichier_Texte,"salut vous autres");

À la fin du programme il faut couper le lien entre le fichier et le tampon. Pour ce faire il faut utiliser la procédure close:

                           ex: Ada.Text_IO.Close(Fichier_Texte);



Fichier binaire(en Ada)

Un fichier binaire (ou typé) est un grand tableau sur un support physique(disque dur, disquette ...), mais dont le contenu peut être de tout type(élémentaire, tableau, article, tableau d'articles...).   Il n'y a que 2 instructions qui permettent de manipuler le contenu d'un fichier binaire en Ada et c'est READ(lecture) et WRITE(écriture).

Pour pouvoir utiliser un fichier binaire, il faut déterminer si on l'accède en mode séquentiel ou direct.  Si on veut accéder aux données séquentiellement, nous devons importer ADA.SEQUENTIAL_IO.  Par contre en accès direct, on doit importer ADA.DIRECT_IO.  Par la suite, nous devons spécifier de quel type est le fichier et cela se fait par l'instanciation de paquetage.

ex;  PACKAGE ES_Fic_Etud IS NEW ADA.SEQUENTIAL_IO(T_Etud);

Cette dernière instruction nous donne tous les outils pour pouvoir utiliser un fichier binaire de type "T_Etud" en accès séquentiel.  Naturellement, le type "T_Etud" doit avoir préalablement été défini.

À partir du moment où le paquetage est défini, n'importe quel référence à un type, une procédure ou une fonction qui provient de ADA.SEQUENTIAL_IO doit être utiliser avec le nom du paquetage en préfixe.

ex:




Sous-programme

Un sous-programme a les mêmes droits qu'un programme principal, mais il peut recevoir des informations sous forme de ce qu'on appelle des paramètres.   Les paramètres qui sont dans la définition du sous-programme sont dits paramètres formels et ceux lors de l'appel du sous-programme sont dits paramètres effectifs.
 

Exemple de définition d'un sous-programme avec paramètres formels :

--Cette procédure valide n'importe quel entier pour qu'il soit entre les bornes minimum et maximum
--reçus également en paramètre.  L'entier est retourné via son canal de sortie(OUT).

PROCEDURE Valider_Entier(Msg_Sollic : IN String;    --message de sollicitation à afficher

                         Entier     : OUTInteger;  --l'entier à lire et à valider

                         Min        : IN  Integer;  --la valeur minimum permise

                         Max        : IN  Integer;  --la valeur maximum permise

                         Msg_Erreur : IN String) IS --message à afficher en cas de non

                                                    --respect des bornes
 
 

BEGIN --Valider_Entier

        LOOP

                   Put(Msg_Sollic);

                  Es_Entiers.Get(Entier);

                  Ada.Text_IO.Skip_Line;

                  EXIT WHEN Entier IN Min..Max;

                 Put(Msg_Erreur);

         END LOOP;

END Valider_Entier;


Différence entre IN, OUT et IN OUT

En Ada tout les paramètres sont passés par références(adresses).  Cependant, il est nécessaire de connaître les règles de passages de paramètres selon les différents modes possibles.  On peut imaginer le fonctionnement de la façon suivante :

IN :    Le contenu du paramètre effectif est copié dans le paramètre formel.  À la fin du sous-programme, le paramètre effectif est inchangé.  Le paramètre formel ne peut servir que comme une constante à l'intérieur du sous-programme.

OUT :  Le contenu du paramètre effectif n'est pas copié dans le paramètre formel.  À la fin du sous-programme, le paramètre effectif est modifié. Le paramètre formel est utilisable comme une variable locale à l'intérieur du sous-programme.  Il est impératif de mettre une valeur dans le paramètre formel avant la fin du sous-programme pour éviter des erreurs à l'exécution.

IN OUT : Le contenu du paramètre effectif est copié dans le paramètre formel.  À la fin du sous-programme, le paramètre effectif est modifié.   Le paramètre formel est utilisable comme une variable locale à l'intérieur du sous-programme.

Ce qui implique qu'aussitôt que l'on voit qu'un paramètre formel est OUT ou IN OUT, il faut absolument que le paramètre effectif soit une variable.  Dans le cas d'un paramètre IN,  le paramètre effectif  peut être aussi une constante ou un littéral.


Règles sur les fonctions

Il peut y avoir de 0 à plusieurs paramètres pour une fonction.  S'il y en a, ils doivent tous être en mode IN. Une fonction doit minimalement avoir une instruction RETURN dans sa partie exécutive(entre begin..end;), pour que le programme compile.  De plus, lors de l'exécution de la fonction, il est impératif qu'il y ait une instruction RETURN d'exécutée.  Si ce n'est pas le cas, une exception PROGRAM_ERROR sera levée.  Il est préférable de mettre les valeurs de retour dans une variable temporaire, locale à la fonction(du même type que la valeur de retour), et de faire le RETURN juste avant le END de la fonction, pour éviter ce genre d'erreur.

Ex:

        FUNCTION Bissextile(Annee : IN Integer) RETURN Boolean IS
        --retourne vrai si l'année reçue en paramètre est bissextile et faux sinon

        BEGIN -- Bissextile

                    RETURN (Annee mod 400 = 0) OR (Annee mod 4 = 0 AND Annee mod 100 /= 0);

       END Bissextile;
 

         FUNCTION Nombre_Jour_Max(Mois : IN Integer; Annee : IN Integer) RETURN Integer IS
        --Retourne le nombre de jours maximum permis pour le mois de l'année reçu en paramètre.

                           --variable
                           Nombre_Jour : Integer;   --nécessaire pour n'avoir qu'un seul RETURN

         BEGIN --Nombre_Jour_Max

                           --Dans cet exemple, nous présumons que le mois a été validé avant l'appel de la fonction.
                            CASE Mois IS

                                        WHEN 2                     => Nombre_Jour := 28 + Boolean'POS(Bissextile(Annee));

                                        WHEN 4|6|9|11           => Nombre_Jour := 30;

                                        WHEN others               => Nombre_Jour := 31;

                            END CASE;

                 RETURN Nombre_Jour;

       END Nombre_Jour_Max;

**Boolean'POS(Bissextile(Annee)) retourne 1 si l'année est bissextile et 0 sinon.


Programmer en couleur

Il est possible de programmer en couleur en mode console.  Pour pouvoir le faire, il faut importer des fichiers et suivre les indications qui sont fournies à cette page.


Peut-on saisir une touche au clavier comme avec getch() en C?

Il est possible de récupérer un caractère sans avoir à presser la touche <entrée>.  Pour ce faire, il faut utiliser le sous-programme "get_immediate", du module Ada.text_io.  Il y a cependant un inconvénient si l'utilisateur utilise la touche <entrée>, il devra le faire 2 fois.  Il est possible également avec PRAGMA et le langage C, mais cela déborde du cadre de ce cours.


Initialiser un tableau d'un coup

Vous pouvez initilialiser un tableau ou un article en une seule instruction grâce à ce qu'on appelle des aggrégats.  Il existe 2 sortes d'aggrégats : par nom ou par position.

Par nom :

Soit le tableau suivant :

      TYPE T_Tab IS ARRAY(1..10) OF Integer;

      Tableau : T_Tab;

Il est possible de mettre des valeurs dans chaque case du tableau en nommant les cases dans lesquelles on veut mettre une valeur et cela pendant l'affectation.  Pour ce faire, il suffit de mettre entre parenthèses le nom de la case suivi du symbole "=>" suivi de la valeur à mettre dans cette case.

ex:  Tableau := (1=>3,5=>4,7=>5,others=>0);

Cette dernière instruction met dans la case 1 la valeur 3, dans la case 5 la valeur 4, dans la case 7 la valeur 5 et dans toutes les autres cases(others) la valeur 0. Donc si on veut mettre toutes les cases d'un tableau à une même valeur...

ex:  Tableau := (others => 1).

Ceci met la valeur 1 dans toutes les cases du tableau qui n'ont pas été nommées.   Comme il n'y en a eu aucune de nommée, toutes les cases du tableau seront affectées.
 
 

Par position :

Cette méthode ne demande pas de nommer les cases, alors c'est l'ordre dans lequel les valeurs sont mises qui détermine dans quelle case ira la valeur.

ex: Tableau :=(0,0,3,0,4,0,5,0,0,0);  ou Tableau :=(0,0,3,0,4,0,5,others=>0);

Ces 2 instructions d'affectation sont équivalentes au premier exemple que nous avons donné plus haut pour la méthode par nom.  Il n'est pas permis de mélanger les 2 méthodes.  La seule chose qui peut être utilisé dans les 2 cas est le "others".


Les caractères indésirables lors de l'affichage d'une chaîne

Vous avez probablement omis d'utiliser la longueur de la chaîne à afficher.

ex:  Put(chaine(1..lng));


Quelles sont les opérations permises sur les articles(RECORD)?

Les seules opérations qui sont permises sont l'affectation(:=) et la comparaison d'égalité(=, /=).  Toutes les autres opérations doivent être définies par le programmeur.


Quelle est la différence entre une variable globale et une variable locale?

Pour bien comprendre la différence, il faut connaître les règles de visibilité du compilateur.  Si une variable est utilisée dans une région déclarative autre que celle où elle a été définie, on dira qu'elle est utilisée globalement et que c'est une variable globale.   En résumé, à l'intérieur d'un sous-programme, les seules choses qu'on devrait utiliser sont les variables définies dans ce sous-programme et les paramètres formels de ce sous-programme.


les règles de visibilité

Une région déclarative est une portion de texte qui est d'une des formes suivantes :

  1. Chacune de ces régions est dite associées avec les déclarations et/ou les instructions qu'elles englobent.
  2. On dit qu'une déclaration ou une instruction "survient immédiatement dans" une région, là où elle est déclarée.
  3. Une déclaration qui survient immédiatement dans une autre est dite locale et celles qui sont déclarées à l'extérieur de la région sont dites globales. On ne peut avoir qu'une seule déclaration de même nom dans une même région.
  4. Une déclaration locale est prioritaire à une déclaration globale, donc si 2 déclarations ont le même nom dans 2 régions différentes, c'est la déclaration locale qui sera prise en compte.
  5. On peut avoir des régions déclaratives dans des régions déclaratives et ces sous régions font parties de la région principale.
  6. Une déclaration n'est visible que dans sa région déclarative, mais est visible dans toutes ses sous régions.

'FIRST

Cet attribut de type se comporte différemment selon le type de données avec lequel il est utilisé.   Si c'est un type énumératif, cet attribut retournera la première valeur dans la définition du type.  Si c'est un type tableau, l'attribut 'FIRST retournera la valeur de l'indice de la première case.

Soit: TYPE T_Jour_Semaine IS (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi);

     TYPE T_Tab IS ARRAY(50..225) OF Integer;

     Tableau : T_Tab;

ex:

      T_Jour_Semaine'FIRST retournera dimanche

      Tableau'First retournera 50

***L'erreur la plus fréquente est de penser que T_Tab'First retournera le contenu de la première case du tableau.  Si ce qu'on désire est le contenu de la case, il faudra faire utiliser plutôt Tableau(Tableau'FIRST).   On peut utiliser aussi bien le nom du type qu'une variable avec cet attribut Tableau(T_Tab'FIRST).


'LAST

Tout comme l'attribut 'FIRST, cet attribut donne de l'information sur les valeurs d'un type de données et se comporte différemment selon le type.   Si c'est un type énumératif, cet attribut retournera la dernière valeur dans la définition du type.  Si c'est un type tableau, l'attribut 'LAST retournera la valeur de l'indice de la dernière case.
 
 

Soit: TYPE T_Jour_Semaine IS (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi);

        TYPE T_Tab IS ARRAY(50..225) OF Integer;

        Tableau : T_Tab;

ex:

         T_Jour_Semaine 'LAST retournera samedi

         Tableau'Last retournera 225

***L'erreur la plus fréquente est de penser que T_Tab'Last retournera le contenu de la dernière case du tableau.  Si ce qu'on désire est le contenu de la case, il faudra faire utiliser plutôt Tableau(Tableau'LAST).   On peut utiliser aussi bien le nom du type qu'une variable avec cet attribut Tableau(T_Tab'LAST).


'RANGE

Cet attribut de type retourne une valeur de type intervalle.  L'intervalle qui est retourné est celui qui a été utilisé lors de la définition d'un tableau.

Soit::

            TYPE T_Tab IS ARRAY(50..225) OF Integer;

            Tableau : T_Tab;

ex:  Tableau'RANGE retournera 50..225


Qu'est-ce qu'un sous-type?

Un sous-type est un sous-ensemble d'un type existant.   Il est totalement compatible et hérite de toutes les opérations permises de son type parent.  Par exemple le type "natural" est un sous-type du type "integer".  Pour définir un sous-type, il faut utiliser le mot réservé SUBTYPE, donner le type parent et une contrainte d'intervalle si nécessaire avec le mot réservé RANGE.

ex:  SUBTYPE Natural IS Integer RANGE 0..Integer'Last

ex:

      TYPE T_Jour_Semaine IS (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi);

      TYPE T_Tab IS ARRAY (T_Jour_Semaine) OF Integer;

      Tableau : T_Tab;

Tableau est un tableau de 7 cases dont le nom des cases est représenté par les valeurs du type T_Jour_Semaine.  Il est donc légal de faire :

        FOR Jour IN T_Jour_Semaine LOOP

                 Tableau(Jour) := 0;

        END LOOP;


Le type intervalle

Dans le langage Ada, un intervalle c'est 2 valeurs séparées par deux points consécutifs(..).  Ceci signifie les valeurs comprises entre les valeurs nommées, incluant ces valeurs.

ex: 10..100  signifie toutes les valeurs de 10 jusqu'à 100 inclusivement.

On peut définir des types et des sous-types de la catégorie des intervalles en utilisant le mot réservé RANGE.  Dès lors, toutes les variables ou constantes définies comme faisant partie de cette catégorie, ne pourront pas avoir de valeur ne faisant pas partie de l'intervalle défini.  Si cette contrainte n'est pas respectée, une exception CONSTRAINT_ERROR est levée.

ex:  TYPE T_Inter IS NEW Integer RANGE 10..100;           OU

      SUBTYPE T_Inter IS Integer RANGE 10..100;



Les exceptions

Une exception est levée lorsqu'une erreur survient lors de l'exécution d'un programme.  Ce genre d'erreurs n'est pas détectables comme une erreur de compilation, il faut absolument que le programme soit en cours d'exécution pour que ces problèmes puissent. survenir.

Voici quelques remarques pour la gestion des exceptions :

Les erreurs classiques :

***REMARQUE

Toutes les erreurs ne se règlent pas nécessairement avec les exceptions et si on peut, vaut mieux éviter de s'en servir.  La gestion des exceptions ajoute du traitement à votre programme et ralentit l'exécution.