题
我经常发现能够安排在离开当前范围时执行的代码很有用。我以前在 TCL 工作时,一位朋友创建了一个我们称为 defer 的函数。
它启用了如下代码:设置fp [open“ x”] defer(“ close $ fp”);
当当前作用域退出时调用它。主要好处是,无论我如何/在何处离开范围,它总是会被调用。
所以我在 Perl 中实现了类似的东西,但似乎有一种更简单的方法。欢迎评论批评。
我在 Perl 中的做法是:
- 创建一个全局绑定变量,其中包含要执行的子数组。
- 每当我想安排在退出时调用 fn 时,我都会使用 local 来更改数组。当我离开当前范围时,perl将全局更改为上一个值,因为全局绑定了,我知道何时发生此值更改并可以在列表中调用subs。
实际代码如下。
有一个更好的方法吗?看来这将是一种普遍需要的能力。
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";
解决方案
好吧,如果您使用词法文件句柄(而不是旧式的裸字文件句柄),您的特定情况已经得到处理。对于其他情况,您始终可以使用对象的 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}();
}
预计到达时间:我更喜欢 brian 的名字,Scope::OnExit 是一个更具描述性的名字。
其他提示
我想我应该创建一个对象,而不是使用 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; }
您可能想尝试一下 B::钩子::作用域结束
我相信这有效:
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';
...
}
(只有在使用非闭包匿名子时,才需要对子的引用进行额外级别的引用来解决优化(这可以说是一个错误)。)
不隶属于 StackOverflow