Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez Perl Discussion :

[FAQ] Partie sur les listes


Sujet :

Contribuez Perl

  1. #1
    Rédacteur/Modérateur

    [FAQ] Partie sur les listes
    Bonjour,

    faisant suite à cette discussion, je vais rédiger une ou deux questions/réponses complémentaires dans la partie de la FAQ consacrée aux listes, concernant les différences entre une liste et un tableau. Mais je désire demander d'abord la correction de l'une des questions/réponses qui contient au moins une grosse erreur et des imprécisions que je serais obligé de contredire dans mes nouvelles questions/réponses.

    Il s'agit de la réponse à la question "Et les fonctions sur les listes et les tableaux ?".

    La fonction shift:

    Elle permet de supprimer le premier élément d'une liste et de retourner sa valeur...
    Elle permet de supprimer le premier élément d'un tableau (et non d'une liste...")

    La distinction peut-paraître subtile, mais comme je compte expliquer que, justement, la fonction shift ne fonctionne pas sur une liste, mais seulement sur un tableau, mieux vaut éviter les contradictions.

    La fonction pop:

    "permet de supprimer le dernier élément d'une liste et de retourner cette valeur. "
    Même chose, remplacer "d'une liste" par "d'un tableau", cette fonction travaille sur un tableau et non une liste.

    La fonction unpop:

    A ma connaissance, cette fonction unpop n'existe pas, à moins de la créer soit-même, ce qui ne servirait pas à grand chose puisqu'il y a la fonction push qui s'occupe très bien " d'ajouter un ou plusieurs éléments en fin de tableau".

    Tant que j'y suis, une petite correction orthographique dans la fonction split:

    Sans entrer dans le détail de ceeux-ci, voici des exemples :

    _______________

    @ Djibril: pour les nouvelles questions/réponses, comment dois-je procéder: ajouter un ou plusieurs nouveau(x) post(s) ci-dessous dans ce thread ou un fichier open-office au format DVP?

    Par ailleurs, il pourrait-être intéressant d'ajouter quelque part sur cette partie de la faq un lien vers mon article sur les opérateurs de listes (lien ci-dessous, sous ma signature).

    Bonne journée à tous.
    Laurent.

  2. #2
    Responsable Perl et Outils

    J'ai corrigé la FAQ.



    Pour les nouvelles QR, tu postes sur ce forum contribuez un post par QR. Et je me chargerai de les mettre dans la FAQ.
    D'ici quelques semaines, on aura une FAQ collaborative où il sera plus simple d'y participer.

  3. #3
    Rédacteur/Modérateur

    Citation Envoyé par djibril Voir le message
    J'ai corrigé la FAQ.
    Ouah, c'est du rapide.

    Merci.

    Citation Envoyé par djibril Voir le message

    Pour les nouvelles QR, tu postes sur ce forum contribuez un post par QR. Et je me chargerai de les mettre dans la FAQ.
    D'ici quelques semaines, on aura une FAQ collaborative où il sera plus simple d'y participer.
    OK, je m'y mets.

  4. #4
    Rédacteur/Modérateur

    Bonjour,

    voilà, j'ai rédigé deux Q/R supplémentaires pour la FAQ. Mon idée est de les mettre juste après "Et les fonctions sur les listes et les tableaux ?" et avant la dernière section sur les fonctions surpuissantes.

    Voici la première:

    ______________

    Quelle est la différence entre une liste et un tableau?

    Les listes et les tableaux possèdent une grosse ressemblance, un fort lien de parenté, et il arrive fréquemment que des listes se transforment en tableau et réciproquement. Le principal point commun est qu'une liste et un tableau sont tous les deux des collections d'objets scalaires (ces objets peuvent être aussi bien des constantes que des variables). Du coup, certaines des questions ci-dessus peuvent entretenir une certaine confusion entre les deux notions, pour des raisons de simplification nécessaire quand on s'adresse à des débutants. Cette simplification, utile dans un premier temps, est une forme de "mensonge pieux à des enfants": on ne peut pas tout expliquer tout de suite, donc on simplifie dans un premier temps. Mais, malgré leurs ressemblances et leurs points communs, les listes et les tableaux sont des entités de nature très différente.

    Un tableau est une variable contenant une collection de choses et ayant souvent un nom (préfixé par le sigil @) ou au minimum, dans le cas de tableaux anonymes, une adresse mémoire utilisable par le programmeur sous la forme d'une référence. Une liste est une collection de choses n'ayant pas de nom et n'ayant pas non plus d'adresse mémoire utilisable. Une liste est donc par essence éphémère ou volatile puisqu'elle n'est pas stockée sous la forme d'une variable. Une liste n'étant pas une variable, mais une constante, elle n'est donc par essence non modifiable ou non-mutable (elle ne peut pas être une lvalue).

    L'affectation suivante utilise une liste pour initialiser un tableau:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my @tableau = (1, 2, "trois", 4, 5, "six"); # copie la liste à droite dans le tableau à gauche


    La partie gauche de l'affectation est bien un tableau (c'est une variable qui a un nom préfixé par le sigil @). La suite du programme pourra accéder à la variable @tableau ou aux valeurs individuelles qu'elle contient. La partie à droite de l'affectation est une simple liste temporaire: dès l'instruction suivante du programme, elle n'existe plus. Partie! Gone for ever!

    Une liste n'étant pas modifiable, les opérateurs dits de listes qui modifient la liste d'origine ne sont en fait pas utilisables sur une liste mais seulement sur un tableau. C'est le cas, en particulier, des fonctions push, pop, shift, unshift, splice et delete. Aucune d'entre elles ne peut fonctionner sur une simple liste. Par exemple, si l'on crée le tableau suivant et y ajoute un élément avec la fonction push (ou unshift), cela fonctionne parfaitement:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    my @tableau = qw /1 2 3/;
    push @tableau, 4;
    print "@tableau"; " imprime: 1 2 3 4


    Mais essayer de faire la même chose avec une liste donne un message d'erreur:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    push (1, 2, 3), 4; # !! ne marche pas


    On obtient le message d'erreur suivant:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    Type of arg 1 to push must be array (not constant item)


    Ce qui explique clairement le problème: une liste est une constante (même si certains éléments de la liste sont des variables), on ne peut pas lui ajouter un élément, alors que ça marche parfaitement avec un tableau.

    Un autre exemple, très souvent troublant pour un débutant, des différences entre une liste et un tableau est le résultat de leur évaluation en contexte scalaire. Si l'on évalue un tableau dans un contexte scalaire, on obtient le nombre d'éléments du tableau:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    my @tableau = (11, 12, 13, 14, 15, 16);
    my $size = @tableau; # évaluation en contexte scalaire
    print $size; # imprime 6, le nombre d'éléments du tableau


    On peut aussi tester la même chose de la façon suivante:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    my @tableau = (11, 12, 13, 14, 15, 16);
    print scalar @tableau; # imprime aussi 6


    Avec une liste, on obtient un résultat complètement différent:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    my $var =  (11, 12, 13, 14, 15, 16);
    print $var; # imprime 16

    De même:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    print scalar (11, 12, 13, 14, 15, 16); # imprime 16


    Que se passe-t-il? On a coutume de dire qu'en contexte scalaire, une liste renvoie son dernier élément. C'est effectivement ce qui semble se passer en pratique (et cette interprétation est souvent suffisante dans la pratique), mais l'explication réelle est un un peu plus subtile: les listes n'ont simplement pas d'existence en contexte scalaire. En contexte de liste, l'opérateur virgule sépare les éléments d'une liste. En contexte scalaire, cet opérateur virgule renvoie l'opérande de droite. Donc, si nous avons:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    print scalar ("toto", "tutu");


    L'évaluation de l'expression entre parenthèses ("toto", "tutu") donnera l'opérande de droite, donc "tutu". Si l'expression à évaluer à trois membres, par exemple ("toto", "tutu", "tata"), perl évaluera d'abord les deux premiers membres et lui donnera la valeur "tutu", puis évaluera "tutu" et "tata", et trouvera "tata", et ainsi de suite si la liste à évaluer contient d'autres valeurs. En définitive, même si l'explication est un peu fausse, tout se passe comme si une liste évaluée en contexte scalaire retournait son dernier élément.

    Dernier point faisant appel à des notions un peu plus avancées : il est possible de prendre une référence sur ce tableau, c'est à dire stocker dans une variable scalaire l'adresse du tableau, ce qui constitue un autre moyen d'accéder à son contenu (voir la partie de cette FAQ consacrée aux références pour plus de détails sur le sujet):

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    my @tableau = (1, 2, "trois", 4, 5, "six");
    my $tab_ref = \@tableau; # $tab_ref est une référence, une variable pointant sur l'adresse mémoire du tableau
    print  $tab_ref ; # affiche : ARRAY(0x80359c08), ce qui nous dit que $tab_ref est une référence sur un tableau dont l'adresse mémoire est 0x80359c08


    Il n'est pas possible de faire la même chose avec une liste:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my $list_ref = \(1, 2, "trois", 4, 5, "six"); # !! ne fonctionne pas


    Cela ne fonctionne pas, car $list_ref contient maintenant une référence non pas sur la liste, mais sur le dernier élément de la liste:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    print $list_ref ; # imprime   SCALAR(0x8034f940)
    print $$list_ref ; imprime "six")


    Ce fonctionnement n'a rien de surprenant, il rejoint ce que nos avons vu lors de l'évaluation d'une liste en contexte scalaire.

  5. #5
    Rédacteur/Modérateur

    Et voici la second Q/R:

    _____________

    Mais, alors, à quoi servent les listes?


    A côté des différences importantes vues dans la question précédente, il y a de nombreux cas où une liste et un tableau se comportent de la même façon et un bon nombre des fonctions (sort, map, grep, join, for, foreach, etc.) sont utilisables de la même manière ou presque sur des listes et des tableaux. Seules les fonctions qui modifient un tableau ne sont généralement pas utilisables sur une liste, sauf éventuellement à créer une autre liste.

    En fait, de nombreux opérateurs ou instructions de Perl travaillent non sur des tableaux, comme on peut le croire, mais sur des listes. Si vous passez un (ou plusieurs) tableau(x) en paramètre à une fonction, la fonction reçoit en fait une (seule) liste d'alias vers les éléments du (ou des) tableaux, et cette liste d'alias est mise dans le tableau @_. En particulier, s'il y avait plusieurs tableaux en entrée, la fonction n'a aucun moyen de distinguer les différents tableaux, elle ne "voit" qu'une seule grande liste indifférencié. Il en va de même en sens inverse quand une fonction renvoie plusieurs tableaux: l'instruction appelante ne reçoit en retour qu'une seule liste indifférenciée, et c'est à elle de la stocker éventuellement dans un tableau (ou d'autres variables) si les éléments de la liste doivent encore être manipulés.

    Il a été vu lors d'une question précédente que l'on peut accéder aux éléments individuels d'une liste en utilisant un indice entre crochets, exactement comme avec un tableau:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my $second = (1, 2, 3, 4)[1]; #second vaut maintenant 2


    Cela ne présente pas beaucoup d'intérêt avec une liste codée en dur comme ci-dessus (on pourrait aussi bien écrire: my $second = 2; ), mais cette possibilité peut être très utile avec des listes générées par une instruction du programme. Par exemple, la fonction split divise une chaîne de caractère en une liste de sous-chaînes (en utilisant le premier argument comme séparateur). Souvent, on affecte la liste de sous-chaînes produite à un tableau:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my @mots = split / /, "La contrepèterie est l'art de décaler les sons.";


    Le split génère une liste de huit sous-chaînes que l'on affecte ensuite au tableau @mots. Il arrive toutefois assez souvent, dans ce genre d'utilisation de split, que l'on ait besoin de seulement une ou deux des sous-chaînes générées par l'opérateur. On peut alors prélever une tranche du tableau @mots:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my ($mot2, $mot5) = @mots[1, 4]; # le "sigil" à employer ici est bien @ (et non $) parce que l'on récupère non pas un scalaire, mais une collection de deux scalaires


    Mais on peut aussi se passer de créer un tableau intermédiaire et récupérer les mots recherchés directement en une seule instruction plus concise:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    my ($mot2, $mot5)  = (split / /, "La contrepèterie est l'art de décaler les sons.")[1, 4];


    De même, on peut parcourir séquentiellement les valeurs d'une liste avec les opérateurs de boucle for ou foreach. Par exemple, l'instruction suivante affiche la table de multiplication par 5:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    print $_, " * 5 = ", $_ * 5, "\n" for 1..10;


    ce qui imprime:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    1 * 5 = 5
    2 * 5 = 10
    3 * 5 = 15
    4 * 5 = 20
    5 * 5 = 25
    6 * 5 = 30
    7 * 5 = 35
    8 * 5 = 40
    9 * 5 = 45
    10 * 5 = 50


    Ou, si l'on désire un affichage plus "propre":

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    printf "%2d%s%2d%s", $_, " x 5 = ", $_ * 5, "\n" for 1..10;


    Ce qui donne:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     1 x 5 =  5
     2 x 5 = 10
     3 x 5 = 15
     4 x 5 = 20
     5 x 5 = 25
     6 x 5 = 30
     7 x 5 = 35
     8 x 5 = 40
     9 x 5 = 45
    10 x 5 = 50


    Cette syntaxe permet aussi de répéter un certain nombre de fois une instruction simple. Supposons par exemple que nous voulions lire un ficher de données en sautant préalablement les six premières lignes d'en-tête. Nous pouvons le faire ainsi:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    open my $FH, "<", $file_in or die "Impossible d'ouvrir le fichier $file_in $!";
    <$fh> for 1..6; # on jette les six premières lignes
    while (my $line = <$fh>) {
         # traitement des lignes de données ...
    }


    De même, si nous avons besoin de lire les éléments d'un tableau en ayant connaissance de leur indice, au lieu de la boucle for de type C:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    my  @mois = qw /undef jan feb mar apr may jun jul aug sep oct nov dec/; # première valeur à undef pour que les mois aillent de 1 à 12 (et non 0 à 11)
    for (my $i = 1; $i ++; $i <= 12) {
         print "Le mois numéro $i de l'année est $mois[$i]\n";
    }


    nous pouvons utiliser une simple liste pour gérer l'indice $i:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    for my $i (1..12) {
         print "Le mois numéro $i de l'année est $mois[$i]\n";
    }


    ou de façon encore plus concise:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    print "Le mois numéro $_ de l'année est $mois[$_]\n" for (1..12);


    Un dernier point qui ne sera que très brièvement esquissé ici: beaucoup des opérateurs de listes de Perl reçoivent en entrée et/ou produisent en sortie une liste. Il est possible d'utiliser en entrée d'un opérateur la liste produite en sortie par un autre opérateur. Ainsi, par exemple, l'instruction suivante enchaîne quatre opérateurs de listes, split, sort, foreach et print:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    print $_, "\n" foreach sort split / /,  "la contrepèterie est l'art de décaler les sons.";


    Ce qui imprime les mots de la phrase triés par ordre lexicographique:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    contrepèterie
    de
    décaler
    est
    l'art
    la
    les
    sons.


    Une telle suite d'instructions se lit de droite à gauche: la fonction split commence par découper la phrase en entrée en mots individuels et passe cette liste de mots à la fonction sort; cette dernière trie la liste de mots reçus de split et passe la liste triée à l'opérateur foreach, lequel passe ensuite le mots triés un par un à print, qui imprime les mots triés assortis d'un retour à la ligne. On voit qu'il se forme un pipeline dans lequel les données en entrée subissent des transformations successives. Le lecteur intéressé trouvera des informations beaucoup plus détaillées sur ce genre de constructions très puissantes dans ce tutoriel: La programmation fonctionnelle en Perl - Partie 1: les opérateurs de liste.

  6. #6

  7. #7

  8. #8
    Rédacteur/Modérateur

    Merci, Djibril, mais il semble que, du coup, une autre QR qui existait auparavant ait disparu: la dernière sur les opérateurs surpuissants (sort, grep, map).

    Autre chose bizarre, une ligne vient deux fois dans le sommaire de cette partie de la FAQ.

    Bonne soirée,
    Laurent.

  9. #9

  10. #10
    Responsable Perl et Outils

    Bonsoir,

    Vous avez maintenant la possibilité de proposer vos Questions/réponses directement dans la FAQ. Vos propositions seront analysés et validées ou non.
    N'hésitez donc pas à faire des propositions.

    N.B. Si vous estimez avoir besoin de créer de nouvelles sections, veuillez me contacter en postant un topic dans ce forum.