2016-11-25 21 views
5

Chciałbym osłabić przechwycone zmienne w kodzie generowanym przez Sub::Quote. Na przykład, oto niekwotowanym alternatywa:Osłabienie przechwytywania przy użyciu Sub :: Quote

use 5.10.0; 
use Scalar::Util qw[ weaken ]; 
{ 
    my $s = 'foo'; 
    my $x = sub { say $s }; 
    weaken(my $y = $x); 

    my $bar = sub { &$y }; 
    &$bar; 
    $x = undef; 
    &$bar 
} 

i wyjście:

foo 
Can't use an undefined value as a subroutine reference [...] 

A oto moja Sub :: Cytat próba:

use 5.10.0; 
use Sub::Quote; 
use Scalar::Util qw[ weaken ]; 
{ 
    my $s = 'foo'; 
    my $x = sub { say $s }; 
    weaken(my $y = $x); 

    my $bar = quote_sub('&$y', { '$y' => \$y }); 
    &$bar; 
    $x = undef; 
    &$bar; 
} 

i wyjście:

foo 
foo 

Najwyraźniej złapany $y nie jest osłabiony. Czy istnieje sposób na zmianę generowanego kodu w celu osłabienia przechwyconych zmiennych?

Dokumentacja jest skąpa, a implementacja Sub::Quote jest złożona; Jestem dość przekonany, że nie jest to możliwe przy obecnym kodzie, ale chciałbym, aby okazało się, że jest źle.

+0

Czy "osłabienie" rzeczywiście coś robi? Gdyby zadziałało, oczekiwałbym, że '$ y' stanie się' undef' zaraz po 'osłabieniu $ y'. – melpomene

+0

@melpomene, istnieje drugie odniesienie do CV (jak widać z Devel :: Peek 'Dump'), choć nie wiem, co to jest. /// To powiedziawszy, oznacza to, że '$ y = undef;' faktycznie nie zwalnia subramu, jak oczekuje OP. Wezwanie do "osłabienia" może zostać usunięte bez wpływu na demo. – ikegami

+0

@ikegami Założę się, że drugie odniesienie jest w samym optree, ponieważ sub nie jest zamknięciem, więc prawdopodobnie został stworzony podczas kompilacji i utrzymany na zawsze. – melpomene

Odpowiedz

3
my $bar = quote_sub('&$y', { '$y' => \$y }); 

jest w przybliżeniu taka sama jak

my $bar = eval(q{ my $y = $y; sub { &$y } }); 

(to nie więcej, lecz te bity mają znaczenia na to pytanie). Jak widać, tworzy to nowe silne odniesienie do sub [1].

jako obejście, można dodać warstwę zadnie:

my $bar = eval(q{ my $y_ref = \$y; sub { &{ $$y_ref } } }); 

ten może zostać osiągnięty za pomocą:

my $bar = quote_sub('&{$$y_ref}', { '$y_ref' => \\$y }); 

Nie byłoby żadnych problemów, jeśli $y stworzony przez Sub :: Quote był aliasem dla twojego $y. Można to osiągnąć za pomocą Data :: Alias ​​lub eksperymentalnej funkcji wprowadzonej w 5.22.

Można to wykazać za pomocą następujących czynności:

{ 
    package Sub::Quote; 

    my $sub = sub { 
    my ($from, $captures, $indent) = @_; 
    join(
     '', 
     "use feature qw(refaliasing);\n", 
     "no warnings qw(experimental::refaliasing);\n", 
     map { 
     /^([\@\%\$])/ 
      or croak "capture key should start with \@, \% or \$: $_"; 
     (' ' x $indent).qq{\\my ${_} = \\${1}{${from}->{${\quotify $_}}};\n}; 
     } keys %$captures 
    ) 
    }; 

    no warnings qw(redefine); 
    *capture_unroll = $sub; 
} 


my $bar = quote_sub('&$y', { '$y' => \$y }); 

Można rozmawiać opiekuna modułu o dodanie opcji, które spowodowałyby użycie aliasingu.


  1. Podczas tworzenia kopii (silne lub słabe) odniesienia, to silna odniesienia.
+0

Zaktualizowałem moją odpowiedź. – ikegami