Aller au menu - Aller au contenu

Icône Les tableaux

Par Avatar Yno
Mise à jour : 09/07/2010
Difficulté : Facile Facile
170 visites depuis 7 jours, dont 7 sur ce chapitre classé 446/786
On est souvent amené à manipuler un ensemble de données plutôt qu'une donnée isolée. Par exemple, un annuaire est un ensemble de noms et de numéros de téléphone ; dans un logiciel de messagerie instantanée, on trouve généralement une fenêtre ou une colonne constituée d'une liste de contacts, et ainsi de suite. Jusqu'à maintenant, nous n'avons manipulé que des données isolées, comme demander son nom à l'utilisateur ou tester si son âge était supérieur à 18 ans. Mais, vous vous en doutez peut-être, il est différent en informatique de traiter une donnée simple et un ensemble de données.

On appelle ces "ensembles" des structures de données. Ce sont des données également, qui peuvent être manipulées comme un seul bloc, mais aussi permettre d'accéder à leurs éléments (un peu comme on peut accéder individuellement aux caractères d'une chaîne). Dans l'annuaire ou la liste de contacts qui précèdent, les individus sont rangés les uns à la suite des autres ; en fait, on peut les numéroter 0, 1, 2... et former ce que l'on appelle, en informatique, un tableau. Dans ce chapitre, nous allons précisément voir comment cette structure de données s'utilise en Vala ;) .
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire

Des tableaux de données

Les tableaux (arrays en anglais) sont des ensembles de cases qui servent à contenir d'autres éléments, et dont chacune possède un numéro appelé indice. En Vala, ils contiennent des éléments d'un même type : vous pouvez manipuler un tableau d'entiers, un tableau de chaînes ou un tableau d'un de vos propres types (nous reviendrons là-dessus dans quelques chapitres), mais jamais de mélanges dans un même tableau. Ceci est une sécurité nécessaire à Vala pour assurer qu'un programme compilé ne contiendra pas d'erreurs de types. Un tableau est de taille finie, et ses éléments sont numérotés, comme les caractères d'une chaîne, de 1 en 1 à partir de 0.

Il est très simple de déclarer une variable contenant un tableau : on fait suivre le nom du type des éléments de crochets, par exemple int[] tableau; . Pour réserver de la place pour ce tableau, on utilise un nouveau mot-clef, new , suivi d'une indication de la taille du tableau : il suffit en fait d'écrire Type[Taille] pour que l'espace mémoire nécessaire soit réservé, et que le nouvel objet correspondant au tableau soit réservé. Le tableau peut alors être utilisé directement.

Voici un exemple : nous déclarons un tableau d'entiers nommé tableau, et en construisons un de taille 2 (donc ses éléments sont accessibles aux indices 0 et 1). Nous le remplissons ensuite à la main puis nous affichons ces éléments :

Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Main
{
  public static int main (string[] args)
  {
    int[] tableau = new int[2];
    
    tableau[0] = 4;
    tableau[1] = 2;
    
    stdout.printf("%d%d", tableau[0], tableau[1]);
    
    return 0;
  }
}


Il est également possible de déclarer littéralement un tableau, en écrivant directement son contenu. Par exemple :

Code : Vala
1
2
3
string[] phrase = {"Bonjour", "les", "Zéros"}; // Prenons des chaînes pour changer un peu...

stdout.printf("%s %s %s", phrase[0], phrase[1], phrase[2]); // Affiche Bonjour les Zéros


Enfin, on peut modifier un élément d'un tableau en écrivant tableau[indice] = nouvel_élément; . Bien sûr, celui-ci doit être du bon type. Dans l'exemple précédent, ajoutez avant l'affichage une ligne du genre phrase[2] = "amis"; pour voir afficher Bonjour les amis.

Dimension d'un tableau

La taille d'un tableau peut, à tout moment, être récupérée dans son attribut length, comme c'est le cas pour une chaîne de caractères. Cependant, même si Vala la connaît à tout instant, le langage ne vérifie pas que vous tentez bien d'accéder à un élément du tableau, et que vous ne dépassez pas les limites.

En d'autres termes, si nous tentons d'utiliser un élément situé à un indice fantasque, qui ne fait pas partie du tableau, nous allons tomber sur un bout de mémoire qui appartient à un autre objet, et ceci peut avoir des conséquences imprévisibles. Il est donc important de vérifier vous-même que ce que vous faites a un sens.

Il est également possible de rajouter des éléments à un tableau, ce qui provoquera éventuellement son redimensionnement automatique :

Code : Vala
1
2
3
4
5
6
7
8
string[] phrase = {"Bonjour", "les"};

stdout.printf("Longueur du tableau : %d\n", phrase.length);

phrase += "Zéros"; // On rajoute l'élément "Zéros"

stdout.printf("Longueur du tableau : %d\n", phrase.length);   
stdout.printf("%s %s %s", phrase[0], phrase[1], phrase[2]);


Cependant, lors d'une telle opération, le tableau est intégralement recopié en mémoire à un nouvel endroit : c'est une opération qui peut prendre du temps quand il y a beaucoup d'éléments. Nous verrons plus tard qu'il existe de nombreuses autres structures de données facilement accessibles en Vala, qui peuvent être bien plus adaptées quand vous risquez de rajouter des éléments au fur et à mesure (et d'en enlever d'autres).

Enfin, il est possible d'utiliser des tableaux à plusieurs dimensions. Par exemple, un tableau de dimension 2 (lignes et colonnes) peut être défini et utilisé ainsi :

Code : Vala
1
2
3
4
5
int[,] d = {{2, 4, 6, 8},
            {3, 5, 7, 9},
            {1, 3, 5, 7}};

stdout.printf("%d", d[1, 2]);


Ceci affichera 7 : d[1,2] correspond à l'élément d'indice 2 de la ligne d'indice 1. Pour l'instant, nous n'avons pas vraiment besoin de tableaux à plusieurs dimensions, mais ils nous serviront plus tard, par exemple pour représenter la carte d'un jeu vidéo (où chaque case du tableau représentera une case de la carte...).

Parcourir un tableau

Pour l'instant, pour afficher le contenu d'un tableau, nous avons dû explicitement écrire toutes les cases dont nous voulions afficher le contenu. Vous l'aurez deviné, c'est très mauvais : on perd totalement l'avantage d'utiliser une même variable pour contenir un ensemble de données.

Fort heureusement, pour afficher le contenu d'un tableau, il nous suffit d'afficher le contenu de ses cases une par une. En d'autres termes, de faire varier une variable (représentant l'indice de la case qu'on est en train d'examiner) de 0 jusqu'à la longueur du tableau. Et ça, c'est le rôle... des boucles, bien sûr !

Il est très fréquent de devoir effectuer une action particulière sur toutes les cases d'un tableau, les unes après les autres. On dit alors que l'on réalise un parcours du tableau. Cette action peut-être un affichage, une modification du tableau, ou encore la recherche d'un élément (nous verrons des exemples). Et pour ça, il est normal de vouloir faire varier un indice au moyen d'une boucle. De plus, selon ce qui précède, nous devinons facilement que les bornes entre lesquelles ont fera varier le compteur de la boucle sont 0 et tableau.length.

Exécutez par exemple le programme suivant :

Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Main
{
  public static int main (string[] args)
  {
    int[] tableau = {1, 6, 3, 12, 6, 2};
    int i;
    
    for (i = 0; i < tableau.length; i++)
    {
        stdout.printf("Case %d : %d\n", i, tableau[i]);
    }
    
    return 0;
  }
}


Le résultat sera

Code : Console
Case 0 : 1
Case 1 : 6
Case 2 : 3
Case 3 : 12
Case 4 : 6
Case 5 : 2


Une boucle for est donc parfaitement adaptée pour faire varier un compteur et visiter toutes les cases d'un tableau. Et les tableaux nous donnent une excellente raison de nous entraîner à utiliser les boucles, n'est-ce pas ;) ?

Voici donc un autre exemple. Supposons que vous cherchiez la position d'un nombre (représenté par une variable) dans un tableau ; il va vous falloir explorer les cases de ce tableau une par une. Mais dès que vous avez trouvé ce nombre, inutile de continuer à chercher, c'est une perte de temps. Comment, alors, sortir de la boucle ? Deux solutions : utiliser un booléen, ou l'instruction break . Par exemple, la deuxième :

Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Main
{
  public static int main (string[] args)
  {
    int[] tableau = {1, 6, 3, 12, 6, 2};
    int e = 12; // L'élément cherché (on pourrait le demander à l'utilisateur)
    int i;
    
    for (i = 0; i < tableau.length; i++)
    {
        if (tableau[i] == e)
        {
            stdout.printf("Trouvé en position %d", i);
            break; // On sort de la boucle !
        }
    }
    
    return 0;
  }
}


Vous avez normalement tout ce qu'il faut pour résoudre les exercices suivants. Essayez de les faire : même si vous n'y arrivez pas, le fait de chercher vous entraîne, et aide à vous approprier le cours. En cas de problème, il y aura toujours quelques programmeurs Vala pour vous aider sur le forum Autres Langages ou sur le forum GTK+ du site Developpez.com (où on trouve notamment le créateur de Val(a)ide).

Exercices (rangés par difficulté croissante) :

  • Ecrivez un programme qui parcourt un tableau d'entiers pour afficher le double de ses éléments (case par case).
  • Ecrivez un programme qui parcourt un tableau d'entiers en cherchant le plus grand, et qui l'affiche ensuite à l'utilisateur. Puis écrivez un programme qui parcourt un tableau de chaînes en cherchant la plus longue. Puis écrivez un programme qui calcule la moyenne d'un tableau d'entiers (pour rappel, elle se calcule comme la somme de tous les éléments divisée par le nombre d'éléments du tableau).
  • Ecrivez un programme qui permet à l'utilisateur de remplir un tableau d'entiers case par case : demandez-lui d'abord la taille du tableau, puis réservez de la place pour ce tableau à l'aide de new int[ ] , et enfin à l'aide d'une boucle remplissez ce tableau nombre par nombre. Encore plus fort : à l'aide de la méthode split des chaînes, permettez à l'utilisateur de rentrer toutes les valeurs d'un tableau sur une même ligne. Cet exercice est très formateur car il réutilise beaucoup de notions que nous avons déjà rencontrées ;) .


Quelques corrigés (mais que ça ne vous empêche pas de chercher !) :

  • Recherche du maximum :
    Secret (cliquez pour afficher)
    Nous aurons besoin d'une variable compteur pour parcourir le tableau, ainsi que d'une variable supplémentaire pour stocker le maximum. Comment le déterminer ? Il suffit de regarder chaque case l'une après l'autre en comparant son contenu au maximum rencontré jusqu'à présent. Lorsqu'on aura étudié toutes les cases, on aura bien le maximum de tout le tableau :

    Code : Vala
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    int i;
    int maxi = tableau[0];
    
    for (i = 1; i < tableau.length; i++)
    {
        if (tableau[i] > maxi) // Si on trouve un élément plus grand...
        { 
            maxi = tableau[i];
        }
    }
    
    stdout.printf("Maximum : %d\n", maxi);
    


    Notez qu'il faut bien initialiser la variable maxi avant de rentrer dans la boucle. Mais quelle valeur choisir ? On prend le contenu de la première case : ainsi, on est sûr de ne pas choisir une valeur par défaut trop grande. Bien sûr, si vous écrivez le contenu du tableau dans le code pour tester, la question ne se pose pas (vous pouvez toujours choisir une valeur initiale pour maxi qui convient).
  • Calcul de la moyenne :
    Secret (cliquez pour afficher)
    Cela n'est pas vraiment différent de ce qui précède. La première chose à faire est de sommer toutes les cases dans une même variable pour calculer la somme. On n'aura plus qu'à afficher le résultat à l'utilisateur après division.
    Code : Vala
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Main
    {
      public static int main (string[] args)
      {
        int[] tableau = {1, 8, 3, 12, 6, 1};
        int i, somme = 0;
        
        for (i = 0; i < tableau.length; i++)
        {
            somme = somme + tableau[i];
        }
        
        stdout.printf("Maximum : %f\n", (float) somme/tableau.length);
        
        return 0;
      }
    }
    
  • Lire un tableau entré par l'utilisateur :
    Secret (cliquez pour afficher)
    Il est important que vous compreniez bien cet exercice, au moins sous sa première forme, car il montre avec quelle facilité la mémoire se gère en Vala. D'abord, il faut demander un nombre à l'utilisateur, qui représente la taille du tableau. Nous savons le faire, il suffit d'utiliser scanf

    Mais comment, après, créer un tableau de la bonne taille et le remplir ? Il suffit d'utiliser new int[ ] : si on a stocké dans une variable n la taille rentrée par l'utilisateur, on écrira new int[n] pour réserver l'emplacement nécessaire en mémoire pour le tableau. À l'aide d'une boucle for et d'une instruction appelant scanf à chaque tour de boucle, nous pouvons ensuite lire nos n entiers.

    Code : Vala
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class Main
    {
      public static int main (string[] args)
      {
        int[] tableau;
        int i, n, entree;
        
        stdout.printf("Entrez la taille du tableau :\n");
        stdin.scanf("%d", out n);
        
        tableau = new int[n];
        
        stdout.printf("Saisie du tableau, un entier sur chaque ligne...");
        for (i = 0; i < n; i++)
        {
            stdin.scanf("%d", out entree); // On ne peut pas utiliser out sur
            tableau[i] = entree;           // une case de tableau
        }
        
        stdout.printf("Affichage du tableau :\n");
        for (i = 0; i < n; i++)
        {
            stdout.printf("%d ", tableau[i]); // On affiche tous les éléments sur
        }                                     // une même ligne
        
        return 0;
      }
    }
    


    Notez que si nous n'utilisons pas la variable entree et que nous écrivons stdin.scanf("%d", out tableau[i]); , Vala nous dit que c'est interdit. Nous passons donc par une variable particulière qui nous sert à lire les nombres entrés.

Une nouvelle boucle pour nos tableaux

Il existe une boucle que nous n'avons pas encore vue qui simplifie le parcours des tableaux : la boucle foreach ("pour tout" en anglais). Son utilisation est très simple, et peu utile sur les tableaux quand nous disposons déjà de la boucle for - mais autant en parler tout de suite, car sur d'autres structures de données elles se révèleront plus pratiques.

La syntaxe est la suivante : foreach (variable in tableau) {...} . Dans le bloc, la variable peut être utilisée librement, et elle prend à chaque tour de boucle les valeurs contenues dans le tableau. Par exemple, pour afficher les valeurs d'un tableau sur une seule ligne :

Code : Vala
1
2
3
4
foreach (int i in tableau)
{
    stdout.printf("%d ", i);
}


Reprenez les exercices précédents. Certains se traduisent bien en utilisant cette nouvelle boucle, et d'autres noms. Lesquels, et pourquoi selon vous ?

Un tableau bien connu : les arguments de la ligne de commande

Il y a un tableau que nous trimballons depuis notre premier programme Vala sans jamais l'avoir utilisé... vous ne voyez pas lequel ? Regardez mieux la ligne public static int main (string[] args) , à quoi peut bien servir args ?

C'est en fait un tableau particulier, défini au lancement de votre programme, qui sert à récupérer les différents arguments de la ligne de commandes. Vous savez certainement que, bien avant de disposer d'interfaces graphiques, les ordinateurs proposaient une ligne de commandes, c'est-à-dire une interface en mode texte où l'utilisateur devait taper les actions à réaliser.

Aujourd'hui, l'utilisateur lambda ne se sert plus trop (voire plus du tout) de cette interface textuelle, mais, comme nous qui sommes curieux, elle n'a pas de raison de nous faire peur. Ainsi, quand nous tapons une commande (par exemple echo Bonjour les amis), un certain programme est appelé (echo), et il reçoit des arguments, comme une fonction, qu'il utilise à sa façon (en l'occurrence, les afficher).

Sous Windows comme sous Linux, vous disposez d'une ligne de commande. Elle peut être un moyen très efficace de scripter votre système, c'est-à-dire d'enregistrer dans un fichier une série d'actions qui vous aident à automatiser votre environnement (par exemple, télécharger automatiquement un fichier distant mis à jour périodiquement). Nous allons nous contenter ici de voir comment utiliser les arguments.

Comme la méthode main, qui correspond à la fonction principale de notre programme, reçoit un tableau de chaînes en argument, nous pouvons en afficher les éléments ligne par ligne pour voir comment il est construit :

Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Main
{
  public static int main (string[] args)
  {
    int i;
    
    for (i = 0; i < args.length; i++)
    {
        stdout.printf("Argument %d : %s\n", i, args[i]);
    }
    
    return 0;
  }
}


Exécutons-le maintenant en ligne de commande : même si vous utilisez un IDE, laissez-le pour une fois de côté et aller chercher votre exécutable à la main pour le faire glisser dans une console DOS/dans un terminal, puis appuyez sur entrée. Supposons que vous l'ayez appelé "Programme.exe", vous verrez alors

Code : Console
Argument 0 : Programme.exe


En effet, le premier argument reçu par notre programme est son nom. Bon, pourquoi pas, essayons maintenant d'en faire un peu plus, en ajoutant des mots sur la ligne de commande après le nom de l'exécutable : par exemple, si nous entrons Programme.exe Bonjour les amis

Code : Console
Argument 0 : Programme.exe
Argument 1 : Bonjour
Argument 2 : les
Argument 3 : amis


Et si nous voulons que "Bonjour les amis" ne forme qu'un seul argument ? Il suffit de mettre des guillemets, en tapant Programme.exe "Bonjour les amis" :

Code : Console
Argument 0 : Programme.exe
Argument 1 : Bonjour les amis


Nous construirons bientôt des programmes graphiques, qui se passent de la ligne de commande et sont certainement moins déroutants pour l'utilisateur. Mais il faut bien comprendre que, pour nous programmeurs, cette interface textuelle est une façon simple de tester des choses ou de réaliser de petits utilitaires.

Par exemple, sous Unix, on trouve généralement les programmes curl ou wget qui permettent de récupérer facilement des pages web. Utilisés conjointement avec une boucle for (dont disposent les lignes de commandes DOS aussi bien que Unix), cela peut permettre de récupérer rapidement un certain nombre de pages (plus facilement qu'en devant cliquer sur Enregistrer sous à chaque fois). Nous verrons qu'un tel programme n'est pas difficile à réaliser en Vala.

Une dernière chose : en ligne de commande, vous pouvez envoyer la sortie (c'est-à-dire l'affichage) de votre programme dans un fichier, en écrivant programme arguments > sortie.txt. Mais attention, en faisant ainsi vous effacerez le contenu précédent de "sortie.txt". Pour ajouter des informations à la fin plutôt que d'effacer ce qui s'y trouve, utilisez ">>".
Chapitre précédent Sommaire

Partager

4 commentaires pour "Les tableaux"
Note moyenne : 3.63 / 4 (35 votes)
Pseudo Commentaire
En ligne antoyo # Posté le 11/07/2010 à 17:31:12
GNU/Linux forever!
Avatar

Études : Cégep de rimouski

Salut,
j'ai lu tout le tutoriel et je le trouve super !

J'aimerais également faire quelques petites remarques sur plusieurs chapitres.

En Vala, comme en C, il est possible de ne pas utiliser les accolades pour un bloc qui ne comporte qu'une seule ligne.
Par exemple :
Code : Vala
1
2
3
4
if(nombre % 2 == 0)
  stdout.printf("%d est pair.\n", nombre);
else
  stdout.printf("%d est impair.\n", nombre);

Il sera bien, je crois, de le préciser.

En outre, il est possible de déclarer la variable (i) dans une boucle for, comme en C++ :
Code : Vala
1
2
for(int i = 0 ; i < tableau.length ; i++)
  stdout.printf("%d\n", tableau[i]);

Vous avez fait ceci pour la boucle foreach , mais sans le préciser.

Ensuite, il est dit que ceci est interdit :
Code : Vala
1
stdin.scanf("%d", out tableau[i]);

Après avoir vu cela, je l'ai essayé... et ça fonctionne.

Puis, si nous utilisons stdin.scanf() et ensuite stdin.read_line() , le programme nous demande seulement le premier...

Par exemple, ce code ne fonctionne pas correctement :
Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Main {
  public static int main(string[] args) {
    int nombre;
    string chaine;
    
    stdout.printf("Entrez un nombre :\n");
    stdin.scanf("%d", out nombre);
    stdout.printf("Vous avez entré %d.\n", nombre);
    
    stdout.printf("Entrez une ligne :\n");
    chaine = stdin.read_line();
    stdout.printf("Vous avez entré :\n%s.\n", chaine);
    
    return 0;
  }
}

Alors que celui-ci fonctionne correctement :
Code : Vala
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class Main {
  public static int main(string[] args) {
    int nombre;
    string chaine;
    
    stdout.printf("Entrez un nombre :\n");
    stdin.scanf("%d", out nombre);
    stdout.printf("Vous avez entré %d.\n", nombre);
    
    stdin.read_line(); //Il faut lire une ligne entre les deux.
    
    stdout.printf("Entrez une ligne :\n");
    chaine = stdin.read_line();
    stdout.printf("Vous avez entré :\n%s.\n", chaine);
    
    return 0;
  }
}


Enfin :D , les arguments du programme sont toujours utilisés dans les applications fenêtrées :
firefox les utilisent.

En effet, il est possible d'appeler firefox (en ligne de commande) comme ceci :
Code : Console
firefox http://www.google.ca/ http://www.google.fr/

Val(a)ide aussi les utilisent !

Je crois qu'il sera bien de préciser ces quelques détails que je viens de mentionner.

Bonne continuation pour votre tutoriel !

Mon extension pour Chrome/Chromium :
SaveAllPasswords : vous permet d’enregistrer vos mots de passe sur tous les sites Web (même ceux qui ne veulent pas :D ).
 
Hors ligne anonyme # Posté le 11/07/2010 à 17:57:32

D'abord merci pour le commentaire détaillé, c'est sympa d'avoir pris du temps pour l'écrire :) .

Ensuite :
- oui c'est vrai pour les instructions en dehors des accolades, mais c'est un peu anecdotique comme fait je trouve, pourquoi ne pas en parler après tout.
- pour la déclaration locale, j'y reviendrai prochainement quand je parlerai de la mémoire et de la portée.
- pour le out utilisé sur une case de tableau, j'imagine que c'est ma version de Vala qui est trop vieille, mais je maintiens que vala 0.7.10 l'a refusé :) .
- pour le coup du scanf/read_line, c'est le même problème qu'en C… je pense réécrire le début du cours sans utiliser scanf, en fait.
- et pour les arguments en ligne de commande… oui, c'est vrai.
Hors ligne Antoine2142 # Posté le 25/07/2011 à 19:50:17

Avis : Très bon

Dommage que la dernière mise a jour aie été faite en 2010.... es ce que quelqun connais un bon tuto complet sur le language vala? (désolé pour mon orthographe , je ne suis pas habitué à mon nouveau clavier =S)
Hors ligne Yno # Posté le 31/07/2011 à 15:03:11
Avatar
Flux RSS

Je dirais http://live.gnome.org/Vala/Tutorial ? :]
 

Voir tous les commentaires