IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Langage Perl Discussion :

fonction glob() : effet de bord extrêmement vicieux


Sujet :

Langage Perl

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juillet 2006
    Messages : 66
    Points : 60
    Points
    60
    Par défaut fonction glob() : effet de bord extrêmement vicieux
    Voici (encore) une prise de tête concernant la fonction glob, qui m'avait déjà donné du fil à retordre.
    Au début, j'ignorais en effet qu'il fallait stocker le résultat d'une recherche avec glob() dans un tableau, puis parcourir celui-ci.

    Voici un exemple trivial d'utilisation de glob() mais sans même effectuer de traitement.
    Il s'agit juste de mettre en lumière que l'appel à glob() a un effet sur une variable qui n'a justement rien à voir : la variable @list.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    my(@list) = ("a", "b");
    for(@list){
    	print "\nTEST #1 - [".$list[0].";".$list[1]."]";
    	while(glob("*.*")) {}
    	print "\nTEST #2 - [".$list[0].";".$list[1]."]";
    }
    print "\nTEST #3 - [".$list[0].";".$list[1]."]";
    Voici le résultat, qui me laisse pour le moins perplexe :

    TEST #1 - [a;b]
    TEST #2 - [;b]
    TEST #1 - [;b]
    TEST #2 - [;]
    TEST #3 - [;]

    La boucle contenant l'appel à glob() ne fait strictement rien (c'est fait exprès pour le test)
    Alors la grande question, c'est pourquoi le contenu du tableau @list est-il modifié ?

  2. #2
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Tu es victime d'un effet de bord très vicieux d'une utilisation excessive de la variable $_ implicite :
    En effet :
    Cette ligne itère sur @list, mais comme tu ne précises pas de variable d'itération elle utilise $_. De plus il faut savoir qu'une boucle de cette forme ne met pas une copie de l'élément courant dans la variable d'itération mais "aliase" celle-ci à l'élément courant de la liste itérée... Autrement dit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    my @list = (1, 2, 3);
    foreach my $num (@list) {
      $num++;
    }
    print "@list"; # "2 3 4"
    Dans cette boucle for où $_ est aliasée à l'élément courant du tableau, tu as placé cette ligne :
    Or cette ligne affecte également à $_ implicitement, elle itère sur tous les éléments renvoyée par le glob(), qui renvoie chaque élément matchant son pattern tour à tour et finit en renvoyant undef, donc lorsqu'on ressort de cette ligne, l'élément courant du tableau sur lequel est en train d'être appliqué le bloc de la boucle for a été mis à undef (puisque $_ était un alias de cet élément et qu'on lui a affecté undef)...

    Pour résumer je te mets la forme "développée" de ton code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    my(@list) = ("a", "b");
    for $_ (@list){
        print "\nTEST #1 - [".$list[0].";".$list[1]."]";
        while( defined($_ = glob("*.*")) ) {}
        print "\nTEST #2 - [".$list[0].";".$list[1]."]";
    }
    print "\nTEST #3 - [".$list[0].";".$list[1]."]";
    Quand tu sais que la boucle for crée des alias, je pense que le code ci-dessus te montre mieux le problème...


    Un tel exemple montre bien pourquoi il ne faut pas abuser de l'utilisation de boucle implicite, et toujours localiser $_ si on fait une telle boucle dans une fonction.
    Il y a deux solutions à ton problème :
    La mauvaise :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    my(@list) = ("a", "b");
    for(@list){
        print "\nTEST #1 - [".$list[0].";".$list[1]."]";
        {
            local $_;
            while(glob("*.*")) {}
        }
        print "\nTEST #2 - [".$list[0].";".$list[1]."]";
    }
    print "\nTEST #3 - [".$list[0].";".$list[1]."]";
    Ce code marche, mais n'est pas très joli, et plus lourd (du point de vue de la rédaction comme de la lisibilité) en fait que la solution propre suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    my(@list) = ("a", "b");
    for my $elt (@list){
        print "\nTEST #1 - [".$list[0].";".$list[1]."]";
        while( my $filename = glob("*.*") ) {}
        print "\nTEST #2 - [".$list[0].";".$list[1]."]";
    }
    print "\nTEST #3 - [".$list[0].";".$list[1]."]";
    En fait il suffit d'expliciter l'une des boucles pour éviter ton problème, mais je te conseille d'éviter comme la peste les boucles implicites (surtout imbriquées, à la profondeur zéro elle ne sont pas vraiment problématique) tant que tu n'auras pas assez d'expérience pour détecter quand elles peuvent être véritablement utiles (ça peut-être assez sympa d'écrire une boucle en trois fois moins de caractère tout en restant lisible et même plus lisible, et il y a d'autres petites astuces intéressantes). Evidemment ce conseil a moins d'importance pour les tout petits scripts, et surtout les unilignes.

    --
    Jedaï

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    66
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Juillet 2006
    Messages : 66
    Points : 60
    Points
    60
    Par défaut
    Et bien merci pour ces précisions très intéressantes sur $_ dans les boucles,
    ce qui dépasse largement le cadre de l'utilisation de la fonction glob().

    Plus jamais de boucles implicites, promis juré !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 05/12/2008, 12h51
  2. Effet de bord...
    Par pierre50 dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 15
    Dernier message: 12/10/2005, 18h11
  3. Effet de bord
    Par Clad3 dans le forum OpenGL
    Réponses: 11
    Dernier message: 04/10/2005, 14h38

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo