Historisch gezien ondersteunt Perl lexicale scope van variabelen, wat het gebruik van sluitingen (closures) mogelijk maakt - functies die de externe omgeving behouden. Het probleem doet zich voor wanneer een sluiting verwijst naar variabelen buiten zijn scope of wanneer geneste structuren cyclische verwijzingen creëren, wat leidt tot geheugenlekken door onzorgvuldig omgaan met verwijzingen.
De oplossing is om sluitingen te gebruiken voor het maken van functie-fabrieken en functionele stijl, en daarbij te zorgen voor goed beheer van verwijzingen bij het sluiten van variabelen uit de externe scope.
Voorbeeldcode:
sub make_counter { my $count = 0; return sub { $count++; }; } my $counter = make_counter(); print $counter->(), "\n"; # 0 print $counter->(), "\n"; # 1
Belangrijke kenmerken:
Wat gebeurt er als je een anonieme functie retourneert die naar zichzelf verwijst?
Er wordt een cyclische verwijzing gemaakt, die Perl niet automatisch kan opruimen met de garbage collector. Dit leidt tot geheugenlekken. Gebruik zwakke verwijzingen om dit op te lossen, module Scalar::Util:
use Scalar::Util qw(weaken); my $foo; $foo = sub { ... $foo ... }; weaken($foo);
Vangt een sluiting altijd een "kopie" van de variabele, of is het een verwijzing naar dezelfde variabele?
Een sluiting werkt altijd met de huidige variabele, de scope ervan wordt vastgelegd bij het creëren van de sluiting. Dus, de variabele is dezelfde voor alle aanroepen van de sluitfunctie.
Kun je het zo maken dat een sluiting werkt met veranderlijke staat buiten zichzelf, maar geen sterke verwijzing naar houdt?
Ja, gebruik zwakke verwijzingen (Scalar::Util::weaken) of structureer de code zodat verwijzingen alleen worden vastgehouden waar nodig (bijvoorbeeld door gegevens van buitenaf bij elke sluitaanroep door te geven).
Een callback-sluiting gemaakt die $self van een OO-object vastlegt en binnen een hash callbacks wordt vastgehouden. Na de vernietiging van het object wordt het geheugen niet vrijgegeven.
Voordelen:
Nadelen:
Sluiting verwijst correct zwak naar $self met behulp van Scalar::Util::weaken:
use Scalar::Util qw(weaken); my $cb = sub { my $self = shift; weaken($self); ... };
Voordelen:
Nadelen: