Developpez.com - Autres
X

Choisissez d'abord la catégorieensuite la rubrique :

Sommaire > Structures de contrôle
        Tout en bloc !
        Visibilité réduite à 6 miles ...
        Longue portée ...
        Quelle alternative ?
        Liberté conditionnelle ...
        Sélection ... La raideur indigeste ...
        Répétitions et itérations
        Boucles bornées pour programmeurs obstinés
        Boucles comptées, mais quand on aime ...
        Boucles d'énumération
        Points de ruptures ... de séquences
        Sauter, c'est le meilleur moyen de se casser la figure.
        L'appel des fonctions à la pelle
        Le départ, des paramètres à mettre
        Et on les récupère comment, les paramètres, dans la fonction ?
        Et avec des listes comme paramètres ?
        Pensons au retour

        


Tout en bloc !
auteur : 2Eurocents
En PERL, il est possible, et même souhaitable, d'organiser le code en blocs d'instructions. Ces blocs seront ou ne seront pas exécutés en fonction de différentes conditions - que l'on nomme alternatives - ou pourront être répétés autant que de besoin - itératives. Un bloc débute par une accolade ouvrante et se termine par une accolade fermante. Les blocs peuvent être imbriqués les uns dans les autres. Un bloc peut contenir indifféremment d'autres blocs ou des commandes isolées. Il est de coutume d'indenter les blocs, c'est à dire d'introduire un ou plusieurs espaces de marge à gauche des instructions contenues dans les blocs. Plus il y a de niveaux d'imbrication, plus on introduit d'espaces. Ceci permet une lecture plus aisée de la structure du code et permet même à certains éditeurs de texte de la mettre en valeur par des barres verticales reliant le début à la fin du bloc, ou par l'apparition de boutons permettant de masquer/démasquer le bloc à volonté (cas de SciTE, Scintilla Text Editor, logiciel libre d'édition de code source).


Visibilité réduite à 6 miles ...
auteur : 2Eurocents
La notion de bloc d'instruction limite la visibilité des variables. En effet, quel que soit leur type (scalaires, tableaux, tables associatives), les variables ne sont visibles - accessibles - qu'à l'intérieur du bloc où elles sont définies. Elles sont donc utilisables à l'intérieur du bloc d'accolades contenant leur déclaration ou à l'intérieur des blocs que celui-ci contient. En outre, toute redéclaration d'une variable de même nom à l'intérieur d'un bloc masquera automatiquement la valeur que la variable avait acquise à l'extérieur de ce bloc. La variable reprendra sa valeur d'origine aussitôt que l'exécution du bloc contenant la redéclaration sera terminée. On parle alors de variables locales, la portée de la variable (là d'où l'on peut l'utiliser) étant limitée à ce seul bloc d'instructions.


Longue portée ...
auteur : 2Eurocents
La portée et la visibilité que l'on vient de définir sont en fait valables pour les variables déclarées au moyen du mot-clef my. Celui-ci limite la portée de la variable au bloc en cours d'exécution. Pour qu'une variable soit accessible hors de ce bloc, il existe deux solutions :
  • définir cette variable hors de tout bloc d'instruction. Elle est alors disponible dans tout le script.
  • déclarer cette variable avec le mot-clef our. Elle est alors disponible dans tout le script et peut même, sous certaines conditions (cas des modules) être accédée depuis l'extérieur.


Quelle alternative ?
auteur : 2Eurocents
Il y a souvent besoin de réaliser un choix entre deux blocs à traiter, selon la valeur d'une condition. C'est ce que l'on appelle une alternative. En PERL, une condition est une expression, constituée de variables, de valeurs, d'opérateurs de tests (<, >, ne, !=, le, ge, cmp, etc.) et éventuellement d'opérateurs de combinaison logique (not, and, or, xor). Le tout doit être habilement parenthèsé afin de garantir les associations de tests souhaitées. PERL met à notre disposition deux types d'alternative :
  • l'instruction if, qui représente l'alternative favorable - quand une condition est remplie,
  • l'instruction unless, qui représente l'alternative défavorable - quand une condition n'est pas remplie.
La syntaxe est simple :

if ( condition ) { # bloc d'instructions à exécuter si la condition est vraie }
Pour le cas où l'on souhaite une alternative complète, on peut même faire :

if ( condition ) { # bloc d'instructions à exécuter si la condition est vraie } else { # bloc d'instructions à exécuter si la condition est fausse }
Et pour les cas de suites d'alternatives imbriquées :

if ( condition_primaire ) { # bloc d'instructions à exécuter si la condition primaire est vraie } elsif (nouvelle_condition) { # bloc d'instructions à exécuter si la nouvelle condition est vraie # alors que la condition primaire est fausse } else { # bloc d'instructions à exécuter si la nouvelle condition est fausse # alors que la condition primaire est fausse }
Il est ainsi possible de cascader les elsif dans chaque condition fausse. L'instruction unless fonctionne exactement comme le if, simplement sa condition est inversée. Ainsi, il est équivalent d'écrire unless (condition) ... et if ( ! ( condition ) ) ...


Liberté conditionnelle ...
auteur : 2Eurocents
Les alternatives que nous venons de voir sont très utiles mais nécessitent l'ouverture et la fermeture de blocs de codes. Cela peut être fastidieux, juste pour une instruction dont l'exécution est conditionnelle. PERL a donc introduit une expression simplifiée pour l'exécution conditionnelle d'une seule instruction :

instruction if (condition);
ou

instruction unless (condition);
exécuteront l'instruction indiquée si la condition est vraie (cas du if) ou fausse (cas du unless).


Sélection ... La raideur indigeste ...
auteur : 2Eurocents
Lorsque l'on a une même variable à tester dans différentes conditions, pour vérifier les différentes valeurs possibles, il est nécessaire d'utiliser une batterie de if/elsif/else. ce qui peut paraître rapidement fastidieux et peu clair :

if ($choix == 0) { # traitement du 0 } elsif ($choix == 1) { # traitement du 1 } elsif ($choix == 2) { # traitement du 2 } elsif ($choix == 3) { # traitement du 3 } else { # traitement par défaut }
Le mécanisme de la sélection (le switch des programmeurs C, le case des pascalistes et des basicois) n'est malheureusement pas disponible pour le moment en PERL (version 5.8 aujourd'hui). Toutefois, devant l'intérêt qu'une telle structure présente, une construction non standard a été ébauchée, et elle sera probablement standardisée pour la version 6 du langage. En attendant, cette fonctionnalité dépend d'un module (une unité d'extension du langage) qui est présent la plupart du temps lors de l'installation de PERL. Il faut l'utiliser avec précaution car elle n'est pas totalement finalisée et possède encore quelques effet de bord, notamment dans les scripts les plus longs. Cependant, voici une ré-écriture de la sélection, sans les if :

use Switch 'Perl6'; # pour avoir le mécanisme de la sélection conforme à ce que permettra PERL6 given ($choix) { when 0 { # traitement du 0 } when 1 { # traitement du 1 } when 2 { # traitement du 2 } when 3 { # traitement du 3 } when /.*/ { # traitement par défaut } }
C'est un mécanisme à suivre car il réservera de nombreuses (bonnes) surprises.


Répétitions et itérations
auteur : 2Eurocents
Plusieurs formes de parcours de boucles, de répétitions ou d'itérations sont disponibles. Elles sont à adapter en fonction des besoins. Le but est toujours de parcourir un bloc de code tant que certaines conditions sont vérifiée, ou bien un certain nombre de fois, ou bien pour toutes les valeurs d'une liste, comme les fonctions map et grep.


Boucles bornées pour programmeurs obstinés
auteur : 2Eurocents
Ce sont les boucles qui exigent une condition de sortie. On y trouve les boucles avec une condition permanente :

while (condition) { # bloc d'instructions }
où le bloc d'instructions est exécuté tant que la condition est vraie. On y trouve aussi les boucles avec une condition terminale :

until (condition) { # bloc d'instructions }
où le bloc d'instructions est exécuté jusqu'à ce que la condition soit vraie. Ces deux formes de boucle acceptent aussi une syntaxe abrégée lorsque le bloc d'instructions se réduit à une seule instruction :

instruction while (condition);
ou

instruction until (condition);

Boucles comptées, mais quand on aime ...
auteur : 2Eurocents
Ce sont des boucles pour lesquelles une variable sert à dénombrer les itérations et à arrêter la répétition lorsqu'une valeur donnée est atteinte. La syntaxe est alors :

for (initialisation ; condition ; incrementation) { # bloc d'instructions }
où l'initialisation est l'affectation de valeurs initiales aux variables d'index de boucle ; la condition est la condition d'arrêt de la répétition et l'incrémentation est l'instruction qui altère les variables d'index de boucle à chaque passage. Chacune des trois clauses est optionnelle - l'initialisation peut être faite en dehors de la boucle, la condition et l'incrémentation pouvant être réalisés dans le bloc d'instructions. Par exemple :

for ($i=0 ; $i<10 ; $i++) { print "$i\n"; }
affiche les valeurs de 0 à 9.

for (($i, $j)=(0, 10) ; $i<10 ; ($i++,$j--)) { print "[$i,$j]\n"; }
affiche un $i croissant et un $j décroissant. En fait, la syntaxe est équivalente à un :

initialisation while (condition) { # bloc d'instructions incrémentation }

Boucles d'énumération
auteur : 2Eurocents
Il est possible de parcourir la totalité des éléments d'une liste, et pas seulement au moyen des instructions map et grep. La fonction each () est destiné aux énumérations de tableaux associatifs. Elle prend une table de hachage comme argument et retourne une clef de cette table. A l'appel suivant, elle retournera la clef suivante. Et ainsi de suite jusqu'à épuisement des clefs. On voit bien qu'intégrée à une boucle while/until ou for, cette fonction peut énumérer tous les éléments d'un hachage. L'autre construction d'énumération très utilisée est la boucle foreach. La syntaxe complète est :

foreach $variable (@liste) { # bloc d'instruction }
Le bloc d'instruction est exécuté pour chaque valeur contenue dans la liste @liste. La variable $variable prendra, à chaque itération, les valeurs successives des éléments de @liste. Il existe aussi une syntaxe abrégée utilisant la variable implicite de contexte $_. Cette variable, interne à PERL permet de simplifier de nombreuses syntaxes. La variable $_ contient, le plus souvent, l'élément en cours de traitement - d'où l'appellation de variable de contexte :

foreach (@liste) { # bloc d'instruction # l'élément en cours est $_ }
Un petit exemple supplémentaire, sachant que la fonction rand retourne un nombre flottant pseudo-aléatoire dans l'intervalle [0..n], n étant passé en paramètre :

# Liste des sentences pour chaque pétale possible (6 x 10) my @marguerite = ('je t\'aime', 'un peu', 'beaucoup', 'passionnément', 'à la folie', 'pas du tout')x10; # Tirage du nombre de pétales, de 0 à 59 $i=int (rand (59)); # Pour tous les pétales ... foreach (0..$i) { # On effeuille la marguerite print $marguerite[$_]."\n"; }

Points de ruptures ... de séquences
auteur : 2Eurocents
Un programme avec un *BON* algorithme se déroule normalement sans problèmes, tous les cas sont prévus dans les alternatives, les boucles sont bien conçues et tout est pour le mieux. Cependant, il arrive que des changements dans les traitements, au cours de la maintenance du code, ou bien des difficultés imprévues de conception, obligent le programmeur à introduire des conditions particulières de rupture des structures de contrôle. Ces ruptures ne sont pas un signe de bonne conception ... plutôt le signe qu'une modification, apparemment bégnine aurait eu de grosses répercussions sur la structure du code et qu'il a été préférable d'en limiter l'ampleur par un petit traitement localisé.
  • redo; est l'instruction de rupture qui permet de reprendre l'itération en cours au début du bloc de code.
  • next; est l'instruction qui permet de passer à l'itération suivante. Elle effectue un saut direct à la fin du bloc de code.
  • last; est l'instruction qui permet d'arriver directement à la fin de la dernière itération. On sort des traitements/boucles et on continue l'exécution du programme aux instructions qui suivent.
Ces ruptures de séquence fonctionnent aussi sur des boucles imbriquées. Dans ce cas, pour savoir de quelle boucle on souhaite rompre la séquence, il faut étiquetter les boucles et préciser l'étiquette à la rupture de séquence :

EXTERNE: foreach $i (0..10) { INTERNE: foreach $j (0..10) { next EXTERNE if (($i + $j) > 10) print "i=$i - j=$j\n" } }
Ici, dès que la valeur de $j est suffisante pour que ($i+$j) soit supérieur à 10, on passe à la valeur suivante de $i, par passage à l'itération suivante de la boucle EXTERNE. On aurait aussi pu remplacer le next EXTERNE par un last INTERNE, le résultat aurait été le même ... mais en PERL, il y a plus d'une façon de le faire !


Sauter, c'est le meilleur moyen de se casser la figure.
auteur : 2Eurocents
Il existe aussi une instruction goto qui permet de faire un saut dans l'exécution du code, jusqu'à une étiquette donnée (fixée à l'écriture du programme, ou évaluée dynamiquement. Utiliser ce type d'instruction n'est pas vraiment une bonne idée. Le langage étant par ailleurs riche en structures de contrôles et en instructions de ruptures, l'usage de cette instruction peut être carrément néfaste, à la maintenance du code si ce n'est à son exécution.


L'appel des fonctions à la pelle
auteur : 2Eurocents
Puisque les sauts sont déconseillés dans le contrôle du flux d'exécution du programme, il existe une structure 'propre' qui permet d'abandonner le bloc courant pour exécuter un autre bloc de code et revenir ensuite à notre point de 'digression'. C'est la notion de sous-routine, procédure ou fonction. Cela permet d'écrire une seule fois un bloc de code qui sera exécuté à de nombreuses reprises dans un programme, depuis plusieurs endroits différents. Une fonction se déclare avec le mot clef sub, suivi du nom de la fonction, suivi du bloc de code à exécuter :

sub A_la_trace { print "Tout passage par la fonction est suivi A_la_trace\n"; }
Cette fonction s'appelle simplement par son nom, dans tout le code :

print "Début"; A_la_trace; print "Milieu"; A_la_trace; print "Fin";

Le départ, des paramètres à mettre
auteur : 2Eurocents
La fonction ainsi définie peut aussi admettre des paramètres. PERL n'étant pas très strict ni rigide, il peut même laisser le nombre de paramètres des fonction totalement libre. Une fonction qui admet des paramètres s'appelle en précisant les paramètres entre parenthèses après le nom de la fonction :

print "Début"; A_la_trace ("Début", "Milieu"); print "Milieu"; A_la_trace ("Milieu", "Fin"); print "Fin";
réalise l'appel de A_la_trace en lui passant deux chaînes comme paramètres à chaque fois. Les parenthèses ne sont toutefois pas une obligation, mais elles aident grandement à la lisibilité du code et permettent d'éviter quelques soucis avec la précédence des opérateurs ...


Et on les récupère comment, les paramètres, dans la fonction ?
auteur : 2Eurocents
Lors de l'appel à une fonction, PERL range les paramètres dans une liste spéciale, @_, interne à la fonction. Cette liste se comporte comme toutes les autres. Il n'est, par contre, pas recommandé de tenter de la modifier. Il est donc possible de récupérer les valeurs de différentes façons :

my ($param1, $param2) = @_;
récupère les 2 premiers paramètres (@_ est inchangée).

my ($param1) = @_;
est équivalent à my $param1 = $_[0];

my $param = shift;
récupère le premier paramètre de la liste et le supprime d'y-celle. Le nombre de paramètres effectif étant laissé libre, il peut être pratique de le connaître, ne serait-ce que pour effectuer le nombre de shift correct pour récupérer toutes les valeurs. Le nombre de paramètres reçus se trouve simplement par l'évaluation de @_ en contexte scalaire ($i = @_; ou bien $i = scalar (@_);).


Et avec des listes comme paramètres ?
auteur : 2Eurocents
On l'a vu, à la réception dans la fonction, les paramètres sont rangés dans une liste. Si lors de l'appel on a spécifié une liste comme paramètre de la fonction, celle-ci va s'aplatir dans la liste @_. Pour conserver le caractère 'liste' des paramètres, il faut passer par la notion de référence, qui sera vue ultérieurement. De même, si l'on souhaite modifier la valeur des paramètres, les références pourront nous aider.


Pensons au retour
auteur : 2Eurocents
Une fonction se termine dans deux cas :
  • la fin du bloc de code de la fonction est atteinte. Retour à l'appelant.
  • une instruction return est rencontrée. Retour à l'appelant, mais on lui transmet les valeurs qui suivent le mot clef return.
Les valeurs retournées peuvent être aussi bien un scalaire qu'une liste. Il est donc possible d'avoir des fonctions retournant des listes d'éléments (concept que l'on a finalement déjà rencontré avec map ou grep). Nous sommes maintenant à peine aguerris mais suffisamment équipés pour aller beaucoup plus loin avec le langage PERL ... l'odyssée ne fait que commencer.


        

Consultez les autres F.A.Q's

Ce document issu de www.developpez.com est soumis à la licence GNU FDL traduit en français ici
Permission vous est donnée de distribuer, modifier des copies de cette page tant que cette note apparaît clairement.
Contacter le responsable de la rubrique Autres