SELFHTML/Aides à la navigation CGI/Perl Éléments de langage Perl |
Boucles | |
Boucles for |
|
Ces boucles sont avant tout appropriées pour les cas dans lesquels on dispose d'une valeur de départ, d'une valeur de fin et d'un pas d'incrémentation, donc par exemple "pour tous les cas entre 1 et 100".
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; for(my $i = 1; $i <= 100; $i++) { print "<span style=\"font-size:$i\pt\">$i pt</span><br>\n"; } print "</body></html>\n"; |
Le script envoie du code HTML au navigateur et sort du texte en tout 100 fois. Pour ce faire sont notées derrière le mot clé for
, qui introduit une boucle for, entre parenthèses, trois petites instructions. La première instruction déclare la variable-compteur $i
et l'initialise avec la valeur 1. La deuxième instruction est une condition. Elle exprime: "$i
plus petit ou égal à 100". La troisième instruction incrémente de 1 la valeur actuelle de $i
. La boucle sera maintenant exécutée aussi longtemps que la condition du milieu est remplie. Étant donné que dans l'exemple $i
a d'abord pour valeur 1 et qu'ensuite à chaque exécution de la boucle grâce à $i++
il est incrémenté de 1, la boucle va être exécutée 100 fois au total. À la 101 ème fois. $i
est plus grand que 100, la condition n'est plus remplie, et la boucle est terminée.
À la suite de la construction for
suit un bloc d'instructions, marqué comme d'habitude par des parenthèses accolades {
et }
. Entre elles peuvent être placées autant d'instructions que vous le souhaitez. Ces instructions vont être exécutées aussi souvent que la boucle est exécutée, dans l'exemple donc 100 fois. Dans l'exemple, du code HTML est créé avec print
. Celui-ci contient dans un élément span
une définition de format CSS pour la taille de police (font-size
). Pour l'affectation à cette propriété CSS, la scalaire $i
est utilisée. Ce qui aboutit dans l'exemple, à ce que le texte soit sorti 100 fois, et cela chaque fois avec une taille de police un peu plus grande. La première ligne sortie est seulement d'1 pt, donc d'une taille de 1 point, ce que personne pour ainsi dire ne pourra lire. Chaque ligne sortie est un point plus grande et la dernière est de 100 points ce qui suffit bien à remplir la fenêtre.
Pour formuler des conditions telles qu'elle figure dans la deuxième instruction dans la construction de la boucle for
, vous avez besoin, soit de deux valeurs que vous voulez comparer, soit vous demandez directement si une expression figurant dans les parenthèses est vraie ou fausse. Dans l'exemple, deux valeurs sont comparées, à savoir la valeur de $i
avec le nombre 100. Pour cela, vous avez besoin d' Opérateurs de comparaison comme dans l'exemple l'opérateur plus petit que- <
.
Cette forme de boucle est spécialement conçue pour parcourir dans l'ordre l'un après l'autre les éléments d'une liste ou Array (variables) . Une liste est ici examinée élément par élément. Vous pouvez en faire dépendre l'exécution d'instructions.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; my @biens = ("ma maison","ma voiture","mon bateau"); foreach (@biens) { print "$_<br>\n"; } my @faiblesses = ("nicotine","alcool","le sexe faible"); my $faiblesse; foreach $faiblesse (@faiblesses) { print "$faiblesse<br>\n"; } print "</body></html>\n"; |
L'exemple montre deux variantes différant légèrement de la façon d'utiliser la boucle foreach
. Dans les deux cas un tableau est déclaré et des valeurs de départ lui sont affectées, d'une part @biens
et d'autre part @faiblesses
. Derrière le mot clé foreach
est simplement mentionné entre guillemets le tableau. Dans le bloc d'instructions qui suit derrière dans les parenthèses accolades, peuvent figurer autant d'instructions que vous le désirez.
Dans le premier des exemples ci-dessus, est utilisée la variable prédéfinie $_
. Elle sauvegarde toujours la valeur actuelle du bloc d'instructions d'une boucle foreach
, ce qui dans ce cas est l'élément respectivement actuel du tableau @biens
.
Dans le deuxième exemple est utilisé à la place de $_
une scalaire distincte nommée $faiblesse
. Si une telle scalaire est notée entre le mot clé foreach
et la parenthèse avec le tableau, la valeur actuelle du bloc d'instructions de la boucle foreach
, ce qui dans ce cas est l'élément respectivement actuel du tableau @faiblesses
est sauvegardé dans cette scalaire.
Les mots clés for et foreach possèdent certes un arrière plan sémantique différent mais peuvent être échangés l'un contre l'autre sur un plan syntaxique. Perl reconnaît lui-même le type de boucle que vous voulez utiliser. Ainsi, vous pouvez également par exemple écrire ce qui suit:
for(1..1000) {
Le code sort simplement 1000 fois le texte, bien qu'il s'agisse en principe d'une boucle foreach, qui travaille la liste des nombres de 1 à 1000.
print "mille fois touché\n";
}
Ce genre de boucle est approprié quand vous ne savez pas auparavant combien de fois la boucle doit être exécutée. Vous formulez simplement une condition et la boucle sera exécutée aussi souvent que la condition est remplie. Vous devez veiller vous même dans le bloc d'instructions qui dépend de la boucle à ce que la condition soit fausse à un moment ou à un autre et que la boucle soit terminée.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; my $heure_debut = time(); my $heure_fin = $heure_debut + 1; my $heure_actuelle = 0; my $i = 0; while ($heure_actuelle <= $heure_fin) { $heure_actuelle = time(); print "exécuté $i fois<br>"; $i++; } print "</body></html>\n"; |
Le script recherche d'abord avec la fonction Perl time l'heure et la date actuelle et sauvegarde le résultat dans la scalaire $heure_debut
. Ici, c'est un nombre qui est sauvegardé, à savoir le nombre de secondes écoulées entre le 1/1/1970 0.00 heures et la date et l'heure actuelle. Ensuite, une scalaire $heure_fin
est déclarée et se voit affecter une valeur supérieure de 1
à celle de $heure_debut
. Deux autres scalaires $heure_actuelle
et $i
sont déclarées et initialisées avec 0
.
La boucle est introduite par le mot-clé while
. Dans les parenthèses, une condition est formulée. Dans l'exemple, il s'agit de la condition "$heure_actuelle
plus petit ou égal à $heure_fin
". Derrière la condition, dans des parenthèses accolades, suit un bloc d'instructions avec autant d'instructions que vous le souhaitez. Ces instructions sont exécutées aussi souvent que la boucle est parcourue et que la condition est encore remplie.
La condition de la boucle est d'abord remplie dans l'exemple étant donné que $heure_actuelle
a été initialisé avec 0
et est donc assurément plus petit que $heure_fin
. Dans la boucle cependant, $heure_actuelle
reçoit une nouvelle valeur à l'appel de la fonction time
, cette valeur est en toute logique au moins aussi haute que celle de $heure_debut
. La boucle sera de ce fait exécutée aussi souvent qu'il est nécessaire pour que $heure_actuelle
, par l'appel de time
, se voie affecter une valeur plus élevée que $heure_fin
. Alors la boucle est terminée. Quand ce sera le cas, vous ne pouvez naturellement pas le prévoir, c'est dans cette mesure que la boucle while
est idéale ici.
En outre une variable-compteur $i
est encore incrémentée de 1 à chaque passage avec $i++
. La valeur de $i
est sortie à chaque fois. Dans la fenêtre du navigateur appelant, il sera possible de voir à la fin, combien de fois la boucle a été parcourue.
Avec les boucles while
il peut arriver que les instructions qui en dépendent ne soient jamais exécutées, à savoir quand la condition de la boucle s'avère fausse dès le premier passage. Une boucle do
veille à ce que les instructions soient absolument exécutées une fois, étant donné que la condition de la boucle n'est vérifiée qu'à la fin de la boucle.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; my $condition = "interruption"; my $quelque_chose; do { $quelque_chose = $condition; print "Ici figure $quelque_chose"; } until ($quelque_chose eq $condition); print "</body></html>\n"; |
L'exemple présente la façon typique de fonctionner d'une telle boucle. Dans un premier temps, une scalaire $condition
se voit affecter une valeur initiale interruption
. Une autre scalaire nommée $quelque_chose
est déclarée sans recevoir de valeur. La boucle est introduite par do
. Derrière duit un bloc d'instructions dans des parenthèses accolades, qui peut contenir autant d'instructions que vous le souhaitez. Dans l'exemple, la valeur de $condition
, à savoir interruption
est tout de suite dès le début affectée à la scalaire $quelque_chose
. Ensuite ce contenu est sorti pour contrôle. Après la parenthèse accolade de fermeture qui met fin au bloc d'instructions, le mot until
est noté suivi entre parenthèses de la condition proprement dite. Dans l'exemple, il est vérifié si $quelque_chose
et $condition
sont égaux, donc s'ils ont le même contenu. Étant donné que ce contenu a bien été affecté dans la boucle, la condition de la boucle est donc remplie. Ce qui fait que la boucle est interrompue. Car until
est à comprendre comme "aussi longtemps que". À l'inverse de la boucle while
dont le bloc d'instructions est exécuté aussi longtemps que la condition est vraie, le bloc d'instructions est ici exécuté jusqu'à ce que la condition soit vraie.
Dans l'exemple, la boucle est exécutée une fois bien que la condition de la boucle soit déjà vraie dès le premier passage. La raison est justement que le code qui dépend de la boucle est d'abord exécuté et qu'ensuite seulement la condition est vérifiée.
Il existe également en Perl des boucles do
pour lesquelles la condition n'est pas précédée de until
, mais de while
. Vous devez alors formuler la condition de la boucle négativement.
De la même façon que les tableaux peuvent parfaitement être "traversés", donc parcourus élément par élément avec les boucles foreach, cette possibilité existe naturellement aussi pour les Hashes. Étant donné qu'un élément de hash est toujours composé de deux valeurs dont la première est la clé et la deuxième la valeur proprement dite, un simple parcours comme avec foreach
n'est pas possible. C'est pourquoi existe pour les hashes une syntaxe de boucle qui leur est propre.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; my %famille = (femme => "Jeanne", fille => "Fanny", fils => "Florian"); my $clef; my $valeur; while (($clef, $valeur) = each(%famille)) { print "$clef correspond à $valeur<br>\n"; } while ($clef = each(%famille)) { print "$clef correspond à $famille{$clef}<br>\n"; } print "</body></html>\n"; |
L'exemple déclare un hash nommé %famille
et lui affecte trois paires clé-valeur. Ensuite deux scalaires $clef
et $valeur
sont déclarées, qui sont nécessaires dans la boucle. La boucle est formulée en tant que boucle while
. À l'intérieur de la condition de la boucle, est appelée cependant la fonction Perl each. Celle-ci donne au choix une liste avec deux éléments, à savoir la clé suivante et la valeur qui y correspond, ou bien - dans un contexte scalaire - seulement la clé suivante du hash transmis.
Le script en exemple montre les deux variantes. Dans la première variante, la liste est sauvegardée avec les deux éléments dans l'expression ($clef, $valeur)
. $clef
contient la clé respectivement actuelle du hash, et $valeur
la valeur de la donnée correspondante. Dans l'exemple, la boucle est parcourue trois fois et sort ces enregistrements sous la forme femme correspond à Jeanne
.
Dans la deuxième variante la fonction each
est appelée dans un contexte scalaire, étant donné que la valeur retournée n'est sauvegardée que dans $clef
. L'instruction print
formulée dans la boucle sort cependant la même chose que dans la première variante. Cette fois cependant, aucune variable $valeur
n'est disponible. Par une construction comme $famille{$clef}
il peut cependant être accédé à la valeur actuelle respective .
La récursivité est un moyen auquel recourir quand les boucles ne suffisent plus. Un cas typique d'utilisation pour la récursivité est le parcours de structures arborescentes. En français: quand par exemple vous voulez lire toute une arborescence de répertoires sans en connaître la structure des fichiers et des répertoires, alors c'est un cas typique pour une application récursive. Pour la récursivité, un sous-programme est défini dans lequel figure une instruction qui appelle à nouveau le sous-programme. Il s'ensuit un effet d'imbrication. La récursivité est de toutes façons pas tout à fait exempte de critique sur le plan informatique. C'est pourquoi elle doit être programmée proprement.
L'exemple suivant montre comment vous pouvez lire une structure de répertoires à partir d'un répertoire donné et pouvez sortir formaté en HTML le résultat au navigateur appelant. L'exemple, au demeurant, n'est pas tout à fait banal.
#!/usr/bin/perl -w use strict; use CGI::Carp qw(fatalsToBrowser); my $repertoire_depart = "/usr/local/web"; my @tous; my $total_octets = 0; print "Content-type: text/html\n\n"; print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "\n"; print "<html><head><title>Sortie du test</title>\n"; print "</head><body>\n"; print "<h1>Atborescence</h1>\n"; print "<pre>repertoire_depart: <b>$repertoire_depart</b></pre>\n"; print "<hr noshade size=\"1\"><pre>\n"; rechercher($repertoire_depart); @tous = sort(@tous); foreach (@tous) { print "$_\n"; } print "</pre><hr noshade size=\"1\">\n"; print "<pre>En tout: [$total_octets octets]</pre>\n"; print "</body></html>\n"; sub rechercher { my $repertoire = shift; my $enregistrement; my $nom_chemin; my $enregistrement_HTML; my $octets; local *DH; unless (opendir(DH, $repertoire)) { return; } while (defined ($enregistrement = readdir(DH))) { next if($enregistrement eq "." or $enregistrement eq ".."); $nom_chemin = $repertoire."/".$enregistrement; if( -d $nom_chemin) { $HTML_enregistrement = $repertoire."/".$enregistrement." [REPERTOIRE]"; } else { $octets = -s $nom_chemin; $total_octets += $octets; $HTML_enregistrement = $repertoire."/".$enregistrement." [$octets octets]"; } push(@tous, $HTML_enregistrement); rechercher($nom_chemin) if(-d $nom_chemin); } closedir(DH); } |
Tout d'abord trois variables importantes sont déclarées: $repertoire_depart
sauvegarde le répertoire à partir duquel la recherche doit être effectuée, @tous
est la liste dans laquelle, par la suite, les éléments lus seront sauvegardés, et $total_octets
recherche le nombre d'octets de tous les fichiers.
Ensuite commence la sortie en HTML au navigateur. Au dessous, figure l'instruction rechercher($repertoire_depart);
. C'est un appel du sous-programme rechercher
, qui est introduit un peu plus bas avec sub rechercher
. Ce sous-programme est en même temps celui s'appelle lui même dans l'avant-dernière instruction du bloc d'instructions qu'il comprend, en provoquant ainsi la récursivité.
Avec l'instruction rechercher($repertoire_depart);
a lieu la lecture complète de la structure des fichiers et des répertoires. Ensuite la liste est triée alphabétiquement avec la fonction sort
pour être sortie dans le navigateur enregistrement par enregistrement avec une boucle foreach
.
Le cœur du script est cependant le sous-programme rechercher
. Y sont d'abord déclarées une série de variables de travail. Étant donné que le sous-programme se rappelle lui-même, la question se pose s'il ne s'ensuit pas une confusion avec les noms de variables. La réponse est non. Car chaque exécution du sous-programme crée une instance distincte du programme dans la mémoire de travail, et étant donné que les variables sont déclarées localement avec my
, leur validité reste cantonnée à une instance.
Une exception évidente est formée par l'instruction local *DH
, qui déclare localement le descripteur de répertoire DH
. Étant donné que my
ne peut pas être appliqué aux descripteurs de fichiers et de répertoires (ou plutôt aux Typeglobs), on a eu recours ici à cette solution, qui certes travaille quelque peu différemment en interne mais donne le résultat désiré. Une autre variante serait d'utiliser la module standard Symbol
et de se créer dans chaque instance du sous-programme un nouveau descripteur de répertoire. Si le procédé paraît plus "propre", ce n'est finalement pas le cas. Par ailleurs, la variante avec local
est sensiblement plus rapide.
Les nombreuses instances du sous-programme pour beaucoup de répertoires impliquent aussi que toujours plus de mémoire de travail est nécessaire. C'est un inconvénient important de la récursivité. Les constructions avec de nombreux appels de sous-programmes à eux-mêmes en récursivité doivent être évités pour cette raison dans les scripts CGI qui peuvent être appelés souvent et plusieurs fois en même temps sur les serveurs Web publics.
Le sous-programme rechercher
attend un nom de chemin qui lui est transmis. Avec $repertoire = shift;
le nom de chemin transmis est sauvegardé dans la scalaire $repertoire
(voir aussi à ce sujet la fonction Perl shift). Ensuite le répertoire transmis est ouvert grâce à la fonction opendir . Ses enregistrements sont lus dans une boucle while
avec la fonction readdir. Les deux enregistrements avec les valeurs .
et ..
, qui figurent dans chaque répertoire et qui symbolisent le répertoire actuel ou le répertoire parent sont sautés avec la commande de saut next. Autrement, la récursivité s'embrouillerait dans une boucle sans fin.
Avec l' opérateurs de test fichier pour fichiers et répertoires -d
dans if( -d $nom_chemin)
il est demandé si l'enregistrement du répertoire respectivement actuel est à nouveau un répertoire, donc un sous répertoire. En fonction de cela un élément HTML pour la liste à sortir est préparé. Plus bas il est demandé encore une fois avec -d
si l'enregistrement est un sous-répertoire, et selon la réponse, le sous-programme rechercher
est à nouveau appelé avec le sous-répertoire.
Après que la structure des répertoires est traitée et que toutes les instances du sous-programme rechercher
sont terminées, Cela continue dans la partie supérieure du script avec @tous = sort(@tous);
.
Commandes de saut | |
Instructions conditionnelles | |
SELFHTML/Aides à la navigation CGI/Perl Éléments de langage Perl |
© 2001 Stefan Münz / © 2003 Traduction Serge François, 13405@free.fr
selfhtml@fr.selfhtml.org