Question

Je trouve souvent utile de pouvoir programmer le code à exécuter à la sortie de la portée actuelle. Dans ma vie précédente en TCL, un ami a créé une fonction que nous avons appelé defer.

Il a permis code comme:     ensemble fp [ouvert "x"]     reporter ( "fp $ close");

qui a été invoqué lorsque la portée actuelle est sortie. Le principal avantage est qu'il a toujours invoqué, peu importe comment / où je laisse la portée.

Je mis en œuvre quelque chose de similaire en Perl, mais il semble qu'il y aurait un moyen plus facile. critiques des commentaires sont les bienvenus.

La façon dont je l'ai fait en Perl:

  • créer une variable globale, liée qui détient un tableau de sous-marins à exécuter.
  • quand je veux programmer une note de bas à invoquer la sortie, je l'utilise locale pour changer le tableau. quand je quitte la portée actuelle, Perl change global à la valeur précédente parce que le monde est lié, je sais quand ce changement de valeur se produit et peut invoquer les sous-marins dans la liste.

Le code actuel est ci-dessous.

Y at-il une meilleure façon de le faire? Semble ce serait une capacité généralement nécessaire.

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";
Était-ce utile?

La solution

Eh bien, votre cas est déjà traité si vous utilisez des descripteurs de fichier lexical (par opposition à l'ancien style bareword handles de fichiers). Pour les autres cas, vous pouvez toujours utiliser la méthode DETRUIRE d'un objet garanti d'aller à zéro des références quand il est hors de portée:

#!/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: J'aime le nom de brian mieux, Scope :: OnExit est un nom beaucoup plus descriptif

.

Autres conseils

Au lieu d'utiliser cravate pour cela, je pense que je venais de créer un objet. Vous pouvez également éviter la local cette façon aussi.

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

...
}

Lorsque la variable est hors de portée, vous avez une chance de faire les choses dans la méthode DETRUIRE.

En outre, dans l'exemple que vous avez publié, vous devez vérifier que les valeurs que vous enregistrez sont des références de code, et il est probablement une bonne idée de vérifier que la valeur VAL est une référence de tableau:

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

Vous pouvez essayer B :: crochets :: EndOfScope

Je crois que cela fonctionne:

   use B::Hooks::EndOfScope; 

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

   foo();
scroll top