Domanda

Spesso mi trovo utile per essere in grado di programmare il codice da eseguire dopo aver lasciato l'ambito corrente. Nel mio precedente vita in TCL, un amico ha creato una funzione che abbiamo chiamato rinviare.

E 'attivato il codice come:     set fp [open "x"]     rinviare ( "chiudi $ fp");

che è stato richiamato quando l'ambito corrente terminato. Il vantaggio principale è che è sempre invocato non importa come / dove lascio portata.

Così ho implementato qualcosa di simile in Perl, ma sembra non ci sarebbe un modo più semplice. Commenti critiche benvenuto.

Il modo in cui l'ho fatto in Perl:

  • creare una variabile globale, legato che contiene una matrice di sub da eseguire.
  • ogni volta che voglio programmare un fn ad essere invocate in uscita, io uso locale per modificare l'array. quando lascio l'attuale portata, Perl cambia il globale al valore precedente perché il globale è legato, so quando questo cambiamento valore accade e può invocare i sub nella lista.

Il codice attuale è al di sotto.

C'è un modo migliore per fare questo? Sembra che questo sarebbe una capacità comunemente necessarie.

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";
È stato utile?

Soluzione

Bene, il vostro caso specifico è già gestito se si utilizza filehandles lessicali (in contrapposizione ai vecchi filehandles stile bareword). 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 nome molto più descrittiva

.

Altri suggerimenti

Invece di usare cravatta per questo, penso che avevo appena creare un oggetto. È anche possibile evitare il local in quel modo.

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

...
}

Quando la variabile va fuori del campo di applicazione, si ha la possibilità di fare le cose nel metodo destroy.

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;
    }

Si consiglia di provare B :: :: Ganci EndOfScope

Credo che questo funziona:

   use B::Hooks::EndOfScope; 

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

   foo();

Penso che si desidera qualcosa di simile a Ambito :: Guardia , ma può' t essere spinta. Hmmm.

Grazie.

Banalmente,

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

usato come:

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

(Il livello supplementare di avere un riferimento a un riferimento a un sub è necessaria solo per aggirare un'ottimizzazione (che è probabilmente un bug) quando si utilizza un non-chiusura sub anonima.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top