Historiquement, Perl prend en charge la portée lexicale des variables, ce qui permet d'utiliser des closures — des fonctions qui conservent l'environnement extérieur. Le problème survient lorsque la closure fait référence à des variables en dehors de sa portée ou lorsque des structures imbriquées créent des références cycliques, ce qui entraîne des fuites de mémoire en raison d'une gestion imprudente des références.
La solution consiste à utiliser des closures pour créer des usines de fonctions et un style fonctionnel, tout en gardant à l'esprit la bonne gestion des références lors de la fermeture des variables de la portée externe.
Exemple de code :
sub make_counter { my $count = 0; return sub { $count++; }; } my $counter = make_counter(); print $counter->(), " "; # 0 print $counter->(), " "; # 1
Caractéristiques clés :
Que se passera-t-il si on retourne une fonction anonyme se référant à elle-même ?
Une référence cyclique sera créée, que Perl ne pourra pas collecter automatiquement avec le ramasse-miettes. Cela entraînera une fuite de mémoire. Pour remédier à cela, utilisez des références faibles, le module Scalar::Util :
use Scalar::Util qw(weaken); my $foo; $foo = sub { ... $foo ... }; weaken($foo);
Une closure capture-t-elle toujours une « copie » de la variable, ou s'agit-il d'une référence à la même variable ?
La closure opère toujours sur la variable actuelle, sa portée étant fixée lors de la création de la closure. Ainsi, la variable est la même pour tous les appels de la fonction closure.
Est-il possible de faire en sorte qu'une closure fonctionne avec un état mutable en dehors d'elle, mais sans maintenir une référence forte ?
Oui, utilisez des références faibles (Scalar::Util::weaken) ou structurez le code de sorte que les références ne soient conservées que là où c'est nécessaire (par exemple, passez des données de l'extérieur à chaque appel de la closure).
Création d'un callback-closure, qui ferme $self d'un objet OO, et se tient à l'intérieur d'un hash callbacks. Après la destruction de l'objet, la mémoire n'est pas libérée.
Avantages :
Inconvénients :
Une closure se réfère correctement faiblement à $self à l'aide de Scalar::Util::weaken :
use Scalar::Util qw(weaken); my $cb = sub { my $self = shift; weaken($self); ... };
Avantages :
Inconvénients :