Cours complet pour apprendre à programmer en D


précédentsommairesuivant

18. Tableaux

Nous avons défini cinq variables dans un des exercices du dernier chapitre et nous les avons utilisés dans certains calculs. Les définitions de ces variables étaient les suivantes :

 
Sélectionnez
double valeur_1;
double valeur_2;
double valeur_3;
double valeur_4;
double valeur_5;

Cette méthode de définir des variables individuellement n'est pas du tout efficace dans les cas où il y a besoin d'encore plus de variables. Imaginez qu'on ait besoin d'un millier de valeurs ; il est pratiquement impossible de définir 1000 variables de valeur_1 à valeur_1000.

Les tableaux sont utiles dans de tels cas : les tableaux permettent la définition de beaucoup de valeurs ensemble. Les tableaux sont aussi la structure de données la plus fréquente quand de multiples valeurs sont utilisées ensemble en tant que collection.

Ce chapitre couvre seulement quelques-unes des fonctionnalités des tableaux. Plus de fonctionnalités seront introduites dans un chapitre ultérieur.

18-1. Définition

La définition de tableaux est vraiment similaire à la définition de variables. La seule différence est que le nombre de variables qui sont définies en même temps est indiqué entre crochets. On peut distinguer les deux définitions suivantes :

 
Sélectionnez
int uneSimpleVariable;
int[10] tableauDeDixVariables ;

La première ligne ci-dessus est la définition d'une simple variable, tout à fait comme les variables que nous avons définies jusqu'alors. La deuxième ligne est la définition d'un tableau consistant en dix variables.

De même, l'équivalent des cinq variables séparées de l'exercice peut être défini comme un tableau de cinq variables en utilisant la syntaxe suivante :

 
Sélectionnez
double[5] valeurs;

Cette définition peut être lue comme cinq valeurs double. Notez qu'un pluriel a été choisi pour le nom du tableau pour éviter de le confondre avec une variable simple.

En résumé, la définition d'un tableau se compose du type des variables, de leur nombre et du nom du tableau :

 
Sélectionnez
nom_du_type[nombre_de_variables] nom_du_tableau;

Le type des variables peut aussi être un type défini par le programmeur (nous verrons les types définis par le programmeur plus tard). Par exemple :

 
Sélectionnez
// Un tableau qui stocke l'information météorologique de
// toutes les villes. Ici, les valeurs booléennes veulent dire :
//   false : couvert
//   true  : ensoleillé
bool[nombreDeVilles] ConditionsMeteorologiques;
 
// Un tableau qui stocke le poids de cent boîtes
double[100] poidsDesBoites;
 
// Information à propos des étudiants d'une école
DonneeEtudiant[NombredDEtudiants] DonneesEtudiants;

18-2. Éléments et conteneurs

Les structures de données qui rassemblent des éléments d'un certain type sont appelées conteneurs. Selon cette définition, les tableaux sont des conteneurs. Par exemple, un tableau qui stocke les températures de l'air des jours de juillet peut rassembler 31 variables double et forme un conteneur d'éléments de type double.

Les variables d'un conteneur sont appelées éléments. Le nombre d'éléments d'un tableau est appelé la longueur d'un tableau.

18-3. Accéder aux éléments

Pour différencier les variables dans l'exercice du chapitre précédent, nous devions ajouter un tiret du bas et un nombre à leur nom (par exemple, valeur_1). Ce n'est ni possible, ni nécessaire quand les variables sont définies ensemble comme un seul tableau avec un seul nom. On accède aux éléments en indiquant leur numéro entre crochets :

 
Sélectionnez
valeurs[0]

Cette expression peut être lue comme « l'élément numéro 0 du tableau de nom « valeurs » ». En d'autres termes, au lieu de taper valeur_1, on tape valeurs[0] avec les tableaux. Il y a deux points importants qu'il vaut la peine de relever :

  • les numéros commencent à 0 : même si les humains comptent à partir de 1, les indices de tableaux commencent à 0. Les valeurs que nous avons numérotées 1, 2, 3, 4 et 5 sont numérotées 0, 1, 2, 3 et 4 dans le tableau. Cette spécificité est une cause de nombreuses erreurs de programmation ;
  • deux utilisations différentes des crochets : ne les confondez pas. Quand on définit un tableau, les crochets sont écrits après le type des éléments et indiquent le nombre d'éléments. Quand on accède aux éléments, les crochets sont écrits après le nom du tableau et indiquent le numéro de l'élément auquel on accède :
 
Sélectionnez
// Ceci est une définition. Elle définit un tableau qui consiste
// en 12 éléments. Ce tableau est utilisé pour stocker le nombre
// de jours de chaque mois.
int[12] joursDuMois;
 
// Ceci est un accès. On accède à l'élément qui
// correspond à décembre et on lui donne la valeur 31.
joursDuMois[11] = 31;
 
// Ceci est un autre accès. On accède à l'élément qui
// correspond à janvier, sa valeur est passé à writeln.
writeln("Janvier a ", joursDuMois[0], " jours.");

Rappel : les numéros des éléments de janvier et décembre sont respectivement 0 et 11, non 1 et 12.

18-4. Indice

Le numéro d'un élément est appelé son indice.

Un indice n'a besoin d'être une valeur constante ; la valeur d'une variable peut aussi être utilisée comme un indice, ce qui rend les tableaux encore plus utiles. Par exemple, le mois est déterminé par la valeur de la variable IndiceMois ci-dessous :

 
Sélectionnez
writeln("Ce mois a ", joursDuMois[IndiceMois], " jours.");

Quand la valeur de IndiceMois est 2, l'expression ci-dessus affiche la valeur de joursDuMois[2], le nombre de jours du mois de mars.

Seuls les indices entre 0 et la longueur du tableau moins 1 sont valides. Par exemple, les indices valides d'un tableau à 3 éléments sont 0, 1 et 2. Accéder à un tableau avec un mauvais indice entraîne l'arrêt du programme avec une erreur.

Les tableaux sont des conteneurs dans lesquels les éléments sont placés les uns à côté des autres dans la mémoire de l'ordinateur. Par exemple, les éléments du tableau qui stocke le nombre de jours dans chaque mois peuvent être vus comme ceci :

Image non disponible

L'élément d'indice 0 a la valeur 31 (nombre de jours en janvier) ; l'élément d'indice 1 a la valeur 28 (nombre de jours en février), etc.

18-5. Tableaux de taille fixe vs tableaux dynamiques

Quand la taille du tableau est indiquée lorsque le programme est écrit, ce tableau est de taille fixe. Quand la longueur peut changer pendant l'exécution du programme, ce tableau est dynamique.

Les tableaux que nous avons définis au-dessus sont des tableaux à taille fixe parce que leur nombre d'éléments est indiqué lors de l'écriture du programme (5 et 12). Les longueurs de ces tableaux ne peuvent pas être changées pendant l'exécution du programme. Pour changer leur longueur, le code source doit être modifié et le programme doit être recompilé.

Définir un tableau dynamique est plus simple que définir un tableau à taille fixe ; il suffit d'omettre la taille et on obtient un tableau dynamique :

 
Sélectionnez
int[] tableauDynamique;

La taille d'un tel tableau peut augmenter ou diminuer pendant l'exécution du programme.

18-6. .length pour récupérer ou changer la taille du tableau

Les tableaux ont également des attributs. Ici, nous ne verrons que .length, qui retourne le nombre d'éléments du tableau.

 
Sélectionnez
writeln("Le tableau a", tableau.length, " éléments.");

De plus, la taille des tableaux dynamiques peut être modifiée en affectant une valeur à cet attribut :

 
Sélectionnez
int[] array;         // initialement vide
tableau.length = 5;  // maintenant, il y a 5 éléments

Voyons maintenant comment on pourrait réécrire l'exercice avec les cinq valeurs en utilisant un tableau :

 
Sélectionnez
import std.stdio;
 
void main()
{
   // Cette variable est utilisée en tant que compteur dans une boucle
   int compteur;
 
   // La définition d'un tableau à taille fixe de cinq
   // éléments de type double
   double[5] valeurs;
 
   // récupérer les valeurs dans une boucle
   while (compteur < valeurs.length) {
      write("Valeur ", compteur + 1, " : ");
      readf(" %s", &valeurs[compteur]);
      ++compteur;
   }
 
   writeln("Le double des valeurs :");
   compteur = 0;
   while (compteur < valeurs.length) {
      writeln(valeurs[compteur] * 2);
      ++compteur;
   }
 
   // La boucle qui calcule le cinquième des valeurs
   // serait écrite de façon similaire
}

Observations : la valeur de compteur détermine combien de fois les boucles sont répétées (itérées). Itérer la boucle tant que cette valeur est strictement inférieure à valeurs.length assure que la boucle est exécutée une fois par élément. Comme la valeur de cette variable est incrémentée à la fin de chaque itération, l'expression valeurs[compteur] fait référence à chaque élément du tableau un par un : valeurs[0], valeurs[1], etc.

Pour voir à quel point ce programme est meilleur que le précédent, imaginez que vous ayez besoin de 20 valeurs. Le programme ci-dessus nécessiterait une seule modification : remplacer 5 par 20 ; alors que le programme qui n'utilisait pas de tableau aurait eu besoin de 15 définitions de variables de plus, idem pour les lignes dans lesquelles elles sont utilisées.

18-7. Initialiser les éléments

Comme toute variable en D, les éléments des tableaux sont automatiquement initialisés. La valeur initiale des éléments dépend du type des éléments : 0 pour int, double.nan pour double, etc.

Tous les éléments du tableau valeurs ci-dessus sont initialisés à double.nan :

 
Sélectionnez
double[5] valeurs;   // les éléments valent tous double.nan

Évidemment, les valeurs des éléments peuvent être modifiées plus tard dans le programme. On a déjà vu cela ci-dessus quand on a affecté une valeur à un élément de tableau :

 
Sélectionnez
joursDuMois[11] = 31;

Et aussi quand on récupérait une valeur depuis l'entrée :

 
Sélectionnez
readf(" %s", &valeurs[compteur]);

Parfois, les valeurs souhaitées des éléments sont connues au moment où le tableau est défini. Dans de tels cas, les valeurs initiales des éléments peuvent être indiquées du côté droit de l'opérateur d'affectation, entre crochets. Voyons cela dans un programme qui demande le numéro du mois à l'utilisateur et qui affiche le nombre de jours de ce mois :

 
Sélectionnez
import std.stdio;
 
void main()
{
   int[12] joursDuMois =
      [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
 
   write("Veuillez saisir le numéro du mois : ");
   int numeroMois;
   readf(" %s", &numeroMois);
 
   int indice = numeroMois - 1;
   writeln("Le mois ", numeroMois, " a ",
            joursDuMois[indice], " jours.");
}

Comme vous pouvez le voir, le tableau joursDuMois est défini et initialisé au même moment. Notez aussi que le numéro du mois, qui est entre 1 et 12, est converti en un indice valide entre 0 et 11. Toute valeur entrée en dehors de l'intervalle 1-12 entraînerait l'arrêt du programme avec une erreur.

Quand on initialise des tableaux, il est possible d'utiliser une seule valeur sur le côté droit. Dans ce cas, tous les éléments du tableau sont initialisés à cette valeur :

 
Sélectionnez
int[10] tousUn = 1;    // Tous les éléments valent 1

18-8. Opérations basiques sur les tableaux

Les tableaux proposent des opérations pratiques qui s'appliquent à tous leurs éléments.

  • Copier des tableaux à taille fixe : l'opérateur d'affectation copie tous les éléments du tableau de droite dans le tableau de gauche :
 
Sélectionnez
    int[5] source = [ 10, 20, 30, 40, 50 ];
    int[5] destination;
     
    destination = source;

La signification de l'opération d'affectation est complètement différente pour les tableaux dynamiques. Nous verrons cela dans un chapitre ultérieur.

  • Ajouter des éléments aux tableaux : l'opérateur ~= ajoute un nouvel élément ou un nouveau tableau à la fin du tableau dynamique :
 
Sélectionnez
    int[] tableau;               // vide
    tableau ~= 7;                // un élément
    tableau ~= 360;              // deux éléments
    tableau ~= [ 30, 40 ];       // 4 éléments
  • Il n'est pas possible d'ajouter des éléments à un tableau à taille fixe :
 
Sélectionnez
    int[10] tableau;
    tableau ~= 7;                //  ERREUR de compilation
  • Combiner des tableaux : l'opérateur ~ crée un nouveau tableau en combinant deux tableaux. Son équivalent ~= combine deux tableaux et affecte le résultat au tableau de gauche :
 
Sélectionnez
    import std.stdio;
     
    void main()
    {
       int[10] premier = 1;
       int[10] second = 2;
       int[] resultat;
     
       resultat = premier ~ second;
       writeln(resultat.length);   // affiche 20
     
       resultat ~= premier;
       writeln(resultat.length);   // affiche 30
    }

L'opérateur ~= ne peut pas être utilisé quand le tableau de gauche est de taille fixe :

 
Sélectionnez
int[20] resultat;
// ...
resultat ~= premier;          //  ERREUR de compilation

Si le tableau de gauche n'a pas exactement la même taille que le tableau résultat, le programme se termine avec une erreur pendant l'affectation :

 
Sélectionnez
int[10] premier = 1;
int[10] second = 2;
int[21] resultat;
 
resultat = premier ~ second;

Sortie :

 
Sélectionnez
object.Error: Array lengths don't match for copy: 21 != 20

Si on traduit : « les longueurs ne correspondent pas pour la copie de tableau. »

18-9. Trier les éléments

std.algorithm.sort trie les éléments de plages à accès directs. Dans le cas des entiers, les éléments sont triés du plus petit au plus grand. Pour utiliser sort(), il est nécessaire d'importer le module std.algorithm :

 
Sélectionnez
import std.stdio;
import std.algorithm;
 
void main()
{
    int[] tableau = [ 4, 3, 1, 5, 2 ];
    sort(tableau);
    writeln(tableau);
}

La sortie :

 
Sélectionnez
[1, 2, 3, 4, 5]

18-9-1. Inverser les éléments

std.algorithm.reverse inverse les éléments sur place (le premier élément devient le dernier, etc.) :

 
Sélectionnez
import std.stdio;
import std.algorithm;
 
void main()
{
    int[] array = [ 4, 3, 1, 5, 2 ];
    reverse(array);
    writeln(array);
}

La sortie :

 
Sélectionnez
[2, 5, 1, 3, 4]

18-9-2. Exercices

  1. Écrivez un programme qui demande à l'utilisateur combien de valeurs vont être entrées et ensuite les lit toutes. Le programme doit trier ces éléments en utilisant .sort et .reverse.
  2. Écrivez un programme qui lit des nombres depuis l'entrée, et affiche les nombres pairs et impairs séparément, mais dans l'ordre. La valeur spéciale -1 termine la liste des nombres et ne fait pas partie de la liste.
    Par exemple, quand les nombres suivants sont entrés :

     
    Sélectionnez
    1 4 7 2 3 8 11 -1
  3. Le programme affiche ceci :

     
    Sélectionnez
    1 3 7 11 2 4 8

    Vous pouvez vouloir enregistrer les éléments dans des tableaux séparés. Vous pouvez déterminer si un nombre est pair ou impair en utilisant l'opérateur % (modulo).

  4. Ce qui suit est un programme qui ne marche pas comme attendu. Le programme est écrit pour lire cinq nombres depuis l'entrée et placer les carrés de ces nombres dans un tableau. Le programme essaie ensuite d'afficher les carrés dans la sortie. Au lieu de ça, le programme se termine avec une erreur.
    Corrigez les bogues de ce programme et faites-le marcher comme ce qui est attendu :
 
Sélectionnez
    import std.stdio;
     
    void main()
    {
        int[5] carres;
     
        writeln("Veuillez entrer 5 nombres");
     
        int i = 0;
        while (i <= 5) {
              int nombre;
              write("Nombre ", i + 1, ": ");
              readf(" %s", &nombre);
     
              carres[i] = nombre * nombre;
              ++i;
        }
     
        writeln("=== Les carrés des nombres ===");
        while (i <= carres.length)
        {
              write(carres[i], " ");
              ++i;
        }
     
        writeln();
    }

Les solutionsTableaux - Correction.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+