La création d'une fonction en Perl est on ne peut plus simple. elle se fait au moyen du mot-clé « sub » suivi d'un bloc.
#!/usr/bin/perl -w
sub simplesub {
print "Bonjour\n";
print "Une sub simple sans parametres\n";
}
simplesub();
&simplesub;
#Avec '&' , on est certain d'invoquer une sub et les parenthèses sont souvent facultatives , mais permettent de mieux délimiter les paramètres quand il y en a.
&simplesub();
Les paramètres d'une fonction peuvent être illimités. La raison à cela est que la liste des paramètres est tout simplement une liste plate : le tableau @_. (cf. variables réservées)
Chaque paramètre peut alors être extrait à l'intérieur de la sub comme on le fait pour toute liste:
En laissant le paramètre en place.
En le supprimant avec la fonction perl shift. shift extrait et supprime le premier paramètre d'un tableau en décalant tous les autres vers la gauche. Par défaut si on ne donne pas de nom de tableau à shift, shift décalera les paramètres de @_.
exemple avec shift:
#!/usr/bin/perl -w
sub dire {
my $mot=shift;
print "$mot\n";
}
dire('bonjour');
exercice: selon ce principe : faire une fonction à 5 paramètres.
il arrive que l'on ne souhaite pas supprimer le paramètre du tableau @_ souvent parce qu'on souhaite y accéder ultérieurement par exemple. dans ce cas, il ne faut pas utiliser shift.
Et l'extraction peut alors se faire ainsi.
sub redire {
my $mot = $_[0];
print "$mot\n";
}
#ou encore directement sans recopie #dans une autre variable
sub redire2 {
print "$_[0]\n";
}
redire('bonjour');
redire2('ça va?');
dans le cas de plusieurs paramètres on préférera l'affectation de plusieurs scalaires à partir de @_ sous forme de liste:
#!/usr/bin/perl -w
sub fenetre {
my ($x,$y,$width,$hight,$color) = @_;
foreach my $item qw(x y width hight color) {
print "$item = " . eval('$' . $item) . "\n";
}
# ...;
}
fenetre(2,5,35,48,'rouge');
Notez qu'il s'agit encore d'une copie de chaque argument dans des variables distinctes et que @_ n'est pas modifié.
Toute sub perl est globale et sera donc visible de partout dans votre code.
Cela signifie que vous pouvez placer son appel avant ou après la définition de la fonction.
Cette règle se limite à votre programme. L' import de package ou l'utilisation des classes que nous allons voir par la suite obéit à d'autres règles.
Une sub Perl peut renvoyer des données au moyen de l'instruction return;
mais elle n'est pas obligatoire puisque la dernière expression valide de la sub est automatiquement renvoyée.
Une sub Perl peut renvoyer toutes les structures Perl existantes c'est à dire des scalaires, des listes et des hashs, mais pour des objets complexes, il est nettement préférable d'utiliser les références.
exemple open_read_close:
#!/usr/bin/perl -w
sub open_read_close {
my $file = shift;
unless (open (FILE,$file)){
warn "Ne peut ouvrir $file : $!" ;
return 0;
}
while (<FILE>)
{
print;
}
close(FILE);
return 1;
}
if (open_read_close('/home/moi/txt/notes.txt')){
print "OK : le fichier est lu!\n";
}
else {
print "L'ouverture a échoué\n";
}
return n'est pas obligatoire comme dernière instruction, exemples :
#!/usr/bin/perl -w
sub testnum {
return 1 if $_[0] =~ /^[0-9]+$/;
#En dernière instruction return n'est pas obligatoire.
0;
}
sub anti { 'anticonstitutionnellement';}
sub douze { 12; }
##############ROUTINES##############
print testnum('toto') . "\n";
print testnum(32) . "\n";
print douze() . "\n";
print &anti . "\n";
Perl peut créer des fonctions qui s'adaptent aux contexte.
Autrement dit si l'appelant veut une liste : je lui renvoie une liste s'il veut un scalaire : je lui retourne un scalaire. C'est déjà le cas de nombreuses fonction natives de Perl comme grep:
my @newliste=grep(/motif/,@liste);
Dans ce contexte on demande à grep de renvoyer une liste et @newliste contiendra tous les éléments de @liste qui contiennent /motif/.
Mais que se passe t-il si j'écris:
my $chose = grep(/motif/,@liste);
A ce moment grep sait qu'il ne peut ni ne doit renvoyer une liste mais son prototype (sa définition interne) lui dit qu'il doit renvoyer le nombre d'éléments qui contiennent le motif.
$chose contiendra alors un nombre qui peut être contenu par un scalaire.
La fonction wantarray appliquée juste avant la sortie d'une fonction permet de créer une fonction locale ayant le même comportement et s'adapter au contexte; ainsi la fonction grep pourrait s'écrire ainsi:
#!/usr/bin/perl -w
use strict;
sub mongrep {
my $motif=shift;
my @out;
foreach (@_) {
push(@out,$_) if /$motif/;
}
return (wantarray() ? (@out) : scalar(@out) );
}
my @set = mongrep(qr/^[EAZ]TERE_CODE/,
qw(ATERE_CODE ZTERE_CODE UTERE_CODE));
foreach my $one (@set){
print "$one\n";
}
my $nb_set = mongrep(qr/^[EAZ]TERE_CODE/,
qw(ATERE_CODE ZTERE_CODE UTERE_CODE));
print "$nb_set\n";
Autre exemple : passer une liste en majuscules
#!/usr/bin/perl -w
my $v1 = 'tutoriel';
my $v2 = 'animal';
my ($v3, $v4) = upcase($v1, $v2); # cela ne change pas $v1 et $v2
print "$v3\n";
print "$v4\n";
sub upcase {
return unless defined wantarray; # contexte vide, ne fait rien
my @parms = @_;
foreach (@parms) { tr/a-z/A-Z/ }
return wantarray ? @parms : $parms[0];
}
Il est possible de référencer les arguments. Cela permet de les manipuler à distance.
Il n'y a d'ailleurs pas vraiment d'autres solutions pour les objets structurés avec des références imbriquées.
Tout d'abord, sachez que la modification de @_ agira directement sur les éléments transmis, à condition bien sur qu'il ne s'agit pas de constantes littérales:
my $n=10;
ajouter($n,3);
print
"$n\n";
#affiche
10;
ajouter(5,2); #erreur
car 5 est une constante littérale qui ne peut être
modifiée.
#####################
sub ajouter {
$_[0]
+= $_[1];
}
cette propriété est pratique si l'on souhaite, par exemple modifier une liste complète:
#!/usr/bin/perl -w
use strict;
my @liste_a_nettoyer;
@liste_a_nettoyer=remplir('ls -1tr'); #rempli la liste
#nettoie la liste
nettoyer_liste(@liste_a_nettoyer);
#et la liste est propre!
print_liste(@liste_a_nettoyer);
sub nettoyer_liste {
foreach (@_){
s,^\s*,,;
s,\s*$,,;
s,PUB,,;#enlève PUB
}
#supprime les répertoires courants '.' et précédents '..'
@_ = grep(!/^\./,@_);
}
sub remplir {
my $cmd=shift;
@_=`$cmd`;
}
sub print_liste {
foreach (@_){
print "$_\n";
}
}
En effet toute action sur la ligne courante $_ va modifier l'élément du tableau courant,en occurrence @_ et puisque celui-ci est directement lié à @liste_a_nettoyer, @liste_a_nettoyer sera modifié aussi.
Si l'on souhaite pouvoir modifier des valeurs issues de constantes littérales, il suffit de copier les arguments de @_ dans de nouvelles variables, et l'on devra dans ce cas retourner un résultat puisque les constantes ne sont pas modifiables.
sub ajouter {
my ($a,$b) =@_;
$a +=
$b;
return $a;
}
Ceci est pratique pour les listes plates et les scalaires, mais à très vite ses limites lorsqu'il s'agit de hashs ou d'objets complexes référencés. De plus si vous avez plusieurs objets, (liste ou hash) rien ne les différenciera puisque la liste plate des arguments @_, ne peut qu'être unique.
Dans ce cas la seule solution est de passer des référencer les objets, vous ne transmettez plus une liste, mais un seul scalaire : une référence.
Voici une façon de le faire:
exemple avec 2 listes
#!/usr/bin/perl -w
use strict;
my (@lst,@autre_liste);
traitement(\@lst,\@autre_liste);
print_liste(\@lst);
print_liste(\@autre_liste);
################# ROUTINES ####################
sub traitement {
#on récupère les 2 références
#notez que ce n'est pas obligatoire : on peut agir directement
#sur $_[0] ou $_[1], cela rend toutefois l'écriture beaucoup plus simple
my ($a,$b)=@_;
#insère 10 items dans chaque liste
for my $i (0 .. 10){
push(@{$a},'item (lst) ' . $i);
#notez qu'il faut dé-référencer
push(@{$b},'item (autre liste) ' . $i)
}
}
sub print_liste {
foreach (@{$_[0]}){
print "$_\n";
}
}
exemple avec une liste et un hash
#!/usr/bin/perl -w
use strict;
my (@lst,%mon_hash);
traitement(\@lst,\%mon_hash);
print_liste(\@lst);
print "--------\n";
print_hash(\%mon_hash);
################## ROUTINES ###################
sub traitement {
#on récupère les 2 références
#notez que ce n'est pas obligatoire : on peut agir directement
#sur $_[0] ou $_[1], cela rend toutefois l'écriture beaucoup plus simple
my ($a,$b)=@_;
#insère 10 items dans chaque liste
for my $i (0 .. 10){
#notez qu'il faut dé-référencer
push(@{$a},'item (lst) ' . $i);
}
%$b = (
Nom => 'DUPONT',
Prenom => 'Julien',
Age => 27,
Sexe => 'Homme'
);
}
sub print_liste {
foreach (@{$_[0]}){
print "$_\n";
}
}
sub print_hash {
foreach my $k (keys %{$_[0]}){
print "$k => $_[0]->{$k}\n";
}
}
Il peut être pratique de référencer les fonctions. Ceci permet des les lier à des scalaires, voir à d'autres objets complexes. On référence les fonctions afin de pouvoir différer facilement des actions, et les insérer, dans des objets plus ou moins complexes, voyons ceci en pratique.
Cet exemple démontre et explique comment créer une référence sur fonction et comment la lancer.
#!/usr/bin/perl -w
use strict;
sub masub {
my $param = shift;
print "sub : masub ";
print "param : $param\n";
}
##################################################
#déclare la référence
my $s=\&{'masub'};
#méthodiquement :
#1 : Ecrire le nom de la sub sous forme de chaîne 'masub'
#2 : la placer entre accolades {'masub'}
#3 : placer & devant pour indiquer qu'il s'agit d'une sub perl : &{'masub'};
#4 : référencer le tout avec l'antislash : \&{'masub'} : vous y êtes.
#5 : affecter le tout à une variable
#APPELS : plusieurs formes possibles
#premier type d'appel : on spécifie qu'un s'agit d'une sub au moyen de '&';
&{$s}('un');
#puisque $s est une expression simple : peut se simplifier ainsi, :
&$s('un');
#deuxième type d'appel : on utilise '->' pour dé-référencer, (idem en C et C++);
$s->('un');
#Notez que à bien y regarder, le mécanisme de référencement/dé-référencement des subs,
#est le même que pour les listes et les hashs
cela peut se faire ainsi :
my $masub = sub {
...
...
}
pour l'appel, il y a deux solutions : il faut un moyen de préciser que la référence est une sub.
En plaçant le & devant
Ou bien en ajoutant les parenthèses et le symbole de dé-référence
&$masub;
ici on peut ajouter les parenthèses, si l'on a des paramètres.
$masub->();
et on peut si besoin, passer des paramètres dans les parenthèses. On les récupère exactement de la même façon avec shift ou @_, dans la sub anonyme.
Puisqu'une référence de sub peut être contenue par n'importe quel scalaire, elle peut au même titre être l'élément d'une liste ou d'un hash.
Alors ce type de construction par exemple, est tout a fait possible :
#!/usr/bin/perl -w
my %calcul;
%calcul= (
'sum' => sub {($_[0] + $_[1])},
'mult' => sub {($_[0] * $_[1])},
'soust' => sub { ($_[0] - $_[1])}
);
my $file='actions.txt';
open(FILE,$file) or die "Ne peut ouvrir $file : $!";
while (<FILE>){
chomp;
my ($action,@values) = split(/\s+/);
print "l'action est $action, ";
print 'valeurs : ' . join(' et ' ,@values) ;
my $result = $calcul{$action}->(@values);
print ', le résultat est : ' . "$result\n";
}
exercice:
Pour l'addition et la multiplication : faire la même chose avec un nombre de valeurs illimitées.
A l'origine perl utilise l'instruction require pour importer un autre fichier. Alors, toute variable globale et sub du fichier importée est accessible au fichier principal.
Exemple de fichier importé :
#fichier contenant des routines : Outils.pm
our $ma_var='Une variable';
sub monter {
println("Je monte.");
}
sub demonter {
println( "Je démonte.");
}
sub faire {
println( "Je fait.");
}
sub println {
print $_[0] . "\n";
}
Programme qui importe et utilise ce fichier:
#!/usr/bin/perl -w
#fichier programme principal : main.pl
use strict;
require 'Outils.pm';
monter();
demonter();
faire();
#désactive le use strict pour les variables
no strict 'vars';
print "$ma_var\n";
Il arrive cependant que l'on souhaite isoler les objets et subs de notre fichier afin d'y accéder de manière groupée. Dans ce cas, il est préférable de créer un package au moyen de l'instruction du même nom, en première ligne du fichier importé. Ceci se fait ainsi:
package Poutil;
Ainsi puisque tous les symboles seront déclarés dans un package, ils ne feront plus partie du package courant: sachez que toute variable appartient à un package et que les variables de votre script sont dans le package main et accessibles aussi de la sorte :
print main::$var . "\n"; => print $var . "\n";
&main::masub(); => masub();
Voici donc l'utilisation d'un package non orienté objet:
package Poutils;
our $ma_var='Une variable';
sub monter {
println("Je monte.");
}
sub demonter {
println( "Je démonte.");
}
sub faire {
println( "Je fait.");
}
sub println {
print $_[0] . "\n";
}
1;
Note : En principe tout package doit retourner 1 , il faut pour cela écrire 1; en fin du package.
Et voici ce que devient le script principal:
#!/usr/bin/perl -w
#fichier programme principal : main_poutils.pl
use strict;
require 'Poutils.pm';
Poutils::monter();
Poutils::demonter();
Poutils::faire();
my $nouvelle = "test";
print "$Poutils::ma_var\n";
#les variables du programme principal sont dans le package main
print "$main::nouvelle\n";
Ainsi les subs , les variables, les hashs, et listes du package Poutils.pm ne sont plus accessibles directement, puisqu'elles se trouvent être dans le package Poutils. Il existe un moyen de les rendre accessible dans le package courant au moyen des typeglobs.
En effet si je veux que la sub monter soit accessible dans main il me suffit d'écrire ceci:
*main::monter = *Poutils::monter;
Ainsi monter pourra être appelé directement:
&monter();
puisqu'il sera dans le package main.
C'est ce que l'on appelle l'import (de symboles) mais vu coté package on l'appelle export et ceci permet de mieux contrôler ce que l'on souhaite rendre accessible directement ou pas en effet, on ne souhaite pas forcément rendre tous les objets du package accessibles, par exemple, la sub println n'est pas forcément nécessaire à l'extérieur du package puisqu'elle sert au subs internes, pour afficher.
Avec l'évolution de Perl, est venue l'instruction use qui permet de mieux gérer l'import/export de symboles.
En réalité l'instruction
use Pkg;
fait tout ceci de manière plus compacte :
BEGIN{
require 'Pkg.pm';
Pkg->import() if Pkg->can('import');
}
BEGIN est un bloc qui s'exécutera en tout premier dans votre code (ou qu'il soit placé).
Avec use, import importera les symboles qui seront exportés coté package. Coté package pour exporter des symboles , il faut faire 2 choses :
Faire hériter le package de Exporter (une classe qui exporte) afin que le package soit lui-même capable d'exporter. Le package Exporter joue avec la table des symboles comme dans l'exemple précédent, mais ceci est plus sûr et plus compact.
Insérer les symboles à exporter dans la liste spéciale (cf variables réservées de Perl) @EXPORT.
Voiçi la façon de le faire:
package Pkg;
use Exporter; #importe la
package Exporter
@ISA = qw(Exporter); #fait hériter le
package Pkg de Exporter : lui permet d'exporter
@EXPORT = qw(&sub1
&sub2 &sub3 $mavar); #exporte les symboles
Perl recherchera par défaut votre package dans le répertoire courant et dans les répertoires contenus dans @INC (cf variables réservées de Perl).
Vous pouvez décider de mettre tous vos paquets personnels dans un chemin dédié, et dans ce cas vous devez modifier cet environnement afin qu'il sage ou les trouver.
Ceci peut être fait de différentes 4 manières :
Dans votre environnement extérieur:
export PERL5LIB=''$PERL5LIB:/home/user/perl/malib'';
sous Windows : utiliser set PERL5LIB=%PER5LIB%;c:\home\moi\perl\malib
dans votre script écrire au début use lib '/home/moi/perl/malib';
dans le script écrire push(@INC,'/home/moi/perl/malib');
La première méthode est de loin la meilleure car les 2 autres vous obligerons à réécrire cette instruction à chaque fois dans votre script.
En supposant que vous ayez un package dont le chemin est celui-ci.
/home/moi/perl/malib/PerlIO/Via/Vcsa.pm
et si vous avez modifié @INC comme précisé au point précédent, alors vous devrez importer votre paquet comme suit:
use PerlIO::Via::Vcsa;
Cette notation n'a aucun effet sur l'objet , il faut juste la considérer comme le remplacement du séparateur de répertoire pour localiser votre module à partir d'un des chemins de base de @INC.
Un module non orienté objet qui exporte des symboles peut donc se faire ainsi :
package Uoutils;
use Exporter; #Importe l'Exporter
@ISA = qw(Exporter);
@EXPORT=qw(&monter &demonter &faire $ma_var);
our $ma_var='Une variable';
sub monter {
println("Je monte.");
}
sub demonter {
println( "Je démonte.");
}
sub faire {
println( "Je fait.");
}
sub println {
print $_[0] . "\n";
}
1;
Et voici la façon d'utiliser ce package:
#!/usr/bin/perl -w
#fichier programme principal : main_uoutils.pl
use Uoutils;
use strict;
monter();
demonter();
faire();
print "$ma_var\n";
Notez que Exporter permet aussi d'utiliser le tableau @EXPORT_OK, celui-ci fonctionne comme @EXPORT, mais il oblige coté du coté de l'import, à mentionner tout symbole après le use comme suit:
use Uoutils qw(monter demonter);
Ne pensez pas que @EXPORT_OK est moins utilisé que @EXPORT, cela devrait même être l'inverse car @EXPORT_OK oblige à mentionner l'import des symboles coté appelant avec use. C'est une sécurité supplémentaire et cela nous laisser la possibilité d'importer seulement ce dont nous avons besoin dans le package.
Il est aussi possible d'exporter des tags. Les tags sont des groupes de symboles contenus dans un hash et exportable par le hash %EXPORT_TAG.
Nous sommes partis de l'instruction require qui permet d'importer un simple fichier, pour arriver à l'instruction use qui permet d'utiliser les symboles d'un package.
Il convient alors autant que possible d'utiliser use pour l'utilisation des packages.
Vous savez à présent ce qu'est un package, comment fonctionne le mécanisme d'import et comment utiliser un package non orienté objet.
Nous allons voir à présent comment créer et utiliser un module simple Orienté Objet.
Il est possible au moyen du hash %EXPORT_TAGS, d'exporter un groupe de symboles.
Ceci peut se faire ainsi à l'intérieur du package :
our %EXPORT_TAGS = ( 'all' => [ qw(
JS_PROP_PRIVATE
JS_PROP_READONLY
JS_CLASS_NO_INSTANCE
) ] );
#Puis invoquer la méthode export_ok_tags
# ou export_tags du module Exporter pour la clé du tag :
Exporter::export_ok_tags('all');
L'appel se fait ainsi:
use Monpkg qw(all);
if
($JS_PROP_PRIVATE eq 'normal') {
}
La programmation Orientée Objet (POO) est apparue au milieux des années 70 et s'est étendue au début des années 80.
Avant cela il était possible de faire de l'objet et de produire une programmation structurée de la même manière, comme on le fait toujours en langage C. Malheureusement les langages non objets ont deux inconvénients pour cela:
Il sont très procéduraux
Il sont peut re-lisibles
Hors en programmation et d'autant plus que le projet est important et est produit en équipe, l'aspect présentation et communication est d'une importance capitale.
Ainsi les langages POO s'adaptent mieux à la modèlisation UML par exemple.
Il est possible de présenter des interfaces (des fonctionnalités) sans entrer dans le détail du code.
La différence entre la collection de routines ou langage orienté fonctions (non objet) et l'objet est la notion d'instance.
Autrement dit tout langage Orientée Objet crée des instances d'objets.
On parle alors de durée de vie et d'état transitoire d'un objet.
Dans la plupart des langages Orientés Objet, l'instance est une référence ou un pointeur statique, c'est à dire qu'il reste en mémoire et garde la même adresse pendant toute la durée de vie de l'objet jusqu'à sa destruction.
Toutes les fonctions sont alors greffées sur cette instance et on ne parle plus de fonctions mais on les nommes des méthodes, (simple question de langage pour ne pas dire des fonctions ).
Plus concrètement voici comment cela se présente en Perl.
#Importe le package Window, qui est développé sous forme de classe,
#nous allons voir comment par la suite.
use Window;
#pour créer une nouvelle instance, on fait souvent appel à la méthode new.
my $w1 = new Window(1,1,50,100,'blue');
#$w est à présent une instance de l'objet Window;
#Une autre forme d'appel à new.
my $w2 = Window->new(3,6,20,20,'red');
A présent : appel des méthodes
$w1->draw(); #dessine la fenêtre
$w2->move_right(10);#déplace la fenêtre $w2 de 10 unités vers la droite.
$w2->destroy();#détruit l'instance
$w1->destroy();#idem
#A présent, il n'est plus possible d'appeler des méthodes sur w1 ou w2.
En perl, une classe est un package avec un mécanisme, le plus souvent une méthode spéciale, le constructeur, qui permet de créer une instance. Pour des raisons de bon sens on nomme cette sub new ou create, par exemple, mais en Perl il n'y a aucune restriction.
En interne de la classe, l'instance n'est jamais qu'une référence anonyme, le plus souvent un hash, mais cela ne suffit pas : il faut que cette référence soit bénie au moyen de la fonction bless.
Voyons cela en pratique avec la classe Fenetre:
package Fenetre;
#Le constructeur new
sub new {
my $name = shift; #Le premier paramètre donne le nom du package
my $self = {}; #Référence sur un hash anonyme
#la fonction bless étant la dernière instruction de la sub new
#elle renvoie le premier paramètre
#$self bénie qui va constituer l'instance en externe.
bless($self, $name);
}
#Méthode init
sub init {
my $self = shift;
print "initialisation\n";
}
#Méthode draw
sub draw {
my $self = shift; #récupère l'instance en interne
$self->init(); #appel la sub init interne
print "Je dessine\n";
}
#Méthode set_color
sub set_color { #Un setteur
my ($self, $color)=@_;
$self->{color}=$color;
}
# ...
# Autres méthodes éventuelles
# ...
1;
Exercices:
Copier cette classe et l'utiliser : créer une instance et lancer toutes les méthodes sauf la méthode init.
Modifier la classe : Ajouter une méthode get_color qui renvoie le paramètre color. Tester cette méthode.
Ajouter une méthode affiche_params qui affiche tous les paramètres de la classe. Tester cette méthode.
Après avoir instancié la classe, Utiliser le module Data::Dumper et analyser ce que vous voyez.
Le constructeur doit parfois définir des valeurs par défaut, pour cela il faut quelque-peu modifier la définition du hash anonyme.
package Window;
sub new {
my $this = shift;
my $class = ref($this) || $this;
die "odd number of elements to new sub." if ( (scalar(@_) % 2 ) != 0);
my $self = {
color => 'white' , # Couleur par défaut
fill => 0 , # Si on doit remplir l'intérieur
char => ' ', # avec quel caractère
'hidden' => 1, # fenêtre cachée par défaut
@_
# Paramètres additionnels : ils peuvent écraser les
# valeurs par défaut initiales;
};
bless $self, $class;
return $self;
}
#Utilisation d'un setter
sub set_color {
my ($self,$color)=@_;
$self->{color}=$color;
}
#....
1;
L'appel au constructeur de notre classe pourra donc se faire ainsi:
use strict;
use Window;
my $w = new Window(color => 'blue' , offset => 18);
Dans ce cas, l'attribut color de la classe sera écrasé par 'blue' au lieu de sa valeur par défaut qui est
'white'.
Exercices:
Refaire les mêmes exercices que précédents pour cette classe.
Dites-vous qu'une fois votre référence de hash créée, TOUT ce que vous ajoutez doit se greffer dessus. Ainsi les attributs sont une simple clé supplémentaire de votre hash anonyme qui peut se faire ainsi;
use MaClass;
my $machin = new MaClass(flag => 'z' , offsets => [2, 5]);
#Ajout d'un nouvel attribut
$machin->{'color'} = 'red';
Il n'y a pas de limitte du nombre d'attributs que vous pouvez déclarer de la sorte, sauf ... la mémoire.
Mais il est nettement préférable d'utiliser des getters et setters comme dans l'exemple précédent. Notez quie si vous avez programmé en C++, Python, ou Java, vous serez tentés d'ajouter des variables à l'extérieur dans le module. Cela est une grosse erreur car dans tous les cas elles seront globales au niveau module, c'est à dire que toutes vos instances agirons sur la même variable, une fois l'import du module fait par use. Ce n'est probablement pas ce que vous voulez puisqu'un attribut comme tous les membres d'une classe ont une vie propre lié à chaque instance. En C++ ou Java cela reviendrai à faire une déclaration statique dans un constructeur par exemple:
static int i;
Certains packages comme le module CGI, permetent une double programmation à la fois orientée objet et orientée fonctions.l'utilisation se fait ainsi, orientée objet:
#!/usr/local/bin/perl -w
use CGI;
# chargement du module CGI
$q = new CGI;
# creation d'un nouvel objet CGI
print $q->header,
# creation de l'en-tete HTTP
$q->start_html('hello world'), # debut du HTML
$q->h1('hello world'),
# titre de niveau 1
$q->end_html;
# fin du HTML
et ainsi Orientée fonctions:
#!/usr/local/bin/perl
use CGI qw/:standard/;
# chargement des fonctions standard de CGI
print header,
# creation de l'en-tete HTTP
start_html('hello world'), # debut du HTML
h1('hello world'),
# titre de niveau 1
end_html;
# fin du HTML
Pour créer de telles méthodes, il faut vérifier si le premier paramètre de la méthode est une référence de classe et si cette référence donne le nom du package ou pas.Il suffit ensuite d'extraire ou pas cette référence. La fonction Perl ref permet de déréférencer directement un scalaire et de vérifier en même temps si ce scalaire est bien une référence.
Voiçi comment ceci peut être réalisé en ajoutant une méthode printmsg à un parametre à notre classe, qui pourra être appelée à la fois sous forme de méthode ou de fonction non objet.
package Fenetre;
use Exporter;
@ISA=qw(Exporter);
@EXPORT_OK = qw(&printmsg);
#Constructeur de fenêtre
sub new {
my $name = shift;
my $self = {};
#bless étant la dernière instruction de la sub new
#il renvoie le premier paramètre $self bénie qui constitue l'instance en externe.
bless($self, $name);
}
sub init {
my $self = shift;
print "initialisation\n";
}
sub draw {
my $self = shift; #récupère l'instance en interne
$self->init(); #appel la sub init interne
print "Je dessine\n";
}
sub set_color {
my ($self, $color)=@_;
$self->{color}=$color;
}
# sub printmsg orientée objet ET fonctions.
sub printmsg {
my ($name,$self);
# vérifie si le premier parametre est
# une référence du nom de la classe
if (($name=ref($_[0])) eq 'Fenetre'){
$self = shift;
}
my $msg = shift;
print "[$msg]\n";
}
# ...
# Autres méthodes éventuelles
# ...
1;
Exercice:
Localisez le module CGI.pm dans votre système et regardez comment ceci est réalisé.
Cette méthode entièrement en majuscules est une méthode magique qui se comporte comme si elle avait le nom de toutes les méthode non existantes du module . En effet si vous appelez la méthode ou la fonction chmol de votre module et que celle-ci n'existe pas, mais que votre module a une sub AUTOLOAD , la méthode AUTOLOAD sera appelée.
Il est possible de connaître le nom de la fausse méthode appelée au moyen de la variable scalaire du même nom : $AUTOLOAD.
Cette technique permet de faire des appels virtuels et calculés en fonction du contexte, très puissants vers de vraies fonctions ou méthodes en fonction du certaines conditions. On peut s'en servir par exemple pour intercepter l'appel d'une fonction afin d'aiguiller la la méthode d'un package A dans un cas et d'un package B dans l'autre.
L'inconvénient est que cela demande une relecture du code de la méthode AUTOLOAD assez poussée parfois.
Exemple:
Nous avons vu l'essentiel pour créer une classe Perl orientée objet et l'utiliser. Il serait inutile d'en dire plus pour l'instant. Si vous n'avez pas tout saisi, contentez-vous de créer des classes en appliquant cette méthode et dites-vous qu'une meilleure compréhension viendra avec le temps et la pratique.
Si certaines parties de la documentation Perl vous dépassent, passez les pour l'instant et attendez d'avoir un peu plus de recul pour y revenir.
Pour terminer, nous constatons que Perl est un langage très souple pour la programmation Orientée Objet et qu'il a l'avantage de ne pas s'imposer comme un langage tout objet : par exemple comme le langage Java. Il laisse au programmeur une liberté de conception et d'utilisation.
La documentation Perl décrit aussi l'Objet à sa manière dans ces fichiers.
Ces documents vous
aiderons à comprendre l'Objet en Perl. Voyez les premiers et
essayez les suivants, s'ils ne vous conviennent pas.perlboot
- Tutoriel pour
l'orienté objet à destination des débutants
perltoot
- Tutoriel
orienté objet de Tom.
perltooc
- Le
tutoriel de Tom pour les données de classe OO en Perl
perlbot
- Collection
de trucs et astuces pour Objets (the BOT)