Les spécificités.

Les blocs spécifiques.

Il existe en Perl un certain npmbre de blocs spécifiques auxquels ont été attribués des noms prédéfinis. Les conditions d'exécution de ces blocs sont régis par des régles simples.

Le bloc BEGIN.

BEGIN {
  Instruction_1;
  Instruction_2;
  . . . . .
  Instruction_n;
}
  

Il sera exécuté dés que possible. En fait, dés qu'il aura été complètement défini et avant même que la totalité du fichier qui le contient soit totalement analysé.

Il n'est pas interdit de trouver plusieurs blocs BEGIN dans un même fichier. Si tel est le cas, ils seront exécutés dans l'ordre de leur définition.
Le fait que le bloc BEGIN s'exécute immédiatement permet de définir des fonctions en provenance d'autres fichiers suffisament rapidement pour qu'elles soient visibles au moment de la compilation du reste du fichier.
Ceci doit être pris en compte car la maniére dont le reste du fichier sera analysé depend de la déclaration des fonctions.

Le bloc END.

END {
  Instruction_1;
  Instruction_2;
  . . . . .
  Instruction_n;
}
  

C'est le pendant du bloc BEGIN, il sera exécuté le plus tardivement possible. En fait, lorsque l'interpréteur finit son travail, même si cette sortie résulte d'une fonction (die) ou d'une exeption interne (fonction inexistante).

Il n'est pas interdit de trouver plusieurs blocs END dans un même fichier. Si tel est le cas, ils seront exécutés dans l'ordre inverse de leur définition.

Programme fs1.pl Exécution sur l'écran
#!/usr/bin/perl;
BEGIN{
  print ("Ligne imprimee par BEGIN.\n")
}
END{
  print ("Ligne imprimee par END.\n")
}
die ("Ligne imprimee par die.\n");
    
c:\progs> perl fs1.plent
Ligne imprimee par BEGIN.
Ligne imprimee par die.
Ligne imprimee par END.
c:\progs>
    

Le bloc AUTOLOAD.

Il est normalement interdit de faire référence à une fonction inexistante. Toutefois, si ce désagrément se produit, il peut être géré au moyen de procédure spécifique AUTOLOAD qui sera activée lorsqu'une référence sera faite é une procédure inexistante.
Le nom de la procédure en question sera passé dans une variable spécifique $AUTOLOAD et la liste d'arguments de cette procédure se trouvera dans la liste standard @_.

Programme fs2.pl Exécution sur l'écran
#!/usr/bin/perl;
BEGIN {
  print ("Soyez les bienvenus.\n\n");
}
END {
  print ("Merci de votre visite.\n\n");
}
AUTOLOAD {
  print ("La fonction $AUTOLOAD\n");
  print ("Dont les arguments sont :\n");
  print ("@_\n");
  print ("n'existe pas.\n\n");
}
print ("Debut du programme.\n");
print ("Appeler d'une fonction inexistante :\n");
print ("Nulle_Part(\"Ici\",\"et\",\"la\");\n\n");
do Nulle_Part("Ici","et","la");
    
c:\progs> perl fs2.plent
Soyez les bienvenus.

Debut du programme.
Appel d'une fonction inexistante :
Nulle_Part("Ici","et","la");

La fonction main::Nulle_Part
Dont les arguments sont :
Ici et la
n'existe pas.

Merci de votre visite.

c:\progs>
    

La liste @INC.

C'est une liste prédéfinie qui contient les répertoires dans lesquels s'effectuera une recherche lancée par la fonction spécifique require.

Programme fs3.pl Exécution sur l'écran
#!/usr/bin/perl;
foreach $a (@INC) {
  print ("$a\n");
}
    
c:\progs> perl fs3.plent
c:\perl\lib
c:\perl\site\lib
.
c:\progs>
    

Il est possible, si besoin est, d'ajouter des chemins dans cette liste.

push (@INC,"c:\progs\perl);
  

Ajoute mon chemin personnel en fin de liste.

unshift (@INC,"c:\progs\perl);
  

Ajoute mon chemin personnel en début de liste.

La fonction require().

Cette fonction va permettre de découper un programme en fichiers disjoints permettant ainsi de créer des bibliothéques de fonctions.
Par exemple, considérons le programme suivant.

c:\progs> type chchain.plent 
#!/usr/bin/perl
$critere = "l";
$nl = 0;
$emplacement = 0;
$noccurences = 0;
$ligne = <>;
while ($ligne ne "") {
  $nl++;
  while (1) {
    $emplacement = index($ligne, $critere, $emplacement);
    last if ($emplacement == -1);
    $noccurences++;
    $emplacement++;
  }
  if ($noccurences != 0){
    print ("Sur la ligne $nl\n");
    print ("la chaine '$critere'\n");
    print ("a été trouvée $noccurences fois.\n")
  } else {
    print ("Sur la ligne $nl\n");
    print ("la chaine '$critere'\n"); 
    print ("n'est pas présente.\n")
  }
$ligne = <>;
$emplacement = 0;
$noccurences = 0;
}
"vrai";
c:\progs>
  

Il a été édité et stocké sous le nom chchain.pl à un emplacement dont le chemin apparaît parmis ceux listés dans la variable @INC.
Voyons maintenant comment nous allons lui faire référence à partir d'un autre programme.

Programme fs4.pl Exécution sur l'écran
#!/usr/bin/perl;
@ARGV =("Poeme.txt");
require ("chchain.pl");
    
c:\progs> perl fs4.plent
Sur la ligne 1, la chaine 'l' a été trouvée 1 fois.
Sur la ligne 2, la chaine 'l' n'est pas présente.
Sur la ligne 3, la chaine 'l' n'est pas présente.
Sur la ligne 4, la chaine 'l' a été trouvée 3 fois.
Sur la ligne 5, la chaine 'l' n'est pas présente.
Sur la ligne 6, la chaine 'l' a été trouvée 1 fois.
Sur la ligne 7, la chaine 'l' n'est pas présente.
Sur la ligne 8, la chaine 'l' a été trouvée 2 fois.
Sur la ligne 9, la chaine 'l' a été trouvée 1 fois.
Sur la ligne 10, la chaine 'l' n'est pas présente.
Sur la ligne 11, la chaine 'l' a été trouvée 1 fois.
Sur la ligne 12, la chaine 'l' a été trouvée 2 fois.
Sur la ligne 13, la chaine 'l' a été trouvée 1 fois.
Sur la ligne 14, la chaine 'l' n'est pas présente.
Sur la ligne 15, la chaine 'l' a été trouvée 3 fois.
Sur la ligne 16, la chaine 'l' a été trouvée 2 fois.
Sur la ligne 17, la chaine 'l' a été trouvée 1 fois.'
c:\progs>
    

Si le code de retour n'avait pas été positionné a "vrai", le message suivant aurait été imprimé.

# chchain.pl did not return a true value, <> chunk 17.
File 'Home:Christian:Perl:MacPerl:CAT:req'; Line 3
  

Mais tout se serait normalement déroulé.

Il est ainsi possible dans un programme de faire référence é la liste de fichiers externes dont il a besoin pour son exécution.

push (@INC,"c:\progs\perl);
@requis = ("p1.pl", "p2.pl","p3.pl", "p4.pl");
foreach (@requis) {
    require ($_);
}
  

La construction dynamique de programmes.

L'utilisation simultanée de AUTOLOAD et de require peut permettre de gérer dynamiquement des parties de code.
Mettons en place les données du programme.
Dans un premier temps, déclarons un repertoire "prpl" dans lequel nous allons stocker deux fichiers.

Le premier est appelé code1.pl et contient.

print ("Code 1\n");
print ("Parametres transmis\n");
print ("@_\n");
"retour de code 1";
  

Le second est appelé code2.pl et contient.

print ("Code 2\n");
print ("Parametres transmis\n");
print ("@_\n");
"retour de code 2";
  

Ecrivons maintenant le programme.

Programme fs5.pl Exécution sur l'écran
#!/usr/bin/perl;
AUTOLOAD {
  $AUTOLOAD =~ s/main:://;
  require ($AUTOLOAD.".pl");
}
unshift (@INC, "C:/prpl");
print ("Quel code execute-t-on 1 ou 2?\n");
chomp($n = );
if ($n==1) {
  print ("On appelle le code 1.\n\n");
  $ret = &code1 (1 .. 9)
} else {
  print ("On appelle le code.\n\n");
  $ret = &code2 (a .. j)
}
print ("Message : $ret\n");
    
c:\progs> perl fs5.plent
Quel code execute-t-on 1 ou 2?
1ent
On appelle le code 1.

Code 1
Parametres transmis
1 2 3 4 5 6 7 8 9
Message : retour de code 1

c:\progs> perl fs5.plent
Quel code execute-t-on 1 ou 2?
2ent
On appelle le code 2.

Code 2
Parametres transmis
a b c d e f g h i j
Message : retour de code 2

c:\progs>
    

Attention, la fonction require va chercher du code é exécuter. Le code considéré ne doit donc pas être représentatif d'un sous programme, bien que, afin d'activer le bloc AUTOLOAD, l'appel soit un appel de sous programme...

Comment spécifier la version requise de Perl.

Si on désire spécifier une version bien précise de perl pour exécuter un programme donné, il est possible d'utiliser la fonction require sous la forme.

require numero_de_version;
  

Si le numéro de la version est supérieur ou égal au numéro spécifié, le programme se poursuit.
Si le numéro de la version est inférieur au numéro spécifié, le programme s'interromp et un message d'erreur est imprimé.

Programme fs6.pl Exécution sur l'écran
#!/usr/bin/perl
require 5;
print ("La version est la bonne.");
    
c:\progs> perl fs6.plent
La version est la bonne
c:\progs>
    
Programme fs7.pl Exécution sur l'écran
#!/usr/bin/perl
require 6;
print ("La version est la bonne.");
    
c:\progs> perl fs7.plent
Perl v6.0.0 required (did you mean v6.000?)
--this is only v5.8.0, stoped at fs7.pl line 2.
c:\progs>
    

Un exemple de récursivité en Perl.

Perl n'est pas, à priori, destiné à gérer des fonctions récursives. Toutefois, il est possible si le besoin s'en fait sentir de mettre en place du code récursif. Une bonne application pour ce type de programme serait l'analyse d'une expression en notation polonaise préfixée. Rappelons que l'expression polonaise préfixée.

- 98 * 4 + 12 11;
  

Equivaut à l'expression parenthésée.

(98 - (4 * (12 + 11)))
  
Programme fs8.pl Exécution sur l'écran
#!/usr/bin/perl;
sub calcul_droit {
  my ($index) = @_;
  my ($resultat, $o1, $o2);
  if ($index + 3 == @liste) {
    $o2 = $liste[$index + 2];
  } else {
    $o2 = &calcul_droit ($index + 2);
  }
  $o1 = $liste[$index + 1];
  if ($liste[$index] eq "+") {
    $resultat = $o1 + $o2;
  } elsif ($liste[$index] eq "*") {
    $resultat = $o1 * $o2;
  } elsif ($liste[$index] eq "-") {
    $resultat = $o1 - $o2;
  } else {
    $resultat = $o1 / $o2;
  }
}
$ligne = ;
$ligne =~ s/^\s+|\s+$//g;
@liste = split (/\s+/, $ligne);
$resultat = &calcul_droit (0);
print ("Resultat du calcul : $resultat.\n");
    
c:\progs> perl fs8.plent
- 98 * 4 + 12 11ent
Resultat du calcul : 6
c:\progs>
    

Les aliases.

Ainsi que nous venons de le voir, le passage d'arguments dans la procédure se fait par l'intermédiaire de la liste prédéfinie @_

&Ma_Procedure (@La_liste_effective);
. . . . .
sub Ma_Procedure {
  my (@Liste_locale) = @_;
. . . . .
}
  

Un probléme risque de se poser si la liste é transmettre est trés grande. Il sera long et couteux d'en créer une copie.
Dans ce cas, la liste peut être passée en donnant la référence de son nom.

@Liste_effective = (1 .. 1000000)
&Ma_Procedure (*Liste_effective);
      . . . . .
sub Ma_Procedure {
  my (*Liste_locale) = @_;
  . . . .
  $longueur = @Liste_locale;
}
  

Le fait de spécifier

*Liste_effective
  

En lieu et place de

@Liste_effective
  

Permet d'indiquer que le contenu actuel de

@Liste_effective
  

Doit être utilisé et si besoin modifié dans la procédure

Ma_Procedure
  

De fait, pendant l'exécution de la procédure, le nom

@Liste_locale
  

S'identifie au nom

@Liste_effective
  

C'est cette création d'un nouveau nom pour référencer un élément déjà existant qui s'appelle l'aliasing.

@Liste_effective
  

Devient maintenant un alias de

@Liste_effective
  

A la fin de l'exécution de la procédure

@Liste_locale
  

Cesse d'être un alias de

@Liste_effective
  

Mais deviendra l'alias d'un nouvel élément lors d'un appel ultérieur.

Ceci n'est pas sans danger comme nous allons le montrer sur l'exemple qui suit

Programme fs9.pl Exécution sur l'écran
#!/usr/bin/perl;
$v = 0;
@v = ("Voici","une","longue","liste");
&ma_procedure (*v);
print ("Ici, la valeur de \$v est $v.\n");
sub ma_procedure {
  local (*ligne) = @_;
  foreach $element (@ligne) {
    print ("$element ");
  }
  print ("\n");
  $ligne = 1000;
}
    
c:\progs> perl fs9.plent
Voici une longue liste

Ici, la valeur de $variable est 1000.
c:\progs>
    

Dans l'exemple qui précède, on a déclaré le scalaire $v et la liste @v. Chose qui, nous l'avons précisé ne pose aucun probléme.
La liste @v est passée par un alias *ligne à la procédure appelée.
Ainsi donc, @ligne devient identique é @variable par le biais de l'aliasing.
Le probléme est que l'alias affecte TOUTES les variables du même nom, quel que soit leur type. Ainsi, l'alias *ligne agit bien sur la liste @v lorsqu'on lui demande de se référer é une liste (@ligne), mais il réfère aussi le scalaire $v si, pour une raison quelconque, on lui demande de référer le scalaire $ligne.

Précédent
Suivant