2009-03-21 8 views
5

현재 범위를 벗어날 때 코드를 실행하도록 예약하는 것이 유용하다는 것을 자주 발견합니다. TCL에서의 나의 이전 생활에서, 친구는 우리가 지연이라고 부르는 함수를 만들었습니다.Perl에서 범위 변경시 코드 연기

다음과 같은 코드가 활성화되었습니다. set fp [open "x"] defer ("close $ fp");

현재 범위가 종료 될 때 호출되었습니다. 가장 큰 장점은 범위를 벗어나는 방법과 장소에 관계없이 항상 호출된다는 것입니다.

그래서 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"; 

답변

4

, 렉시 컬 파일 핸들 (old style bareword filehandles와 반대되는)을 사용하면 특정 케이스가 이미 처리됩니다.

#!/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 : 브라이언의 이름을 더 나은, 범위 ::의 OnExit이 훨씬 더 많은 것입니다 내가 좋아하는이 범위를 벗어나면 다른 경우를 들어, 당신은 항상 제로 참조로 이동 보장 물체의 방법 DESTROY 사용할 수 있습니다 설명적인 이름.

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

(여분의 수준 :

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

처럼 사용, 사소

use B::Hooks::EndOfScope; 

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

    foo(); 
1

나는 Scope::Guard과 같은 것을 원하지만 푸시 할 수는 없다고 생각합니다. 흠.

감사합니다.

4

대신 넥타이를 사용하는 대신 객체를 만들 것입니다. 또한 local도 그렇게 피할 수 있습니다.

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

... 
} 

변수가 범위를 벗어나면 DESTROY 방법으로 작업 할 수 있습니다.

또한 게시 된 예에서, 당신은 당신이 저장 값이 코드 참조 있는지 확인해야하고, 그것은 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; 
    } 
3

당신은 시도 할 수는

B::Hooks::EndOfScope 나는이 작품 믿어 근래의 하위 참조에 대한 참조를 사용하는 것은 비 폐쇄 익명 하위를 사용할 때만 최적화 (버그 일 가능성이 있음)를 해결하는 데 필요합니다.

+0

좋은 아이디어이지만 더 나은 버전을 만들려고합니다. 그. 구현은 약간 성가신 IMHO입니다. –