PERLLOL

Section: User Contributed Perl Documentation (1)
Updated: 2001-09-05
Index Retour au Menu Principal

 

NOM

perllol - Manipulaton des Tableaux de Tableaux en Perl  

DESCRIPTION

 

Déclaration et Accès aux Tableaux de Tableaux

La chose la plus simple à construire est un tableau de tableaux (parfois appelé de façon peu précise une liste de listes). C'est raisonnablement facile à comprendre, et presque tout ce qui s'y applique pourra aussi être appliqué par la suite aux structures de données plus fantaisistes.

Un tableau de tableau est juste un bon vieux tableau @TdT auquel vous pouvez accéder avec deux indices, comme $TdT[3][2]. Voici une déclaration du tableau :

    # affecte à notre tableau un tableau de références à des tableaux
    @TdT = (
           [ "fred", "barney" ],
           [ "george", "jane", "elroy" ],
           [ "homer", "marge", "bart" ],
    );

    print $TdT[2][2];
  bart

Maintenant, vous devez faire bien attention au fait que les parenthèses extérieures sont bien des parenthèses et pas des accolades ou des crochets. C'est parce que vous affectez à un @tableau, vous avez donc besoin de parenthèses. Si vous n'aviez pas voulu que cela soit un @TdT, mais plutôt juste une référence à elle, vous auriez pu faire quelque chose du style de ceci :

    # affecte une référence à une liste de références de liste
    $ref_to_TdT = [
        [ "fred", "barney", "pebbles", "bambam", "dino", ],
        [ "homer", "bart", "marge", "maggie", ],
        [ "george", "jane", "elroy", "judy", ],
    ];

    print $ref_to_TdT->[2][2];

Notez que le type des parenthèses extérieures a changé, et donc notre syntaxe d'accès aussi. C'est parce que, contrairement au C, en perl vous ne pouvez pas librement échanger les tableaux et leurs références. $ref_to_TdT est une référence à un tableau, tandis que @TdT est un tableau proprement dit. De la même manière, $TdT[2] n'est pas un tableau, mais une réf. à un tableau. Ainsi donc vous pouvez écrire ceci :

    $TdT[2][2]
    $ref_to_TdT->[2][2]

au lieu de devoir écrire ceci :

    $TdT[2]->[2]
    $ref_to_TdT->[2]->[2]

Eh bien, c'est parce que la règle est qu'entre des crochets adjacents (ou des accolades), vous êtes libre d'omettre la flèche de déréférencement de pointeur. Mais vous ne pouvez pas faire cela pour le tout premier si c'est un scalaire contenant une référence, ce qui signifie que $ref_to_TdT en a toujours besoin.  

Développer la vôtre

Tout ceci est bel et bien pour la déclaration d'une structure de données fixe, mais si vous voulez ajouter de nouveaux éléments à la volée, ou tout contruire à partir de zéro ?

Tout d'abord, étudions sa lecture à partir d'un fichier. C'est quelque chose comme ajouter une rangée à la fois. Nous présumerons qu'il existe un fichier tout simple dans lequel chaque ligne est une rangée et chaque mot un élément. Voici la bonne façon de le faire si vous essayez de développer un tableau @TdT les contenant tous :

    while (<>) {
        @tmp = split;
        push @TdT, [ @tmp ];
    }

Vous auriez aussi pu charger tout cela dans une fonction :

    for $i ( 1 .. 10 ) {
        $TdT[$i] = [ somefunc($i) ];
    }

Ou vous auriez pu avoir une variable temporaire traînant dans le coin et contenant le tableau.

    for $i ( 1 .. 10 ) {
        @tmp = somefunc($i);
        $TdT[$i] = [ @tmp ];
    }

Il est très important que vous vous assuriez d'utiliser le constructeur de référence de tableau "[]". C'est parce que ceci serait très mauvais :

    $TdT[$i] = @tmp;

Voyez vous, affecter comme ceci un tableau nommé à un scalaire ne fait que compter le nombre d'éléments dans @tmp, ce qui n'est probablement pas ce que vous désirez.

Si vous fonctionnez sous "use strict", vous devrez ajouter quelques déclarations pour qu'il soit content :

    use strict;
    my(@TdT, @tmp);
    while (<>) {
        @tmp = split;
        push @TdT, [ @tmp ];
    }

Bien sûr, vous n'avez pas du tout besoin de donner un nom au tableau temporaire :

    while (<>) {
        push @TdT, [ split ];
    }

Vous n'êtes pas non plus obligé d'utiliser push(). Vous pourriez juste faire une affectation directe si vous saviez où vous voulez le mettre:

    my (@TdT, $i, $line);
    for $i ( 0 .. 10 ) {
        $line = <>;
        $TdT[$i] = [ split ' ', $line ];
    }

ou même juste

    my (@TdT, $i);
    for $i ( 0 .. 10 ) {
        $TdT[$i] = [ split ' ', <> ];
    }

Vous devriez en général lorgner d'un regard mauvais l'usage de fonctions qui pourraient potentiellement retourner des listes dans un contexte scalaire sans le formuler explicitement. Ceci serait plus clair pour le lecteur de passage :

    my (@TdT, $i);
    for $i ( 0 .. 10 ) {
        $TdT[$i] = [ split ' ', scalar(<>) ];
    }

Si vous vouliez avoir une variable $ref_to_TdT comme référence à un tableau, vous devriez faire quelque chose comme ceci :

    while (<>) {
        push @$ref_to_TdT, [ split ];
    }

Maintenant vous pouvez ajouter de nouvelles rangées. Et pour ajouter de nouvelles colonnes ? Si vous traitez juste des matrices, le plus facile est souvent d'utiliser une simple affectation :

    for $x (1 .. 10) {
        for $y (1 .. 10) {
            $TdT[$x][$y] = func($x, $y);
        }
    }

    for $x ( 3, 7, 9 ) {
        $TdT[$x][20] += func2($x);
    }

Peu importe que ces éléments soient déjà là ou pas : elle les créera joyeusement pour vous, mettant les éléments intermédiaires à "undef" si besoin est.

Si vous vouliez juste en ajouter à une rangée, vous devriez faire quelque chose ayant l'air un peu plus bizarre :

    # ajoute de nouvelles colonnes à une rangée existante
    push @{ $TdT[0] }, "wilma", "betty";

Remarquez que je ne pourrais pas juste dire :

    push $TdT[0], "wilma", "betty";  # FAUX !

En fait, cela ne se compilerait même pas. Pourquoi donc ? Parce que l'argument de push() doit être un véritable tableau, et pas juste une référence.  

Accès et Sortie

Maintenant il est temps de sortir votre structure de données. Comment allez-vous faire une telle chose ? Eh bien, si vous voulez uniquement l'un des éléments, c'est trivial :

    print $TdT[0][0];

Si vous voulez sortir toute la chose, toutefois, vous ne pouvez pas dire :

    print @TdT;         # FAUX

car vous obtiendrez juste la liste des références, et perl ne déréférencera jamais automatiquement les choses pour vous. Au lieu de cela, vous devez faire tourner une boucle ou deux. Ceci imprime toute la structure, en utilisant la construction for() dans le style du shell pour boucler d'un bout à l'autre de l'ensemble des indices extérieurs.

    for $aref ( @TdT ) {
        print "\t [ @$aref ],\n";
    }

Si vous vouliez garder la trace des indices, vous pourriez faire ceci:

    for $i ( 0 .. $#TdT ) {
        print "\t elt $i is [ @{$TdT[$i]} ],\n";
    }

ou peut-être même ceci. Remarquez la boucle intérieure.

    for $i ( 0 .. $#TdT ) {
        for $j ( 0 .. $#{$TdT[$i]} ) {
            print "elt $i $j is $TdT[$i][$j]\n";
        }
    }

Comme vous pouvez le voir, cela devient un peu compliqué. C'est pourquoi il est parfois plus facile de prendre une variable temporaire en chemin :

    for $i ( 0 .. $#TdT ) {
        $aref = $TdT[$i];
        for $j ( 0 .. $#{$aref} ) {
            print "elt $i $j is $TdT[$i][$j]\n";
        }
    }

Hmm... c'est encore un peu laid. Pourquoi pas ceci :

    for $i ( 0 .. $#TdT ) {
        $aref = $TdT[$i];
        $n = @$aref - 1;
        for $j ( 0 .. $n ) {
            print "elt $i $j is $TdT[$i][$j]\n";
        }
    }

 

Tranches

Si vous voulez aller à une tranche (une partie d'une rangée) d'un tableau multidimensionnel, vous allez devoir faire un peu d'indiçage fantaisiste. C'est parce que, tandis que nous avons un joli synonyme pour les éléments seuls via la flèche de pointeur pour le déréférencement, il n'existe pas de telle commodité pour les tranches (souvenez-vous, bien sûr, que vous pouvez toujours écrire une boucle pour effectuer une opération sur une tranche).

Voici comment faire une opération en utilisant une boucle. Nous supposerons avoir une variable @TdT comme précédemment.

    @part = ();
    $x = 4;
    for ($y = 7; $y < 13; $y++) {
        push @part, $TdT[$x][$y];
    }

Cette même boucle pourrait être remplacée par une opération de tranche:

    @part = @{ $TdT[4] } [ 7..12 ];

mais comme vous pouvez l'imaginer, c'est plutôt rude pour le lecteur.

Ah, mais et si vous vouliez une tranche à deux dimensions, telle qu'ayant $x dans 4..8 et $y allant de 7 à 12 ? Hmm... voici la façon simple :

    @newTdT = ();
    for ($startx = $x = 4; $x <= 8; $x++) {
        for ($starty = $y = 7; $y <= 12; $y++) {
            $newTdT[$x - $startx][$y - $starty] = $TdT[$x][$y];
        }
    }

Nous pouvons réduire une partie du bouclage via des tranches

    for ($x = 4; $x <= 8; $x++) {
        push @newTdT, [ @{ $LoL[$x] } [ 7..12 ] ];
    }

Si vous faisiez des Transformations Schwartziennes, vous auriez probablement choisi map pour cela

    @newTdT = map { [ @{ $TdT[$_] } [ 7..12 ] ] } 4 .. 8;

Bien que si votre directeur vous accusait de rechercher la sécurité de l'emploi (ou l'insécurité rapide) à l'aide d'un code indéchiffrable, il serait difficile d'argumenter. :-) Si j'étais vous, je mettrais cela dans une fonction :

    @newTdT = splice_2D( \@LoL, 4 => 8, 7 => 12 );
    sub splice_2D {
        my $lrr = shift;     # réf à un tableau de réfs. de tableau !
        my ($x_lo, $x_hi,
            $y_lo, $y_hi) = @_;

        return map {
            [ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ]
        } $x_lo .. $x_hi;
    }

 

VOIR AUSSI

perldata(1), perlref(1), perldsc(1)  

AUTEUR

Tom Christiansen <tchrist@perl.com>

Dernière mise à jour : Thu Jun 4 16:16:23 MDT 1998  

VERSION FRANÇAISE

Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.0. Pour en savoir plus concernant ces traductions, consultez http://www.enstimac.fr/Perl/ .  

TRADUCTION

Roland Trique <roland.trique@free.fr>  

RELECTURE

Régis Julié <regis.julie@cetelem.fr>



 

Index

NOM
DESCRIPTION
Déclaration et Accès aux Tableaux de Tableaux
Développer la vôtre
Accès et Sortie
Tranches
VOIR AUSSI
AUTEUR
VERSION FRANÇAISE
TRADUCTION
RELECTURE


Time: 18:59:24 GMT, December 19, 2004