NAME

perlfaq4 - Manipulation de données ($Revision: 1.26 $, $Date: 1998/08/05 12:04:00 $)


DESCRIPTION

Cette section de la FAQ répond aux questions liées aux manipulations de données comme les nombres, les dates, les chaînes de caractères, les tableaux, les hachages, ainsi qu'à divers problèmes relatifs aux données.


Données: Nombres


Pourquoi est-ce que j'obtiens des nombres décimaux longs (e.g.. 19.9499999999999) à la place du nombre que j'attends (e.g. 19.95)?

L'ensemble infini tel que le mathématicien se représente les nombres réels ne peut qu'être approché par un ordinateur, puisque celui-ci n'a qu'un nombre fini de bits pour ranger un nombre infini de, hum, nombres.

Les nombres à virgule flottante sont représentés par votre ordinateur, en interne, sous forme binaire. Ceux qui sont lus à partir d'un fichier ou sous forme littérale dans votre programme sont traduits de leur représentation décimale à virgule flottante (e.g.. 19.95) en leur représentation binaire interne.

Mais 19.95 ne peut pas être représenté exactement par un nombre à virgule flottante binaire, tout comme 1/3 ne peut être représenté sous forme décimale à virgule flottante. En conséquence, l'ordinateur ne se représente pas 19.95 comme étant exactement 19.95.

Lorsque l'on imprime un nombre à virgule flottante, cette représentation binaire à virgule flottante est retransformée en représentation décimale. Ces nombres décimaux sont affichés soit dans le format spécifié avec printf(), soit suivant le format actuel de sortie des nombres (voir $#) si vous utilisez print. $# a une valeur par défaut différente dans Perl5 que dans Perl4. Il est périmé de changer soi-même la valeur de $#).

Ce problème concerne tous les langages informatiques qui représentent les nombres à virgule flottante sous forme binaire, et pas uniquement Perl. Perl fournit des nombres décimaux avec une précision arbitraire en utilisant le module Math::BigFloat (partie de la distribution standard de Perl), mais les opérations mathématiques en sont nettement ralenties.

Pour se débarrasser des chiffres superflus, il vous suffit d'utiliser un format (e.g.. printf("%.2f", 19.95)) pour obtenir la précision nécessaire. Voir Floating-point Arithmetic.


Pourquoi mon nombre octal n'est-il pas interprété correctement?

Perl ne comprends les nombres octaux et hexadécimaux en tant que tels que lorsqu'ils sont utilisés comme des valeurs littérales dans le programme. S'ils sont lus de quelque part et affectés à une variable, aucune conversion automatique n'a lieu. Pour convertir les valeurs, il faut utiliser explicitement oct() ou hex(). oct() interprète à la fois les nombres hexadécimaux (``0x350'') et octaux (``0350'' ou même sans le ``0'' de tête, comme dans ``377''), tandis que hex() ne convertit que les hexadécimaux, avec ou sans l'en-tête ``0x'', comme pour ``0x255'', ``3A'', ``ff'', ou ``baffe''.

Ce problème apparaît fréquemment lorsque l'on essaye d'utiliser les fonctions chmod(), mkdir(), umask(), ou sysopen(), qui demandent toutes des permissions en octal.

    chmod(644,  $file); # FAUX -- perl -w le repère
    chmod(0644, $file); # correct


Est-ce que perl a une fonction d'arrondi? Et qu'en est-il de ceil() (majoration) et floor() (minoration)? Et des fonctions trigonométriques?

Il faut retenir que int() ne fait que tronquer vers 0. Pour arrondir à un certain nombre de chiffres, sprintf() ou printf() sont d'habitude la voie la plus simple.

    printf("%.3f", 3.1415926535);       # affiche 3.142

Le module POSIX (élément de la distribution standard de perl) implémente ceil(), floor(), et d'autres fonctions mathématiques et trigonométriques.

    use POSIX;
    $ceil   = ceil(3.5);                        # 4
    $floor  = floor(3.5);                       # 3

Dans les Perls 5.000 à 5.003, la trigonométrie était faite dans le module Math::Complex. A partir de 5.004, le module Math::Trig (élément de la distribution standard de perl) implémente les fonctions trigonométriques. En interne, il utilise le module Math::Complex, et quelques fonctions peuvent s'échapper de l'axe des réels vers le plan des complexes, comme par exemple le sinus inverse de 2.

Les arrondis dans des applications financières peuvent avoir des conséquences majeures, et la méthode utilisée se doit d'être spécifiée précisément. Dans ces cas, il vaut mieux de ne pas avoir confiance dans un quelconque système d'arrondis utilisé par Perl, mais implémenter sa propre fonction d'arrondis telle qu'elle est nécessaire.


Comment est-ce que je convertis des bits en entiers?

Pour convertir une chaîne de 1 et de 0 comme 10110110 en un scalaire contenant sa valeur binaire, on utilise la fonction pack() (documentée dans pack):

    $decimal = pack('B8', '10110110');

Voici un exemple pour faire le chemin inverse:

    $binary_string = join('', unpack('B*', "\x29"));


Comment puis-je multiplier des matrices?

Utiliser les modules Math::Matrix ou Math::MatrixReal (disponibles sur le CPAN) ou l'extension PDL (également disponible sur le CPAN).


Comment puis-je effectuer une opération sur une série d'entiers?

Pour appeler une fonction sur chaque élément d'un tableau, et récupérer le résultat, utiliser:

    @results = map { my_func($_) } @array;

Par exemple:

    @triple = map { 3 * $_ } @single;

Pour appeler une fonction sur chaque élément d'un tableau, mais sans tenir compte du résultat:

    foreach $iterator (@array) {
        &my_func($iterator);
    }

Pour appeler une fonction sur chaque entier d'un (petit) intervalle, on peut utiliser:

    @results = map { &my_func($_) } (5 .. 25);

mais il faut être conscient que l'opérateur .. crée un tableau de tous les entiers de l'intervalle utilisant beaucoup de mémoire pour de grands intervalles. Il vaut mieux utiliser:

    @results = ();
    for ($i=5; $i < 500_005; $i++) {
        push(@results, &my_func($i));
    }


Comment puis-je produire des chiffres romains?

Récupérer le module http://www.perl.com/CPAN/modules/by-module/Roman.


Pourquoi mes nombres aléatoires ne sont-ils pas aléatoires?

Explication courte: on n'obtient que des nombres pseudo-aléatoires et pas aléatoires, parce que les ordinateurs sont efficaces en étant prévisibles, et mauvais à être aléatoires (en dépit des apparences dues aux bugs dans les programmes :-). Une explication plus longue due à Tom Poenix est disponible à l'adresse http://www.perl.com/CPAN/doc/FMTEYEWTK/random. John von Neumann a dit: ``Quiconque tente de générer des nombres aléatoires par des moyens déterministes vit, bien entendu, dans le péché.''

Jeter aussi un oeil au module Math::TrulyRandom sur le CPAN. Il utilise des imperfections de l'horloge du système pour générer des nombres aléatoires, mais ceci prend un certain temps. Les ``Numerical Recipes in C'' à l'adresse http://nr.harvard.edu/nr/bookc.html fournissent un meilleur générateur pseudo-aléatoire que celui qui vient avec le système d'exploitation.


Données: Dates


Où trouver la semaine/le jour de l'année?

Le jour de l'année fait partie du tableau renvoyé par localtime() (voir localtime):

    $day_of_year = (localtime(time()))[7];

ou plus lisiblement (pour les versions 5.004 ou supérieures):

    use Time::localtime;
    $day_of_year = localtime(time())->yday;

On peut obtenir la semaine de l'année en divisant ce nombre par 7:

    $week_of_year = int($day_of_year / 7);

Bien entendu ceci suppose que le compte des semaines commence à zéro. Le module Date::Calc du CPAN propose beaucoup de fonctions de calculs de date, y compris le jour de l'année, la semaine de l'année et ainsi de suite. Il faut noter que toutes les entreprises ne comptent pas la ``semaine 1'' de la même manière. Par exemple, les entreprises américaines considèrent souvent que la première semaine comportant un lundi est la Semaine #1, bien que la norme ISO 8601 considère la semaine #1 comme la première comportant un mardi.


Comment puis-je comparer deux dates ou en calculer une différence?

Si le format des dates est en secondes depuis l'origine, il suffit de les soustraire l'une à l'autre. S'il s'agit d'une date structurée (séparant les valeurs pour l'année, le jour, le mois, l'heure, la minute et la seconde), utiliser l'un des modules Date::Manip ou Date::Calc disponibles sur le CPAN.


Comment puis-je convertir une chaîne de caractères en secondes depuis l'origine?

Si cette chaîne est suffisamment régulière pour avoir toujours le même format, on peut la découper et en passer les morceaux à la fonction timelocal du module standard Time::Local. Autrement, regarder les modules Date::Calc et Date::Manip disponibles sur le CPAN.


Comment puis-je trouver le jour du calendrier Julien?

Ni Date::Manip, ni Date::Calc ne traitent les jours du calendrier Julien. A leur place, on peut s'aider d'un exemple de calculs de dates dans ce calendrier: http://www.perl.com/CPAN/authors/David_Muir_Sharnoff/modules/Time/JulianDay.pm.gz .


Est-ce que Perl à un problème lié à l'an 2000? Est-ce que Perl respecte l'Y2K?

Réponse courte: Non, Perl n'a pas de problème avec l'an 2000. Oui, Perl respecte l'Y2K; par contre, pas nécessairement les programmeurs qui ont été engagés pour l'utiliser.

Réponse longue: Perl est autant certifié An 2000 que l'est un crayon, ni plus, ni moins. Les fonctions de date et de temps fournies avec perl (gmtime et localtime) donnent une information suffisante pour définir l'année bien au-delà de l'an 2000 (pour les machines 32 bits, les problèmes arrivent en 2038). L'année renvoyée par ces fonctions utilisées dans un contexte de tableau est l'année moins 1900. Pour les années comprises entre 1910 et 1999 il se trouve que c'est un nombre à deux chiffres. Pour éviter le problème de l'an 2000, il ne faut ne pas traiter l'année comme un nombre à deux chiffres: ce n'en est pas un.

Quand gmtime() et localtime() sont utilisées dans un contexte scalaire, elles renvoient une chaîne qui contient l'année écrite en entier. Par exemple, $timestamp = gmtime(1005613200) fixe $timestamp à la valeur ``Tue Nov 13 01:00:00 2001''. Ici encore, il n'y a pas de problème de l'an 2000.

Ceci ne veut pas dire que Perl ne peut pas être utilisé pour créer des programmes qui ne respecteront pas l'an 2000. Il peut l'être. Mais un crayon le peut aussi. La faute en revient à l'utilisateur, pas au langage. Au risque d'indisposer la NRA (National Rifle Association): ``Perl ne viole pas l'an 2000, les gens le font.'' Pour un exposé plus complet, voir http://language.perl.com/news/y2k.html.


Données: Chaînes de caractères


Comment m'assurer de la validité d'une entrée?

La réponse à cette question sera d'habitude l'usage d'une expression régulière, avec parfois un peu de logique en plus. Voir les questions plus spécifiques (nombres, adresses de courrier électronique, etc.) pour des détails.


Comment enlever les caractères d'échappement d'une chaîne de caractères?

Cela dépend du type de caractère d'échappement. Les caractères d'URL sont traités dans la <perlfaq9>. Les caractères de l'interpréteur de commandes avec des barres obliques inversées (\) sont enlevées avec:

    s/\\(.)/$1/g;

Ceci ne convertira pas les "\n" ou "\t" ni aucun autre caractère spécial.


Comment enlever des paires de caractères successifs?

Pour transformer "abbcccd" en "abccd":

    s/(.)\1/$1/g;


Comment effectuer des appels de fonction dans une chaîne?

Ceci est documenté dans la page de manuel perlref. C'est en général sujet à beaucoup de problèmes de citation et de lisibilité, mais c'est faisable. Pour mettre un appel à une sous-routine (dans un contexte de liste) dans une chaîne:

    print "My sub returned @{[mysub(1,2,3)]} that time.\n";

S'il s'agit plutôt d'un contexte scalaire, le même genre d'astuce est utilisée pour des expressions arbitraires:

    print "That yields ${\($n+5)} widgets\n";

La version 5.004 de perl avait un bug qui donnait à l'expression dans ${...} un contexte de liste, mais ceci a été fixé dans la version 5.005.

Voir aussi ``Comment puis-je substituer des variables dans des chaînes de texte?'' dans cette section de la FAQ.


Comment repérer des éléments appariés/imbriqués?

Ceci ne peut être réalisé en une seule expression régulière, même très compliquée. Pour trouver quelque chose se situant entre deux caractères simples, un motif comme /x([^x]*)x/ mettra les morceaux de l'intervalle dans $1. Lorsque le séparateur est de plusieurs caractères, il faudrait en utiliser un ressemblant plus à /alpha(.*?)omega/. Mais aucun de ces deux-ci ne gère les motifs imbriqués, et ils ne le pourraient pas. Pour cela, il vous faudra écrire un analyseur syntaxique.

Si vous songez sérieusement à écrire un analyseur syntaxique, de nombreux modules ou gadgets pourront vous rendre la vie plus facile. Il y a le module du CPAN Parse::RecDescent, le module standard Text::Balanced, le programme byacc, et l'excellent outil py de Mark-Jason Dominux, disponible à l'URL: http://www.plover.com/~mjd/perl/py/ .

Une approche simple, mais destructive, est de s'orienter de l'intérieur vers l'extérieur en retirant les plus petites parties imbriquées les unes après les autres:

      while (s//BEGIN((?:(?!BEGIN)(?!END).)*)END/gs) {
        # faire quelque chose de $1
    } 


Comment inverser une chaîne de caractères?

Utiliser reverse() dans un contexte scalaire, tel qu'indiqué dans reverse.

     $reversed = reverse $string;


Comment développer les tabulations dans une chaîne de caractères?

On peut le faire soi-même:

    1 while $string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;

ou utiliser simplement le module Text::Tabs (qui fait partie de la distribution standard de perl).

    use Text::Tabs;
    @expanded_lines = expand(@lines_with_tabs);


Comment remettre en forme un paragraphe?

Utiliser Text::Warp (qui fait partie de la distribution standard de perl):

    use Text::Wrap;
    print wrap("\t", '  ', @paragraphs);

Les paragraphes passés en argument à Text:Warp ne doivent pas contenir de caractère de nouvelle ligne. Text::Wrap ne justifie pas les lignes (alignées à droite).


Comment accéder ou/modifier les N premières lettres d'une chaîne de caractères?

Plusieurs méthodes sont possibles. Pour en faire simplement une copie, utiliser substr():

    $first_byte = substr($a, 0, 1);

Pour modifier une partie d'une chaîne, le moyen le plus simple est d'utiliser substr() en tant que lvalue:

    substr($a, 0, 3) = "Tom";

Cependant, ceux qui ont une affinité pour les recherches de motifs préféreront sans doute:

    $a =~ s/^.../Tom/;


Comment changer la n-ième occurrence de quelque chose?

Il faut conserver soi-même l'évolution de n. Par exemple, si l'on souhaite changer la cinquième occurrence de "whoever" ou "whomever" en "whosoever" ou "whomsoever", sans tenir compte de la casse:

    $count = 0;
    s{((whom?)ever)}{
        ++$count == 5           # Est-ce la cinquième?
            ? "${2}soever"      # oui: faire l'échange
            : $1                # non: le laisser tel quel
    }igex;

Dans des cas plus généraux, on peut utiliser le modificateur /g dans une boucle while, en comptant le nombre de correspondances.

    $WANT = 3;
    $count = 0;
    while (/(\w+)\s+fish\b/gi) {
        if (++$count == $WANT) {
            print "The third fish is a $1 one.\n";
            # Attention: ne pas sortir de cette boucle par un `last'
        }
    }

Ceci sort: "The third fish is a red one." On peut aussi utiliser un compteur de répétition, et un motif répété comme ceci:

    /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i;


Comment compter le nombre d'occurrences d'une sous-chaîne dans une chaîne de caractères?

Plusieurs moyens sont possibles, avec une efficacité variable: pour trouver le nombre d'un caractère particulier (X) dans une chaîne, on peut utiliser la fonction tr/// comme ceci:

    $string = "ThisXlineXhasXsomeXx'sXinXit":
    $count = ($string =~ tr/X//);
    print "There are $count X characters in the string";

Ceci marche bien lorsque l'on cherche un seul caractère. Cependant si l'on essaye de compter des sous-chaînes de plusieurs caractères à l'intérieur d'une chaîne plus grande, tr/// ne marchera pas. On peut alors envelopper d'une boucle while() une recherche de motif globale. Par exemple, comptons les entiers négatifs:

    $string = "-9 55 48 -2 23 -76 4 14 -44";
    while ($string =~ /-\d+/g) { $count++ }
    print "There are $count negative numbers in the string";


Comment mettre en lettres majuscules tous les mots d'une ligne?

Pour mettre en lettres majuscules toutes les premières lettres de mots:

        $line =~ s/\b(\w)/\U$1/g;

Ceci a pour effet de bord de transformer ``aujourd'hui'' en ``Aujourd'Hui''. On peut préférer parfois ceci (suggéré par Brian Foy):

    $string =~ s/ (
                 (^\w)    #au début d'une ligne
                   |      # ou
                 (\s\w)   #précédé d'un espace
                   )
                /\U$1/xg;
    $string =~ /([\w']+)/\u\L$1/g;

Pour transformer toute la ligne en lettres majuscules:

        $line = uc($line);

A l'inverse, pour avoir chaque mot en lettres minuscules, avec la première lettre majuscule:

        $line =~ s/(\w+)/\u\L$1/g;

On peut (et l'on devrait toujours) rendre ces caractères sensibles aux locales en plaçant un pragma use locale dans le programme. Voir perllocale pour d'interminables détails sur les locales.


Comment découper une chaîne séparée par un [caractère] sauf à l'intérieur d'un [caractère]? (Champs délimités par des virgules)

Prenons l'exemple du cas où l'on souhaite découper une chaîne qui est subdivisée par des virgules en différents champs. (Supposons que l'on dit séparé par des virgules et pas délimité par des virgules, ce qui est différent, mais probablement jamais ce que l'on sous-entends.) On ne peut utiliser split(/,/) parce qu'il ne faudrait pas découper si la virgule est entre guillemets. Par exemple pour la ligne de donnée suivante:

    SAR001,"","Cimetrix, Inc","Bob Smith","CAM",N,8,1,0,7,"Error, Core Dumped"

A cause de la restriction imposée par les guillemets, ceci devient un problème relativement complexe. Heureusement, nous avons Jeffrey Frield, auteur d'un livre chaudement recommandé sur les expressions régulières, qui a pris soin pour nous. Il suggère (en supposant la chaîne dans $text):

     @new = ();
     push(@new, $+) while $text =~ m{
         "([^\"\\]*(?:\\.[^\"\\]*)*)",?  # regroupe les éléments entre guillemets
       | ([^,]+),?
       | ,
     }gx;
     push(@new, undef) if substr($text,-1,1) eq ',';

Pour représenter un vrai guillemet à l'intérieur d'un champ délimité par des guillemets, il faut le protéger d'une barre oblique inverse comme caractère d'échappement (eg "comme \"ceci\""). Les déprotéger est une tâche qui a déjà été vue plus tôt dans cette section.

Comme alternative, le module Text::ParseWords (qui fait partie de la distribution standard de perl) permet de mettre:

    use Text::ParseWords;
    @new = quotewords(",", 0, $text);


Comment supprimer des espaces blancs au début/à la fin d'une chaîne?

Bien que l'approche la plus simple semblerait être:

    $string =~ s/^\s*(.*?)\s*$/$1/;

celle-ci est inutilement lente, destructive, et échoue sur les insertions de caractères de nouvelle ligne. Il est mieux et plus rapide de le faire en deux étapes:

    $string =~ s/^\s+//;
    $string =~ s/\s+$//;

Ou en l'écrivant plus joliment:

    for ($string) {
        s/^\s+//;
        s/\s+$//;
    }

Cette expression utilise avantageusement la création d'alias par la boucle foreach pour factoriser le code en commun. Ceci peut être fait en une seule fois sur plusieurs chaînes, sur des tableaux, ou même sur les valeurs d'un hachage en utilisant un glissement:

    # rogner les espaces dans le scalaire, le tableau
    # et toutes les valeurs du hachage
    for ($string) {
        s/^\s+//;
        s/\s+$//;
    }


Comment extraire une sélection de colonnes d'une chaîne de caractères?

Utiliser les fonctions substr() ou unpack(), toutes deux documentées dans la page de manuel perlfunc. Ceux qui préfèrent penser en termes de colonnes plutôt qu'en longueurs de lignes peuvent utiliser ce genre de choses:

    # déterminer le format de unpack nécessaire pour découper la
    # sortie d'un ps de Linux
    # les arguments sont les colonnes sur lesquelles découper
    my $fmt = cut2fmt(8, 14, 20, 26, 30, 34, 41, 47, 59, 63, 67, 72);
    sub cut2fmt { 
        my(@positions) = @_;
        my $template  = '';
        my $lastpos   = 1;
        for my $place (@positions) {
            $template .= "A" . ($place - $lastpos) . " "; 
            $lastpos   = $place;
        }
        $template .= "A*";
        return $template;
    }


Comment calculer la valeur soundex d'une chaîne?

Utiliser le module standard Test::Soundex qui est distribué avec perl.


Comment interpoler des variables dans des chaînes de texte?

Supposons une chaîne comme celle-ci:

    $text = 'this has a $foo in it and a $bar';

S'il s'agissait de deux variables globales, alors ceci suffirait:

    $text =~ s/\$(\w+)/${$1}/g;

Mais comme elles sont probablement lexicales, ou au moins elles pourraient l'être, il faudrait faire ceci:

    $text =~ s/(\$\w+)/$1/eeg;
    die if $@;                  # nécessaire pour /ee, pas /e

Il serait probablement préférable dans le cas général de traiter ces variables comme des entrées dans une table de hachage à part. Par exemple:

    %user_defs = ( 
        foo  => 23,
        bar  => 19,
    );
    $text =~ s/\$(\w+)/$user_defs{$1}/g;

Voir aussi ``Comment interpoler des appels de fonction dans une chaîne?'' dans cette section de la FAQ.


En quoi est-ce un problème de toujours mettre "$vars" entre guillemets?

Le problème est que ces guillemets forcent la conversion en chaîne, imposant aux nombres et aux références de devenir des chaînes, même lorsqu'on ne le souhaite pas.

Si l'on prend l'habitude d'écrire des choses bizarres comme ça:

    print "$var";       # MAL
    $new = "$old";      # MAL
    somefunc("$var");   # MAL

on se retrouve vite avec des problèmes. Les constructions suivantes devraient (dans 99.8% des cas) être plus simples et plus directes:

    print $var;
    $new = $old;
    somefunc($var);

Autrement, en plus de ralentir, ceci risque de casser le code lorsque ce qui est dans la variable scalaire n'est effectivement ni une chaîne de caractères, ni un nombre mais une référence:

    func(\@array);
    sub func {
        my $aref = shift;
        my $oref = "$aref";  # FAUX
    }

On peut aussi se retrouver face à des problèmes subtils pour les quelques opérations pour lesquels Perl fait effectivement la différence entre une chaîne de caractères et un nombre, comme pour l'opérateur magique d'autoincrémentation ++, ou pour la fonction syscall().

La mise en chaîne détruit aussi les tableaux:

    @lines = `command`;
    print "@lines";             # FAUX - ajoute des blancs
    print @lines;               # correct


Pourquoi est-ce que mes documents <<INSERE ne marchent pas?

Vérifier les trois points suivants:

  1. Il ne doit pas y avoir d'espace après le <<.
  2. Il devrait (habituellement) y avoir un point-virgule à la fin.
  3. On ne peut pas mettre (facilement) d'espace devant la marque.

Si l'on souhaite indenter le texte du document inséré, on peut faire ceci:

    # tout à la fois
    ($VAR = <<HERE_TARGET) =~ s/^\s+//gm;
        your text
        goes here
    HERE_TARGET

Mais la cible HERE_TARGET doit quand même être alignée sur la marge. Pour l'indenter également, il faut mettre l'indentation entre apostrophes.

    ($quote = <<'    FINIS') =~ s/^\s+//gm;
            ...we will have peace, when you and all your works have
            perished--and the works of your dark master to whom you
            would deliver us. You are a liar, Saruman, and a corrupter
            of men's hearts.  --Theoden in /usr/src/perl/taint.c
        FINIS
    $quote =~ s/\s*--/\n--/;

Une jolie fonction d'usage général pour réarranger proprement des documents insérés indentés est donnée ci-dessous. Elle attend un document inséré en argument. Elle regarde si chaque ligne commence avec une sous-chaîne commune, et si tel est le cas, elle l'enlève. Dans le cas contraire, elle prend le nombre d'espaces en tête de la première ligne et en enlève autant à chacune des lignes suivantes.

    sub fix {
        local $_ = shift;
        my ($white, $leader);  # espaces en commun et chaîne en commun
        if (/^\s*(?:([^\w\s]+)(\s*).*\n)(?:\s*\1\2?.*\n)+$/) {
            ($white, $leader) = ($2, quotemeta($1));
        } else {
            ($white, $leader) = (/^(\s+)/, '');
        }
        s/^\s*?$leader(?:$white)?//gm;
        return $_;
    }

Ceci fonctionne avec des chaînes d'en-tête spéciales et déterminées dynamiquement:

    $remember_the_main = fix<<'    MAIN_INTERPRETER_LOOP';
        @@@ int
        @@@ runops() {
        @@@     SAVEI32(runlevel);
        @@@     runlevel++;
        @@@     while ( op = (*op->op_ppaddr)() ) ;
        @@@     TAINT_NOT;
        @@@     return 0;
        @@@ }
    MAIN_INTERPRETER_LOOP

Ou encore avec une quantité fixée d'en-tête d'espaces, tout en conservant correctement l'indentation:

    $poem = fix<<EVER_ON_AND_ON;
       Now far ahead the Road has gone,
          And I must follow, if I can,
       Pursuing it with eager feet,
          Until it joins some larger way
       Where many paths and errands meet.
          And whither then? I cannot say.
                --Bilbo in /usr/src/perl/pp_ctl.c
    EVER_ON_AND_ON


Données: Tableaux


Quelle est la différence entre $array[1] et @array[1]?

La première est une valeur scalaire tandis que la seconde est une tranche de tableaux, ce qui en fait une liste avec une seule valeur (scalaire). On est censé utiliser $ lorsque l'on désire une valeur scalaire (le plus souvent) et @ lorsque l'on souhaite une liste avec une seule valeur scalaire à l'intérieur (très, très rarement, presque jamais en fait).

Il n'y a souvent pas de différence, mais il arrive que si. Par exemple, comparer:

    $good[0] = `some program that outputs several lines`;

avec

    @bad[0]  = `same program that outputs several lines`;

Le drapeau -w prévient lorsque de tels problèmes se présentent.


Comment puis-je extraire seulement les éléments uniques d'un tableau?

Il y a plusieurs moyens, suivant si le tableau est ordonné et si l'on souhaite conserver l'ordre.

a) Si @in est ordonné, et que l'on souhaite obtenir @out ordonné: (ceci suppose que toutes les valeurs du tableau sont "vraies")

    $prev = 'nonesuch';
    @out = grep($_ ne $prev && ($prev = $_), @in);

Ceci est joli en ce qu'il n'utilise que peu de mémoire supplémentaire, simulant le comportement d'uniq(1) de n'enlever que les duplicatas adjacents. C'est moins joli en ce qu'il ne marchera pas avec des valeurs fausses comme undef, 0 ou ``''; ``0 mais vrai'' est correct malgré tout.

b) Si on ne sait pas si @in est ordonné:

    undef %saw;
    @out = grep(!$saw{$_}++, @in);
c) Comme (b), mais @in ne contient que de petits entiers positifs:

    @out = grep(!$saw[$_]++, @in);
d) Un moyen de faire comme (b) mais sans boucles ni greps:

    undef %saw;
    @saw{@in} = ();
    @out = sort keys %saw;  # enlever sort si non souhaité
e) Comme (d), mais @in ne contient que de petits entiers positifs:

    undef @ary;
    @ary[@in] = @in;
    @out = @ary;


Comment puis-je déterminer si une liste ou un tableau contient un certain élément?

D'entendre le mot ``inclus'' est une indication qu'on aurait dû utiliser un tableau de hachage, et pas une liste ou un tableau, pour enregistrer ses données. Les hachages sont conçus pour répondre rapidement et efficacement à cette question, alors que les tableaux ne le sont pas.

Cela dit il y a plusieurs moyens pour résoudre ce problème. Si vous faites cette requête plusieurs fois sur des valeurs de chaînes arbitraires, le plus rapide est probablement d'inverser le tableau original et de laisser traîner un tableau associatif dont les clefs sont les valeurs du premier tableau.

    @blues = qw/azure cerulean teal turquoise lapis-lazuli/;
    undef %is_blue;
    for (@blues) { $is_blue{$_} = 1 }

Vous pouvez alors regarder si telle couleur ($some_color) est du bleu: $is_blue{$some_color}. Il aurait été une bonne idée de conserver les bleus dans un hachage dès le début.

Si les valeurs sont de petits entiers positifs, on peut simplement utiliser un tableau indexé. Ce genre de tableau prendra moins de place:

    @primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
    undef @is_tiny_prime;
    for (@primes) { $is_tiny_prime[$_] = 1; }

On peut alors vérifier si $some_number est un nombre premier ($is_tiny_prime[$some_number]).

Si les valeurs en question sont des entiers plutôt que des chaînes de caractères, on économise un certain espace en utilisant des chaînes de bits à la place:

    @articles = ( 1..10, 150..2000, 2017 );
    undef $read;
    for (@articles) { vec($read,$_,1) = 1 }

Et là vérifier si vec($read,$n,1) est vrai pour un $n.

De grâce, ne pas utiliser

    $is_there = grep $_ eq $whatever, @array;

ou pire encore

    $is_there = grep /$whatever/, @array;

Ces expressions sont lentes (elles vérifient chaque élément même si les premiers correspondent), inefficaces (même raison), et potentiellement sources de bugs (que se passe-t-il s'il y a des caractères spéciaux d'expression régulière dans $whatever?)


Comment puis-je calculer la différence entre deux tableaux? Comment puis-je calculer l'intersection entre deux tableaux?

Utiliser un hachage. Voici un code pour faire les deux et même plus. Il suppose chaque élément dans un tableau donné:

    @union = @intersection = @difference = ();
    %count = ();
    foreach $element (@array1, @array2) { $count{$element}++ }
    foreach $element (keys %count) {
        push @union, $element;
        push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
    }


Comment puis-je trouver le premier élément d'un tableau pour lequel une proposition est vraie?

On peut utiliser ceci si l'index est important:

    for ($i=0; $i < @array; $i++) {
        if ($array[$i] eq "Waldo") {
            $found_index = $i;
            last;
        }
    }

Maintenant $found_index contient la valeur attendue.


Comment puis-je gérer des listes chaînées?

En général, avec Perl, on n'a pas de besoin de liste chaînée, puisqu'avec des tableaux usuels, on peut ajouter (push/unshift) et enlever (pop/shift) de part et d'autre, ou l'on peut utiliser splice pour ajouter et/ou enlever un nombre arbitraire d'éléments à un point arbitraire. Pop et shift sont toutes deux des opérations en O(1) sur les tableaux dynamiques de perl. En absence de shifts et pops, push a en général besoin de faire des réallocations autour de toutes les log(N) fois, et unshift aura besoin de copier des pointeurs à chaque fois.

Si vous le voulez vraiment, vraiment, vous pourriez utiliser les structures décrites dans la page de manuel perldsc ou la page de manuel perltoot et faire exactement comme le livre d'algorithmes dit de faire.


Comment puis-je gérer des listes circulaires?

Des listes circulaires peuvent être gérées de façon traditionnelle par des listes chaînées, ou encore en utilisant simplement quelque chose comme ceci avec un tableau:

    unshift(@array, pop(@array));  # les derniers seront les premiers
    push(@array, shift(@array));   # et vice versa


Comment puis-je mélanger un tableau au hasard?

Utiliser ceci:

    # fisher_yates_shuffle( \@array ) : 
    # génère une permutation aléatoire de @array à sa place
    sub fisher_yates_shuffle {
        my $array = shift;
        my $i;
        for ($i = @$array; --$i; ) {
            my $j = int rand ($i+1);
            next if $i == $j;
            @$array[$i,$j] = @$array[$j,$i];
        }
    }

    fisher_yates_shuffle( \@array );    # permute @array

Vous avez probablement vu des algorithmes de mélange qui fonctionnent en utilisant splice, choisissant au hasard un élément à mettre dans l'élément courant:

    srand;
    @new = ();
    @old = 1 .. 10;  # just a demo
    while (@old) {
        push(@new, splice(@old, rand @old, 1));
    }

Ceci est mauvais car splice est déjà en O(N), et puisqu'on l'exécute N fois, on vient d'inventer un algorithme quadratique; c'est-à-dire en O(N**2). Ceci ne se monte pas en échelle, même si Perl est tellement efficace qu'on ne le remarquerait probablement pas avant d'atteindre des tableaux relativement grands.


Comment puis-je traiter/modifier chaque élément d'un tableau?

Utiliser for/foreach:

    for (@lines) {
        s/foo/bar/;     # changer ce mot
        y/XZ/ZX/;       # échanger ces lettres
    }

En voici un autre; calculons des volumes sphériques:

    for (@volumes = @radii) {   # @volumes contient les parties modifiées
        $_ **= 3;
        $_ *= (4/3) * 3.14159;  # ceci sera transformé en une constante
    }

Si l'on veut faire la même chose pour modifier les valeurs d'une table de hachage, assez étrangement on ne peut pas utiliser la fonction values. Il faut utiliser une tranche:

    for $orbit ( @orbits{keys %orbits} ) {
        ($orbit **= 3) *= (4/3) * 3.14159; 
    }


Comment puis-je sélectionner un élément aléatoire d'un tableau?

Utiliser la fonction rand() (voir rand):

    # en tête du programme:
    srand;                      # inutile pour 5.004 et au-delà

    # et plus tard
    $index   = rand @array;
    $element = $array[$index];

Assurez-vous de n'appeler srand qu'une seule fois par programme. Si vous l'appelez plus d'une fois (comme avant chaque appel à rand), vous êtes certainement en train de faire quelque chose de mal.


Comment permuter N éléments d'une liste?

Voici un petit programme qui génère toutes les permutations de tous les mots de chaque ligne d'entrée. L'algorithme présenté dans la fonction permute() devrait marcher sur toute liste:

    #!/usr/bin/perl -n
    # tsc-permute: permute chaque mot d'entrée
    permute([split], []);
    sub permute {
        my @items = @{ $_[0] };
        my @perms = @{ $_[1] };
        unless (@items) {
            print "@perms\n";
        } else {
            my(@newitems,@newperms,$i);
            foreach $i (0 .. $#items) {
                @newitems = @items;
                @newperms = @perms;
                unshift(@newperms, splice(@newitems, $i, 1));
                permute([@newitems], [@newperms]);
            }
        }
    }


Comment trier un tableau par (n'importe quoi)?

Fournir une fonction de comparaison à sort() (décrit dans sort):

    @list = sort { $a <=> $b } @list;

La fonction de tri par défaut est cmp, la comparaison des chaînes, laquelle trierait (1, 2, 10) en (1, 10, 2). <=>, utilisé ci-dessus, est l'opérateur de comparaison numérique.

Si vous utilisez une fonction compliquée pour extraire la partie sur laquelle vous souhaitez faire le tri, il vaut mieux ne pas le faire à l'intérieur de la fonction sort. L'extraire d'abord, parce que le BLOC de tri peut être appelé plusieurs fois pour un même élément. Voici par exemple comment récupérer le premier mot après le premier nombre de chaque élément, et ensuite faire le tri sur ces mots sans tenir compte de la casse des caractères.

    @idx = ();
    for (@data) {
        ($item) = /\d+\s*(\S+)/;
        push @idx, uc($item);
    }
    @sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

Ceci pourrait aussi s'écrire ainsi, en utilisant un truc qui est connu sous le nom de Transformation Schwartzienne:

    @sorted = map  { $_->[0] }
              sort { $a->[1] cmp $b->[1] }
              map  { [ $_, uc((/\d+\s*(\S+)/ )[0] ] } @data;

Si vous avez besoin de trier sur plusieurs champs, le paradigme suivant est utile:

    @sorted = sort { field1($a) <=> field1($b) ||
                     field2($a) cmp field2($b) ||
                     field3($a) cmp field3($b)
                   }     @data;

Ceci pouvant être aisément combiné avec le pré-calcul des clef comme détaillé ci-dessus.

Voir http://www.perl.com/CPAN/doc/FMTEYEWTK/sort.html pour plus de détails sur cette approche.

Voir aussi ci-dessous la question relative au tri des hachages.


Comment puis-je manipuler des tableaux de bits?

Utiliser pack() et unpack(), ou encore vec() et les opérations bit à bit.

Par exemple, ceci impose à $vec d'avoir le bit N positionné si $ints[N] était positionné:

    $vec = '';
    foreach(@ints) { vec($vec,$_,1) = 1 }

Et voici comment, avec un vecteur dans $vec, amener ces bits dans votre tableau d'entiers @ints:

     sub bitvec_to_list {
        my $vec = shift;
        my @ints;
        # Choisir le meilleur algorithme suivant la densité de bits nuls
        if ($vec =~ tr/\0// / length $vec > 0.95) {
            use integer;
            my $i;
            # Méthode rapide avec surtout des bits nuls
            while($vec =~ /[^\0]/g ) {
                $i = -9 + 8 * pos $vec;
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
                push @ints, $i if vec($vec, ++$i, 1);
            }
        } else {
            # Algorithme général rapide
            use integer;
            my $bits = unpack "b*", $vec;
            push @ints, 0 if $bits =~ s/^(\d)// && $1;
            push @ints, pos $bits while($bits =~ /1/g);
        }
        return \@ints;
    }

Cette méthode devient d'autant plus rapide que le vecteur en bits est clairsemé. (Gracieuseté de Tim Bunce et Winfried Koenig.)


Pourquoi est-ce que defined() retourne vrai sur des tableaux et hachages vides?

Voir defined dans les versions 5.004 et suivantes de Perl.


Données: Hachages (Tableaux associatifs)


Comment puis-je traiter un tableau entier?

Utiliser la fonction each() (voir each) si vous n'avez pas besoin de le trier:

    while ( ($key, $value) = each %hash) {
        print "$key = $value\n";
    }

Si vous le souhaitez trié, il vous faudra utiliser foreach() sur le résultat du tri des clefs, comme montré dans une question précédente.


Que se passe-t-il si j'ajoute ou j'enlève des clefs d'un hachage pendant que j'itère dessus?

Ne faites pas ça.


Comment puis-je rechercher un élément d'un hachage par sa valeur?

Créer un hachage inversé:

    %by_value = reverse %by_key;
    $key = $by_value{$value};

Ceci n'est pas particulièrement efficace. Il aurait été plus efficace pour l'espace de stockage d'utiliser:

    while (($key, $value) = each %by_key) {
        $by_value{$value} = $key;
    }

Si votre hachage pouvait avoir plus d'une valeur identique, les méthodes ci-dessus ne trouveront qu'une seule des clefs associées. Ceci peut être ou ne pas être un problème pour vous.


Comment puis-je savoir combien d'entrées sont dans un hachage?

Si vous voulez dire combien de clefs, alors il vous suffit de prendre dans son sens scalaire la fonction keys():

    $num_keys = scalar keys %hash;

En absence de contexte, ceci remet juste à zéro l'itérateur, ce qui est plus rapide pour les hachages attachés.


Comment puis-je trier un hachage (en option: par valeur plutôt que par clef)?

En interne, les hachages sont conservés d'une manière qui interdit d'imposer un ordre aux paires clef-valeur. A la place, il faut trier une liste des clefs ou des valeurs:

    @keys = sort keys %hash;    # trié par clef
    @keys = sort {
                    $hash{$a} cmp $hash{$b}
            } keys %hash;       # et par valeur

Ici nous ferons un tri numérique inverse sur les valeurs, et si deux valeurs sont identiques, un tri par la longueur de la clef, et si cela échoue par comparaison ASCII directe des clefs (hum, potentiellement modifié par vos valeurs de locale -- voir perllocale).

    @keys = sort {
                $hash{$b} <=> $hash{$a}
                          ||
                length($b) <=> length($a)
                          ||
                      $a cmp $b
    } keys %hash;


Comment puis-je conserver mes hachages toujours ordonnés?

Vous pouvez regarder dans le module DB_File et attacher avec tie() en utilisant les liens de hachage $DB_TREE tel que documenté dans DB_File. Le module Tie::IxHash du CPAN peut aussi être instructif.


Quel est la différence entre "delete" et "undef" pour des hachages?

Les hachages sont des paires de scalaires: le premier est la clef, le second est la valeur. La clef sera convertie en une chaîne, cependant la valeur peut être tout type de scalaire: chaîne, nombre ou référence. Si la clef $key est présente dans le tableau, exists($key) renverra vrai. La valeur d'une clef donnée peut être undef, auquel cas $array{$key} sera undef tandis que $exists{$key} retourne vrai. Ceci correspond à avoir dans le hachage la paire ($key, undef).

Les dessins aident... Voici la table %ary:

          clefs  valeurs
        +-------+-------+
        |   a   |   3   |
        |   x   |   7   |
        |   d   |   0   |
        |   e   |   2   |
        +-------+-------+

Les conditions suivantes sont vérifiées

        $ary{'a'}                       est vrai
        $ary{'d'}                       est faux
        defined $ary{'d'}               est vrai
        defined $ary{'a'}               est vrai
        exists $ary{'a'}                est vrai (perl5 seulement)
        grep ($_ eq 'a', keys %ary)     est vrai

Si vous dites maintenant

        undef $ary{'a'}

la table devient la suivante:

          clefs  valeurs
        +-------+-------+
        |   a   | undef |
        |   x   |   7   |
        |   d   |   0   |
        |   e   |   2   |
        +-------+-------+

et les conditions suivantes sont vérifiées, avec les changements en majuscules:

        $ary{'a'}                       est FAUX
        $ary{'d'}                       est faux
        defined $ary{'d'}               est vrai
        defined $ary{'a'}               est FAUX
        exists $ary{'a'}                est vrai (perl5 seulement)
        grep ($_ eq 'a', keys %ary)     est vrai

Remarquez les deux dernières: on a une valeur undef, mais une clef définie!

Maintenant considérez ceci:

        delete $ary{'a'}

la table se lit maintenant:

          clefs  valeurs
        +-------+-------+
        |   x   |   7   |
        |   d   |   0   |
        |   e   |   2   |
        +-------+-------+

et les conditions suivantes sont vérifiées, avec les changements en majuscules:

        $ary{'a'}                       est faux
        $ary{'d'}                       est faux
        defined $ary{'d'}               est vrai
        defined $ary{'a'}               est faux
        exists $ary{'a'}                est FAUX (perl5 seulement)
        grep ($_ eq 'a', keys %ary)     est FAUX

Voyez, l'entrée entière a disparu!


Pourquoi est-ce que mes hachages attachés ne font pas la distinction entre exists et defined?

Les méthodes EXISTS() et DEFINED() peuvent avoir été implémentées différemment suivant les cas. Par exemple, il n'y a pas de concept d'undef avec des hachages qui sont attachés à des fichiers DBM*. Ceci signifie que les tables de vérité ci-dessus donneront des résultats différents lorsqu'ils sont utilisés sur un tel hachage. Ceci signifie également que exists et defined font la même chose avec un fichier DBM*, et ce qu'ils finissent par faire n'est pas ce qu'ils font avec des hachages ordinaires.


Comment remettre à zéro une opération each() à mi-chemin?

Utiliser keys %hash dans un contexte scalaire renvoie le nombre de clefs d'un hachage et remet à zéro le compteur associé au hachage. Vous pouvez avoir besoin de ceci, si vous sortez prématurément d'une boucle avec un last, pour qu'au retour à cette boucle le compteur du hachage ait été remis à zéro.


Comment puis-je obtenir l'unicité des clefs de deux hachages?

Tout d'abord, extraire les clefs des hachages dans des tableaux, et ensuite résoudre le problème d'unicité dans un tableau tel que décrit plus haut. Par exemple:

    %seen = ();
    for $element (keys(%foo), keys(%bar)) {
        $seen{$element}++;
    }
    @uniq = keys %seen;

Ou plus succinctement:

    @uniq = keys %{{%foo,%bar}};

Ou, pour vraiment économiser de la place:

    %seen = ();
    while (defined ($key = each %foo)) {
        $seen{$key}++;
    }
    while (defined ($key = each %bar)) {
        $seen{$key}++;
    }
    @uniq = keys %seen;


Comment puis-je enregistrer un tableau multidimensionnel dans un fichier DBM?

Soit vous transformez vous-mêmes la structure en une chaîne de caractères (casse-tête), soit vous récupérez le module MLDBM (qui utilise Data::Dumper) du CPAN, et vous l'utilisez au-dessus de DB_File ou GDBM_File.


Comment puis-je faire en sorte que mon hachage conserve l'ordre des éléments que j'y mets?

Utiliser la liaison Tie::IxHash du CPAN.

    use Tie::IxHash;
    tie(%myhash, Tie::IxHash);
    for ($i=0; $i<20; $i++) {
        $myhash{$i} = 2*$i;
    }
    @keys = keys %myhash;
    # @keys = (0,1,2,3,...)


Pourquoi est-ce que de passer à une sous-routine un élément non défini d'un hachage le crée du même coup?

Si on dit quelque chose comme:

    somefunc($hash{"nonesuch key here"});

Alors cet élément ``s'autoactive''; c'est-à-dire qu'il se crée tout seul, que l'on y range quelque chose ou pas. Ceci arrive parce que les fonctions reçoivent des scalaires passés par références. Si somefunc() modifie $_[0], elle doit être prête à la réécrire dans la version de l'appelant.

Ceci a été réglé à partir de perl5.004.

Normalement, d'accéder simplement à la valeur d'une clef dans le cas d'une clef inexistante ne produit pas une clef présente éternellement. Ceci est différent du comportement d'awk.


Comment puis-je faire l'équivalent en Perl d'une structure en C, d'une classe/d'un hachage en C++ ou d'un tableau de hachages ou de tableaux?

Utiliser des références (documentées dans la page de manuel perlref). Des exemples de structures de données complexes sont donnés dans la page de manuel perldsc et la page de manuel perllol. Des exemples de structures et de classes orientées objet sont dans la page de manuel perltoot.


Comment puis-je utiliser une référence comme clef d'un hachage?

On ne peut pas faire cela directement, mais on peut utiliser le module standard Tie::Refhash qui est distribué avec perl.


Données: Divers


Comment puis-je manipuler proprement des données binaires?

Perl est adapté au binaire, donc ceci ne devrait pas être un problème. Par exemple, ceci marche correctement (en supposant les fichiers trouvés):

    if (`cat /vmunix` =~ /gzip/) {
        print "Your kernel is GNU-zip enabled!\n";
    }

Sur certains systèmes, on doit cependant jouer à des jeux éreintants en séparant les fichiers textes (``text'') des fichiers binaires (``binary''). Voir binmode.

Si le problème provient de données ASCII 8-bits, alors voir perllocale.

Si vous souhaitez agir sur des caractères multibits, alors il y a quelques pièges. Voir la section sur les expressions régulières.


Comment puis-je déterminer si un scalaire est un nombre/entier/à virgule flottante?

En supposant que vous ne vous occupiez pas des notations IEEE comme ``NaN'' ou ``Infinity'', il vous suffit probablement d'utiliser une expression régulière.

    warn "has nondigits"        if     /\D/;
    warn "not a natural number" unless /^\d+$/;             # rejette -3
    warn "not an integer"       unless /^-?\d+$/;           # rejette +3
    warn "not an integer"       unless /^[+-]?\d+$/;
    warn "not a decimal number" unless /^-?\d+\.?\d*$/;     # rejette .2
    warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/;
    warn "not a C float"
        unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

Sur un système POSIX, Perl accepte la fonction POSIX::strtod. Sa sémantique est quelque peu malcommode, donc il y a une fonction d'emballage getnum pour un usage plus aisé. Cette fonction prend une chaîne et retourne le nombre qu'elle a trouvé, ou undef pour une entrée qui n'est pas un nombre à virgule flottante du C. La fonction is_numeric est une couverture frontale à getnum, au cas ou vous voudriez simplement demander ``Ceci est-il un nombre à virgule flottante?''

     sub getnum {
        use POSIX qw(strtod);
        my $str = shift;
        $str =~ s/^\s+//;
        $str =~ s/\s+$//;
        $! = 0;
        my($num, $unparsed) = strtod($str);
        if (($str eq '') || ($unparsed != 0) || $!) {
            return undef;
        } else {
            return $num;
        } 
    } 

    sub is_numeric { defined &getnum } 

A la place, vous pouvez également regarder http://www.perl.com/CPAN/modules/by-module/String/String-Scanf-1.1.tar.gz. Le module POSIX (élément de la distribution standard de Perl) fournit les fonctions strtol et strtod pour convertir des chaînes en longs et en doubles, respectivement.


Comment puis-je conserver des données persistantes entre divers appels de programme?

Pour des application spécifiques, on peut utiliser l'un des modules DBM. Voir AnyDBM_File. Plus généralement, vous devriez consulter les modules FreezeThaw, Storable ou Class::Eroot du CPAN.


Comment puis-je imprimer ou copier une structure de données récursive?

Le module Data::Dumper du CPAN est agréable pour imprimer des structures de données et FreezeThaw pour les copier. Par exemple:

    use FreezeThaw qw(freeze thaw);
    $new = thaw freeze $old;

$old peut être (une référence à) n'importe quelle sorte de structure de données que l'on souhaite. Elle sera recopiée en profondeur.


Comment puis-je définir des méthodes pour toutes les classes ou tous les objets?

Utiliser la classe UNIVERSAL (voir UNIVERSAL).


Comment est-ce que je vérifie la somme de contrôle d'une carte de crédit?

Récupérer le module Business::CreditCard du CPAN.


AUTEUR ET COPYRIGHT

Copyright (c) 1997, 1998 Tom Christiansen et Nathan Torkington. Tous droits réservés.

Lorsque ce travail est inclus comme un élément de la distribution standard de Perl, ou comme une partie de sa documentation complète sous forme imprimée ou autrement, il ne peut être distribué que dans les limites fixées par la Perl's Artistic License. Toute distribution de ce fichier ou de ses dérivés hors de cet ensemble nécessite un accord particulier avec le titulaire des droits.

Indépendemment de sa distribution, tous les exemples de code de ce fichier sont ici placés dans le domaine public. Vous êtes autorisés et encouragés à utiliser ce code dans vos programmes que ce soit pour votre plaisir ou pour un profit. Un simple commentaire dans le code précisant l'origine serait de bonne courtoisie mais n'est pas indispensable.


TRADUCTION

Guillaume van der Rest (gvdr@dcmr.polytechnique.fr) La traduction française est distribuée avec les même droits que sa version originale (voir ci-dessus).


RELECTEUR

Régis Julié <Regis.Julie@cetelem.fr>