5

現在のスコープを離れるときに実行されるコードをスケジュールできると便利なことがよくあります。TCLでの私の前の人生で、友人がdeferと呼ばれる関数を作成しました。

次のようなコードが有効になりました。setfp[open"x"] defer( "close $ fp");

これは、現在のスコープが終了したときに呼び出されました。主な利点は、スコープを離れる方法や場所に関係なく、常に呼び出されることです。

だから私はPerlで似たようなものを実装しましたが、もっと簡単な方法があるようです。コメント批評を歓迎します。

私がPerlでそれをした方法:

  • 実行するサブの配列を保持するグローバルなタイ変数を作成します。
  • 終了時にfnが呼び出されるようにスケジュールするときはいつでも、localを使用して配列を変更します。現在のスコープを離れると、グローバルが関連付けられているため、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

5 に答える 5

4

これにネクタイを使用する代わりに、オブジェクトを作成するだけだと思います。その方法も回避できlocalます。

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

...
}

変数がスコープ外になると、DESTROYメソッドで何かを行う機会があります。

また、投稿した例では、格納する値がコード参照であることを確認する必要があります。VAL値が配列参照であることを確認することをお勧めします。

sub TIESCALAR {bless {VAL => []}、$ _ [0]}

サブストア{
    my($ self、$ value)= @_;

    carp「配列参照のみを保存できます!」ref $ value eq ref [];

    foreach {@ $ value} {
        carp「配列にはコード参照のみが存在する必要があります」
            ref $ _ eq refsub{}でない限り
        }

    foreach(@ {$ self-> {VAL}}){$ _->()}


    $ self-> {VAL} = $ value;
    }
于 2009-03-22T00:30:47.640 に答える
4

レキシカル ファイル ハンドルを使用する場合、特定のケースは既に処理されています (古いスタイルのベアワード ファイル ハンドルとは対照的です)。他のケースでは、オブジェクトがスコープ外になったときにゼロ参照になることが保証されているオブジェクトの DESTROY メソッドを常に使用できます。

#!/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: 私はブライアンの名前の方が好きです。Scope::OnExit はもっとわかりやすい名前です。

于 2009-03-22T00:37:31.167 に答える
3

B::Hooks::EndOfScopeを試してみてください。

私はこれがうまくいくと信じています:

   use B::Hooks::EndOfScope; 

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

   foo();
于 2009-03-22T05:10:47.233 に答える
1

Scope :: Guardのようなものが必要だと思いますが、プッシュすることはできません。うーん。

ありがとう。

于 2009-03-21T23:44:07.877 に答える
1

些細なことですが、

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

次のように使用されます:

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

(サブへの参照への参照を持つ余分なレベルは、非クロージャの匿名サブを使用する場合の最適化 (おそらくバグ) を回避するためにのみ必要です。)

于 2009-03-22T05:27:26.493 に答える