문제

나는 종종 현재 범위를 떠날 때 코드를 실행하도록 예약 할 수있는 것이 유용하다고 생각합니다. TCL의 이전 생활에서 친구는 우리가 연기라고 불렀던 기능을 만들었습니다.

다음과 같은 코드를 활성화합니다. SET FP [OPEN "X"] DEFER ( "Close $ FP");

현재 범위가 종료되면 호출되었습니다. 주요 이점은 내가 스코프를 떠나는 방법/위치에 관계없이 항상 호출된다는 것입니다.

그래서 나는 Perl에서 비슷한 것을 구현했지만 더 쉬운 방법이있는 것 같습니다. 댓글 비평을 환영합니다.

Perl에서 내가 한 방식 :

  • 실행할 서브 배열을 보유하는 전역의 묶인 변수를 만듭니다.
  • 출구에서 FN을 호출하도록 예약하려면 로컬을 사용하여 배열을 변경합니다. 현재 범위를 떠날 때 Perl은 글로벌이 묶여 있기 때문에 글로벌을 이전 값으로 변경합니다.이 값 변경이 언제 발생하는지 알고 목록에서 잠수함을 호출 할 수 있습니다.

실제 코드는 다음과 같습니다.

더 좋은 방법이 있습니까? 이것은 일반적으로 필요한 기능 인 것 같습니다.

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";
도움이 되었습니까?

해결책

어휘 파일 핸들을 사용하는 경우 (이전 스타일의 바레 노드 파일 핸들과는 달리) 특정 케이스는 이미 처리됩니다. 다른 경우, 당신은 항상 범위를 벗어날 때 제로 참조로 이동하도록 보장되는 객체의 파괴 방법을 사용할 수 있습니다.

#!/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 : 나는 Brian의 이름을 더 좋아합니다. Scope :: OneXit은 훨씬 더 설명적인 이름입니다.

다른 팁

넥타이를 사용하는 대신 객체 만 만들 것이라고 생각합니다. 당신은 또한 피할 수 있습니다 local 그런 식도.

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

...
}

변수가 범위를 벗어나면 파괴 방법으로 일을 할 수 있습니다.

또한 게시 한 예에서 저장 값이 코드 참조인지 확인해야하며 Val 값이 배열 참조인지 확인하는 것이 좋습니다.

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

당신은 시도하고 싶을 수도 있습니다 b :: 후크 :: endofscope

나는 이것이 효과가 있다고 믿는다 :

   use B::Hooks::EndOfScope; 

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

   foo();

나는 당신이 같은 것을 원한다고 생각합니다 범위 :: 가드, 그러나 밀어 넣을 수는 없습니다. 흠.

감사.

사소하게,

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

사용 :

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

(서브에 대한 참조에 대한 참조를 갖는 추가 수준은 비 폐쇄 익명 서브를 사용할 때 최적화를 중심으로 작업하기 위해서만 필요합니다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top