2009-03-21 30 views
5

Tôi thường thấy hữu ích khi có thể lên lịch mã được thực hiện khi rời khỏi phạm vi hiện tại. Trong kiếp trước của tôi trong TCL, một người bạn đã tạo ra một hàm mà chúng tôi gọi là trì hoãn.Trì hoãn mã về thay đổi phạm vi trong Perl

Mã kích hoạt như sau: đặt fp [open "x"] trì hoãn ("đóng $ fp");

được gọi khi phạm vi hiện tại đã thoát. Lợi ích chính là nó luôn luôn được gọi cho dù tôi có rời khỏi phạm vi như thế nào.

Vì vậy, tôi đã triển khai một cái gì đó tương tự trong Perl nhưng có vẻ như sẽ có một cách dễ dàng hơn. Bình luận phê bình chào đón.

Con đường tôi đã làm điều đó trong Perl:

  • tạo ra một biến gắn toàn cầu nắm giữ một loạt các tàu ngầm được thực hiện.
  • bất cứ khi nào tôi muốn lên lịch fn được gọi khi thoát, tôi sử dụng cục bộ để thay đổi mảng. khi tôi rời khỏi phạm vi hiện tại, Perl thay đổi toàn cầu thành giá trị trước đó vì toàn cầu bị ràng buộc, tôi biết khi nào thay đổi giá trị này xảy ra và có thể gọi các người đăng ký trong danh sách.

Mã thực tế bên dưới.

Có cách nào tốt hơn để thực hiện việc này không? Dường như đây sẽ là một khả năng phổ biến cần thiết.

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

Trả lời

4

Vâng , trường hợp cụ thể của bạn đã được xử lý nếu bạn sử dụng các tập tin từ vựng (trái ngược với các tập tin kiểu thanh công cụ kiểu cũ). Đối với các trường hợp khác, bạn luôn có thể sử dụng DESTROY phương pháp của một đối tượng được bảo đảm để đi đến tài liệu tham khảo không khi nó đi ra khỏi phạm vi:

#!/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: Tôi thích tên brian của tốt hơn, Phạm vi :: OnExit là nhiều hơn nữa Tên mô tả.

1

Tôi nghĩ bạn muốn một cái gì đó như Scope::Guard nhưng không thể đẩy được. Hừm.

Cảm ơn.

4

Thay vì sử dụng cà vạt cho điều này, tôi nghĩ rằng tôi chỉ muốn tạo một đối tượng. Bạn cũng có thể tránh được local theo cách đó.

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

... 
} 

Khi biến ngoài phạm vi, bạn có cơ hội thực hiện mọi thứ theo phương pháp DESTROY.

Ngoài ra, trong ví dụ bạn đăng, bạn cần phải kiểm tra rằng các giá trị bạn lưu trữ là tài liệu tham khảo mã, và nó có thể là một ý tưởng tốt để kiểm tra xem giá trị VAL là một tham chiếu mảng:

 
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ạn có thể muốn thử ra B::Hooks::EndOfScope

I Believe công trình này:

use B::Hooks::EndOfScope; 

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

    foo(); 
+0

Đó là một ý tưởng hay, nhưng tôi dự định tạo ra một phiên bản tốt hơn cái đó. Việc thực hiện là một chút IMHO cồng kềnh. –

1

trivially,

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

sử dụng như:

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

(Mức thêm của hav một tham chiếu đến tham chiếu đến một phụ chỉ cần thiết để làm việc xung quanh một tối ưu hóa (đó được cho là lỗi) khi sử dụng một phụ không được đóng kín.)