문제
나는 종종 현재 범위를 떠날 때 코드를 실행하도록 예약 할 수있는 것이 유용하다고 생각합니다. 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';
...
}
(서브에 대한 참조에 대한 참조를 갖는 추가 수준은 비 폐쇄 익명 서브를 사용할 때 최적화를 중심으로 작업하기 위해서만 필요합니다.)