我经常发现能够在离开当前范围时安排执行代码很有用。在我之前的TCL生活中,一位朋友创建了一个名为defer的函数。
它启用了以下代码: 设置fp [open“x”] 推迟(“关闭$ fp”);
当前范围退出时调用的。主要的好处是,无论我如何/在哪里留下范围,它总是被调用。
所以我在Perl中实现了类似的东西,但似乎有一种更简单的方法。评论批评欢迎。
我在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";
答案 0 :(得分:4)
我认为我只是创建一个对象,而不是使用tie。您也可以通过这种方式避免使用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; }
答案 1 :(得分: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:我更喜欢brian的名字,Scope :: OnExit是一个更具描述性的名字。
答案 2 :(得分:3)
您可能想试用B::Hooks::EndOfScope
我相信这有效:
use B::Hooks::EndOfScope;
sub foo {
on_scope_end {
$codehere;
};
$morecode
return 1; # scope end code executes.
}
foo();
答案 3 :(得分:1)
我认为你想要像Scope::Guard这样的东西,但它不能被推动。嗯。
感谢。
答案 4 :(得分:1)
中平凡,
sub OnLeavingScope::DESTROY { ${$_[0]}->() }
用过:
{
...
my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
...
}
(当使用非闭包匿名子时,引用sub的引用的额外级别仅用于解决优化(可以说是一个bug)。)