Un cours pour débuter en Perl


previous up contents next

Sous-sections


11. Les fonctions

11.1 Définition d'une fonction

Perl permet d'écrire des fonctions qui admettent ou pas des arguments et qui rendent ou pas une valeur de retour. Elles sont définies par le mot-clef sub suivi d'un identificateur et d'un bloc qui va contenir le code de la fonction.

  

sub Ma_Fonction {
   instruction 1;
   instruction 2;
}
  

Les fonctions peuvent être placées n'importe où dans un source Perl (elles sont sautées à l'exécution), il est toutefois conseillé de les placer en début ou en fin de programme. La portée des fonctions est globale, il est possible d'enliser une fonction dans une autre mais celà ne restreint pas sa visibilité, cette forme d'écriture n'a donc pas de sens.

  

sub Ma_Fonction {
   instruction 1;
   instruction 2;
sub Fonction_Enlisee { ... }  <==> Fonction_Enlisee est
    tout de même accessible en dehors de Ma_Fonction
}
  

Si une fonction retourne une valeur elle est précisée par l'instruction return suivi d'une expression à évaluer. Les arguments d'une fonction sont passés par valeurs et récupérés dans le tableau @_ (arobas souligné), en fonction du cas posé diverses solutions sont possibles pour y avoir accès :

  

my ($var1,$var2,$var3) = @_;
# ou
my $var1 = $_[0]; 
my $var2 = $_[1]; 
my $var3 = $_[2];
# ou encore 
foreach (@_) { .... }
  

Dans les faits une fonction retourne toujours une valeur, c'est celle de la dernière expression évaluée avant la sortie. On peut donc se passer de l'instruction return mais sa présence ne nuit pas à lisibilité du code !

11.2 Appel d'une fonction

On appelle une fonction en indiquant son identificateur suivi entre paranthèses des arguments (éventuellement 0). L'appel à une fonction peut être placé dans une expression en fonction de sa valeur de retour :

  

#!/usr/local/bin/perl
$I = 10;
$J = 20;
resultat();  <==> appel d'une fonction qui ne retourne pas
                  de valeur (ou plus exactement dont la 
                  valeur de retour est ignorée ici)
sub resultat {
 printf ("La somme est : %d \n",som($I,$J));
}
sub som {
 my ($a,$b) = @_;
 return $a + $b;
}
  

11.3 Prototyper les fonctions

Perl permet de vérifier (à la compilation) que les arguments passés à une fonction correspondent bien à ce qui est prévu par le programmeur. On utilise pour celà le prototypage des fonctions utilisées (qui doivent être visibles à la compilation). Le prototypage consiste à indiquer le nombre d'arguments attendus en précisant leur type. En Perl le prototype suit la déclaration du nom de la fonction (entre parenthèses) et utilise les conventions suivantes :

  • un $ indique la présence d'un scalaire;
  • un @ indique la présence d'une liste;
  • un % indique la présence d'un tableau associatif;
  • un ; sépare les arguments obligatoires des arguments optionnels;
  • lorsque le caractère précéde un @ ou un % il rend obligatoire que l'argument correspondant soit effectivement une liste ou un tableau associatif (cf. l'exemple suivant).

Il faut faire attention à l'utilisation des @ et % dans les prototypes car ils mangent les caractères suivants et forcent un contexte de liste. L'utilisation du devant ces caractères est donc très importante (cf l'exemple suivant).
  

#!/usr/bin/perl
# prototypes des fonctions utilisées
sub fonc1($$$);  # 3 scalaires
sub fonc2($$@);  # 2 scalaires et une liste
sub fonc3($$\@); # 2 scalaires et une variable de type liste
@liste = (1, 2, 3);
print (fonc1(@liste,5,6)," ",fonc2(5,6,7), fonc3(5,6,7));
#
# ici l'appel de fonc1 rendra 14 car @liste dans un contexte
# scalaire vaut 3 (nombre d'éléments). L'appel de fonc2 
# rendra 18 car 7 est pris comme une liste de 1 élément. 
# En l'état ce programme ne compile pas car l'appel à fonc3
# est incorrect, le 3ème argument n'est pas une variable de
# type liste.
#
sub fonc1($$$) {
###############
   my ($a, $b, $c) = @_;
   return($a+$b+$c);
}
sub fonc2($$@) {
###############
   my ($a, $b, @liste) = @_;
   my $res;
   foreach (@liste) {
     $res += $_;
   }
   return($a+$b+$res);
 }
sub fonc3($$\@) {
###############
   my ($a, $b, @liste) = @_;
   my $res;
   foreach (@liste) {
     $res += $_;
   }
   return($a+$b+$res);
 }
  

11.4 Passer des arguments par références

Les arguments d'une fonction sont récupérés dans le tableau @_ , ce mécanisme est suffisant pour passer quelques valeurs scalaires, mais devient très difficile à gérer s'il est nécessaire de passer plusieurs tableaux en paramètres (comment les délimiter entre eux ?). Pour résoudre ce problème, Perl propose d'utiliser des références plutôt que des valeurs.


11.4.1 Les références de variables

On référence une variable Perl en précédant son identificateur d'un
  

$scalaire = 4;
@tableau = (1,2,3,4,5,6);
$refscal = \$scalaire; 
$reftab  = \@tableau;
$refhac  = \%hache;
  

Une reférence est un variable de type scalaire, on peut donc corriger ce qui a été indiqué en section 2.1, un scalaire est un nombre, une chaîne de caractère ou une référence.

Pour déréférencer une variable contenant une référence, on précéde son identificateur d'un $,@ ou % en fonction du type de données qu'elle référence.

  

$scalaire = 4;
@tableau = (1,2,3,4,5,6);
%hache = ("a",1,"b",2,"c",3);
$refscal = \$scalaire;
$reftab  = \@tableau;
$refhac  = \%hache;
$$refscal = 5;            <==> $scalaire = 5;
@$reftab = (7,8,9)        <==> @tableau = (7,8,9);
$$reftab[0] = 2;          <==> $tableau[0] = 2;
%$refhac = ("d",4,"e",5,"f",6); 
           <==> %hache = ("d",4,"e",5,"f",6); 
  

Pour déréférencer une variable contenant une référence sur un tableau ou un tableau associatif, on dispose d'une notation fléchée qui est plus confortable à utiliser que la notation utilisant le $$ :

  

@tableau = (1,2,3,4,5,6,7);
%hache = ('bleu',1,'rouge',2,'vert',3);
$reftab = \@tableau;
$refhache = \%hache;
$reftab->[1] = 10; <==> $$reftab[1] = 10;
$refhache->{'rouge'} = 4; <==> $$refhache{'rouge'} = 4;
  

Il est également possible de créer des références des tableaux anonymes ou des hashes anonymes. Cette forme d'écriture est utilisée pour manipuler des structures de données complexes (tableaux de tableaux, tableaux de hashes ... cf. section 14).

Une référence sur un tableau anonyme se crée en utilisant des crochets ([]) :

  

@tableau = (1,2,3,4);
$reftab = \@tableau;
#  peut aussi s'écrire
$reftab = [1,2,3,4]; 
#  l'accès aux éléments est inchangé
$reftab->[1] ou $$reftab[1]
  

Une référence sur un hash anonyme se crée en utilisant des accolades {} :

  

%hache = ('bleu',1,'rouge',2,'vert',3);
$refhache = \%hache;
#  peut aussi s'écrire
$refhache={'bleu',1,'rouge',2','vert',3};
# l'accès aux éléments est inchangé
$refhache->{'rouge'} = 4;
  

Les références anonymes en Perl sont très pratiques à utiliser car le programmeur (contrairement au langage C par exemple) n'a pas à se soucier de la gestion de la mémoire (taille demandée et libération). Perl gère un compteur des références à chaque valeurs, qu'elles soient référencées directement ou pas, celles qui ne sont plus référencées sont détruites automatiquement.

11.4.2 Un exemple

Dans l'exemple suivant on désire réaliser une fonction recevant deux listes en arguments (@tab1 et @tab2), elle va rendre une liste résultante contenant la première liste triée suivie de la seconde également triée. On utilise ici un passage des arguments par références car on utilise deux tableaux, par valeurs la variable @_ ne pourrait pas nous renseigner sur la limite du tableau @tab1. Par références on peut passer un nombre très important de tableaux car les références sont des scalaires.

  

#!/usr/bin/perl
@tab1 = (7,3,2,8,9,1,2);
@tab2 = (3,2,1,4);
# Appel de la fonction tri avec deux arguments
# qui sont des références sur les tableaux.
@ltri = tri(\@tab1, \@tab2);
# Affichage du résultat.
print (@ltri);
# Fonction tri 
sub tri {
#########
   #  Récupération des arguments dans 2 variables
   #  locales qui contiennent des références sur
   #  les tableaux @tab1 et @tab2.
   my ($reftab1, $reftab2) = @_; 
   my (@tri1, @tri2);
   #  Tri de chaque tableau, on déréférence pour
   #  pouvoir appeler la fonction sort().
   @tri1=sort(@$reftab1);
   @tri2=sort(@$reftab2);
   #  Retour du résultat
   push(@tri1,@tri2);
   return @tri1;  # retour de @tri1 (par valeur)
}
  

11.5 Exercice numéro 8

  

#!/usr/bin/perl
# TP8
# On reprend ici le TP3 mais à la fin de la saisie on appelle un 
# fonction qui va calculer la valeur du stock. 
# Elle reçoit en paramètre le hash du stock, un hash donnant le 
# prix H.T. d'une bouteille de xxxx et le taux de TVA.
sub ValStock($$$);
%Stock=();
%Prix = ( 'Bordeaux'  => 25,
	  'Bourgogne' => 40,
	  'Bourgueil' => 33,
	  'Bandol'    => 29);
$Tva=18.6;
print ("Nom du vin : \n");
while ($Vin=<STDIN>) {   # $Vin sera la clef du Hash de stock
    chomp($Vin);         # enlève le caractère de fin de ligne
    print ("Quantité : ");
    $Quant=<STDIN>;      # Il faudrait vérifier qu'il s'agit bien
                         # bien d'un nombre entier relatif.
    chomp($Quant);
    unless ($Quant) {    # Si l'utilisateur ne saisit pas de Quantité
	last;            # on passe à l'affichage du stock.
    }
    $Stock{$Vin} += $Quant; 
    print ("Nom du vin : \n");
}
# On appelle la fonction ValStock en lui passant des références
# sur les hashs (sinon le premier mangerait le second).
print ("Valeur totale : ",ValStock(\%Stock, \%Prix, $Tva),"\n");
sub ValStock($$$) {
###################
    my ($StockPtr, $PrixPtr, $Tva) = @_;
    my ($Vin, $Quant, $Total);
    while (($Vin,$Quant) = each (%$StockPtr)) { # %$ pour déréférencer
	$Total +=  ($Quant * $PrixPtr->{$Vin});
    }
    return($Total + ($Total*$Tva/100) );
}
  


previous up contents next

FD