2009-03-21 3 views
5

Spesso trovo utile essere in grado di pianificare il codice da eseguire all'uscita dall'ambito corrente. Nella mia precedente vita in TCL, un amico ha creato una funzione che abbiamo chiamato differire.Differire codice sulla modifica dell'oscilloscopio in Perl

Abilitato codice come: set fp [open "x"] posticipato ("chiudi $ fp");

che è stato richiamato all'uscita dall'ambito corrente. Il vantaggio principale è che è sempre invocato indipendentemente da come/dove lascio lo scope.

Così ho implementato qualcosa di simile in Perl ma sembra che ci sarebbe stato un modo più semplice. Commenti critici benvenuto.

La modalità utilizzate in Perl:

  • creare una variabile globale, legato che contiene una matrice di sottomarini da eseguire.
  • ogni volta che voglio programmare un fn da richiamare all'uscita, utilizzo local per cambiare l'array. quando lascio l'ambito corrente, Perl cambia il globale al valore precedente perché il globale è legato, so quando questo cambiamento di valore accade e può richiamare i sottotitoli nella lista.

Il codice reale è di seguito.

C'è un modo migliore per farlo? Sembra che questa sarebbe una capacità comunemente necessaria.

use strict; 

package tiescalar; 

sub TIESCALAR { 
    my $class = shift; 

    my $self = {}; 
    bless $self, $class; 
    return $self; 
} 

sub FETCH { 
    my $self = shift; 
    return $self->{VAL}; 
} 

sub STORE { 
    my $self = shift; 
    my $value = shift; 

    if (defined($self->{VAL}) && defined($value)) { 
    foreach my $s (@{$self->{VAL}}) { &$s; } 
    } 
    $self->{VAL} = $value; 
} 

1; 

package main; 

our $h; 
tie($h, 'tiescalar'); 
$h = []; 
printf "1\n"; 
printf "2\n"; 

sub main { 
    printf "3\n"; 
    local $h = [sub{printf "9\n"}]; 
    push(@$h, sub {printf "10\n";}); 
    printf "4\n"; 
    { 
    local $h = [sub {printf "8\n"; }]; 
    mysub(); 
    printf "7\n"; 
    return; 
    } 
} 

sub mysub { 
    local $h = [sub {printf "6\n"; }]; 
    print "5\n"; 
} 

main(); 

printf "11\n"; 

risposta

4

Bene , il tuo caso specifico è già gestito se usi filehandle lessicali (al contrario dei filehandle bareword vecchio stile). Per gli altri casi, si può sempre utilizzare il metodo di un oggetto garantito per andare a zero riferimenti distrugge quando si va fuori del campo di applicazione:

#!/usr/bin/perl 

use strict; 
use warnings; 

for my $i (1 .. 5) { 
    my $defer = Defer::Sub->new(sub { print "end\n" }); 
    print "start\n$i\n"; 
} 

package Defer::Sub; 

use Carp; 

sub new { 
    my $class = shift; 
    croak "$class requires a function to call\n" unless @_; 
    my $self = { 
     func => shift, 
    }; 
    return bless $self, $class; 
} 

sub DESTROY { 
    my $self = shift; 
    $self->{func}(); 
} 

ETA: Mi piace il nome di Brian meglio, Scope :: Su uscita è un molto più nome descrittivo.

1

Penso che si desidera qualcosa di simile Scope::Guard, ma non può essere spinto. Hmmm.

Grazie.

4

Invece di usare la cravatta per questo, penso che creerei un oggetto. Puoi anche evitare lo local in questo modo.

{ 
my $defer = Scope::OnExit->new(@subs); 
$defer->push($other_sub); # and pop, shift, etc 

... 
} 

Quando la variabile esce dal campo di applicazione, è possibile eseguire operazioni nel metodo DISTRIBUZIONE.

Inoltre, nell'esempio che hai postato, è necessario controllare che i valori memorizzati sono riferimenti di codice, ed è probabilmente una buona idea per verificare che il valore VAL è un riferimento ad array:

 
sub TIESCALAR { bless { VAL => [] }, $_[0] } 

sub STORE { 
    my($self, $value) = @_; 

    carp "Can only store array references!" unless ref $value eq ref []; 

    foreach { @$value } { 
     carp "There should only be code refs in the array" 
      unless ref $_ eq ref sub {} 
     } 

    foreach (@{ $self->{VAL}}) { $_->() } 


    $self->{VAL} = $value; 
    } 
3

Si consiglia di provare B::Hooks::EndOfScope

Credo che questo funziona:

use B::Hooks::EndOfScope; 

    sub foo { 
     on_scope_end { 
       $codehere; 
     }; 
     $morecode 
     return 1; # scope end code executes. 
    } 

    foo(); 
+0

È una buona idea, ma intendo fare una versione migliore di quella. L'implementazione è un po 'ingombrante IMHO. –

1

Banalmente,

sub OnLeavingScope::DESTROY { ${$_[0]}->() } 

utilizzato come:

{ 
    ... 
    my $onleavingscope = bless \sub { ... }, 'OnLeavingScope'; 
    my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope'; 
    ... 
} 

(Il livello extra di hav un riferimento a un riferimento a un sottotitolo è necessario solo per aggirare un'ottimizzazione (che è probabilmente un bug) quando si utilizza un sub anonimo non di chiusura)