IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Comment utiliser des décorateurs en Perl ?
Un tutoriel de Laurent Rosenfeld

Le , par Lolo78

0PARTAGES

12  0 
Bonsoir,

j'ai le plaisir d'annoncer la publication sur ce site d'un nouvel article: Comment utiliser des décorateurs en Perl - Un tutoriel pour changer le comportement d'une fonction sans en modifier le code source.

Un « décorateur » est une fonction qui permet de modifier le comportement d'une autre fonction sans toucher au code de cette autre fonction. Cela permet notamment d'ajouter des traces d'exécution (par exemple à des fins de débogage) ou d'accélérer le fonctionnement d'une fonction en ajoutant un cache.

Perl n'a pas de fonctionnalité spécifique pour utiliser des décorateurs, mais cet article montre qu'il est assez facile de créer cette fonctionnalité. Au passage, l'article illustre l'utilisation de certaines fonctions dites « avancées » de Perl, lesquelles sont en fait moins mystérieuses qu'il n'y paraît de prime abord.

Bonne lecture et bonne soirée à tous.

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de djibril
Responsable Perl et Outils https://www.developpez.com
Le 29/11/2017 à 20:59
Bonsoir,

Encore un très bon tutoriel sur Perl qui nous apprend de nouvelles techniques de programmation avancées.

2  0 
Avatar de dca_marshall
Nouveau membre du Club https://www.developpez.com
Le 30/11/2017 à 11:59
Bonjour,

Excellent 👍

Jusque la, j'utilisais un Wrapper pour se charger des compteurs d'appel, des code-retours et des compteurs internes à la fonction (avec PadWalker).

Je vais donc revoir ma copie...

Merci pour ce cours et sa modernité.
2  0 
Avatar de ptonnerre
Membre habitué https://www.developpez.com
Le 17/12/2017 à 10:33
La lecture du début de ce tutoriel, notamment les premiers exemples d'utilisation de cache, m'a fait réfléchir à un de mes scripts.
J'ai pu ainsi optimiser un traitement mensuel qui dure en moyenne 1h27 à 52 minutes.
Pas mal non !

Encore un grand merci à Laurent pour ses tutoriels
2  0 
Avatar de ptonnerre
Membre habitué https://www.developpez.com
Le 04/12/2017 à 11:13
Un nouveau tuto que je vais lire dès que possible.
1  0 
Avatar de ptonnerre
Membre habitué https://www.developpez.com
Le 19/01/2018 à 11:10
Bonjour,

j'essaye de mettre en oeuvre les décorateurs avec ce cas d'application simple : nos programmes écrivent dans un fichier de log qui sera poussé vers un serveur de logs. Les lignes du fichier log ont un format spécifique et les écritures sont gérées par des fonctions d'un module.

Les fonctions gérant les lignes de type INFO et WARN sont similaires, à l'exception du préfixe de la ligne (OXRES ou OXINC).

Version sans décorateurs :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sub oxa_msg_info {
    my ( $msg, $flag ) = @_;

    my $tms = ( $flag ) ? utils_timestamp() . ' - '  : '';
    say 'OXRES: ' . $tms . $msg;

    return;
}

sub oxa_msg_warn {
    my ( $msg, $flag ) = @_;

    my $tms = ( $flag ) ? utils_timestamp() . ' - '  : '';
    say "OXINC: " . $tms . "$msg";

    return;
}
Version avec décorateurs :

Cette version crée en début de module deux coderefs via l'appel de la fonction decorateur en lui passant en argument la référence à la fonction générique info_or_warn qui ne fait qu'une écriture et le préfixe associé.
Nous avons donc à disposition les deux fonctions oxa_msg_info et oxa_msg_warn utilisant la fonction générique info_or_warn redécorée en fonction du besoin .

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    no warnings 'redefine';
    *{xxx_utils::oxa_msg_info} = decorateur(\&info_or_warn, 'OXRES');
    *{xxx_utils::oxa_msg_warn} = decorateur(\&info_or_warn, 'OXINC');
}

sub info_or_warn {
    my ( $msg ) = @_;
    say $msg;
}

sub decorateur {
    my ( $coderef, $type_msg) = @_;
    return sub {
        my ( $msg, $flag ) = @_;
        my $tms = ( $flag ) ? utils_timestamp() . ' - '  : '';
        $coderef->($type_msg . ': ' . $tms . $msg);
    }
}
Cela fonctionne, et je me pose maintenant la question de la pertinence de ce cas et si oui, de son écriture.
Commentaires/suggestions bienvenus.
1  0 
Avatar de Lolo78
Rédacteur/Modérateur https://www.developpez.com
Le 20/01/2018 à 18:19
Citation Envoyé par ptonnerre Voir le message

c'est historique dans le service, les fonctions oxa doivent toujours avoir le même nom (oxa_xxx et non pas $oxa_xxx) quel que soit le langage utilisé
OK, dans ce cas, tu peux sans doute créer ta fonction directement dans la table des symboles sans pour autant avoir besoin d'utiliser de décorateur:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use strict;
use warnings;
use feature "say";

sub create_oxa_message {
    my $ox_what = shift;
    return sub {
        my $msg = shift;
        say "$ox_what: TMS $msg";
    }
}

*::oxa_msg_info = create_oxa_message ("OXRES: ");
*::oxa_msg_warn = create_oxa_message ("OXINC: ");

oxa_msg_info("Information");
oxa_msg_warn("Avertissement");
Ce qui affiche:
Code : Sélectionner tout
1
2
3
OXRES: : TMS Information
OXINC: : TMS Avertissement
Voire même ceci en bouclant sur les clefs et valeurs d'un hachage de paramétrage:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use feature "say";

sub create_oxa_message {
    my $ox_what = shift;
    return sub {
        my $msg = shift;
        say "$ox_what: TMS $msg";
    }
}

my %hash = (info => "OXRES: ", warn => "OXINC: ");
{
    no strict "refs";
    *{"::oxa_msg_" . $_} = create_oxa_message ($hash{$_}) for keys %hash;
}

oxa_msg_info("Information");
oxa_msg_warn("Avertissement");
ce qui affiche la même chose que précédemment.

La syntaxe devient un peu plus complexe et moins claire (voire quelque peu inélégante), mais ça peut être intéressant si tu as beaucoup de types de messages (en plus de info et warn).


Hormis cela, est-ce que le codage des décorateurs te paraît correct ?
A priori, oui (mais je n'ai pas testé, n'ayant pas de données me permettant de le faire), mais si tu as testé et dis que ça marche, alors oui, certainement. Sauf, bien sûr, que comme je viens de le dire ci-dessus, tu peux en l'occurrence utiliser les techniques que j'ai décrites dans le tutoriel (écriture dans la table des symboles) sans avoir besoin d'un décorateur.
1  0 
Avatar de Lolo78
Rédacteur/Modérateur https://www.developpez.com
Le 20/01/2018 à 19:41
En y réfléchissant, ce que j'ai proposé dans mon dernier post n'est en définitive pas très différent de ce que tu as fait à l'origine dans le tien, même si ce n'est pas fait exactement de la même façon (ma version est quand même un peu plus simple, me semble-t-il).

J'ai été un peu trompé par le nom (decorateur) de ta fonction, mais ce que tu fais dans ton code n'est pas tout-à-fait un décorateur dans la mesure où tu ne remplaces pas une fonction existante par une autre (du moins dans le sens que j'ai donné su mot dans le tutoriel), mais te contentes en fait de créer de nouvelles fonctions en utilisant une fonction tout juste créée (note que, du coup, tu n'as semble-t-il pas besoin du pragma no warnings 'redefine';, puisque tu ne redéfinis pas une fonction existante (à toi de vérifier avec ton code, mais en tous cas, je n'en ai bas besoin avec le mien). Cela dit, ton code utilise bien le même principe que les décorateurs décrits dans le tuto.

C'est un usage qui peut être utile et auquel je n'avais pas vraiment pensé quand j'ai rédigé le tuto. J'y réfléchirai, mais peut-être que j'ajouterai un paragraphe sur ce type d'utilisation des techniques employées pour créer mes décorateurs. Merci à toi de me donner une piste d'enrichissement du tutoriel.
1  0 
Avatar de ptonnerre
Membre habitué https://www.developpez.com
Le 22/01/2018 à 13:41
Je confirme que le pragma no warnings 'redefine' n'est pas nécessaire.

Ta version qui écrit directement dans la table des symboles est effectivement plus simple et je vais la retenir, tout en gardant la mienne pour mémo, version qui mélangeait allègrement, il me semble, décorateurs et usine à fonctions

Quoi qu'il en soit, ce tuto ouvre de nombreuses voies de réflexion et j'espère qu'il sera utile à beaucoup d'entre nous.
1  0 
Avatar de Lolo78
Rédacteur/Modérateur https://www.developpez.com
Le 30/01/2018 à 19:55
Bonjour,

Citation Envoyé par Lolo78 Voir le message

C'est un usage qui peut être utile et auquel je n'avais pas vraiment pensé quand j'ai rédigé le tuto. J'y réfléchirai, mais peut-être que j'ajouterai un paragraphe sur ce type d'utilisation des techniques employées pour créer mes décorateurs. Merci à toi de me donner une piste d'enrichissement du tutoriel.
suite à la discussion avec ptonnerre sur ces utilisations de la table des symboles (hors décorateurs), j'ai ajouté une nouvelle section (§ 4.3) dans le tutoriel.

Bonne lecture à tous et merci à ptonnerre.

Bonne soirée,
Laurent.
1  0 
Avatar de Lolo78
Rédacteur/Modérateur https://www.developpez.com
Le 17/12/2017 à 11:24
Merci du compliment.

Citation Envoyé par ptonnerre Voir le message
La lecture du début de ce tutoriel, notamment les premiers exemples d'utilisation de cache, m'a fait réfléchir à un de mes scripts.
J'ai pu ainsi optimiser un traitement mensuel qui dure en moyenne 1h27 à 52 minutes.
Si ce n'est pas confidentiel et si ce n'est pas trop compliqué, il serait peut-être intéressant que tu publies un post illustrant cette optimisation. Ça pourrait rendre service à d'autres.
0  0