我正在尝试编写一个带有coderef参数的子程序。我的子做了一些初始化,调用了coderef,然后进行了一些清理。
我需要使用我的sub调用的相同上下文(标量,列表,void上下文)来调用coderef。我能想到的唯一方法就是这样:
sub perform {
my ($self, $code) = @_;
# do some initialization...
my @ret;
my $ret;
if (not defined wantarray) {
$code->();
} elsif (wantarray) {
@ret = $code->();
} else {
$ret = $code->();
}
# do some cleanup...
if (not defined wantarray) {
return;
} elsif (wantarray) {
return @ret;
} else {
return $ret;
}
}
显然,此代码中存在大量冗余。有没有办法减少或消除任何这种冗余?
编辑我后来意识到我需要在$code->()
块中运行eval
,以便即使代码消失也能运行清理。添加eval支持,并结合user502515和cjm的建议,这就是我的想法。
sub perform {
my ($self, $code) = @_;
# do some initialization...
my $w = wantarray;
return sub {
my $error = $@;
# do some cleanup...
die $error if $error; # propagate exception
return $w ? @_ : $_[0];
}->(eval { $w ? $code->() : scalar($code->()) });
}
这消除了冗余,但不幸的是现在控制流程有点难以理解。
答案 0 :(得分:3)
查看CPAN上的Contextual::Return模块。我认为它可以让你做你想做的事情(可能还有更多)。
答案 1 :(得分:1)
您可以提前排除!defined wantarray
个案,因为没有清理工作(因为$code->()
的结果(如果有的话)没有存储)。这将从剩余的功能中删除一个案例,使其更简单。
其次,您可以将清理内容移动到自己的功能中。我想到了这样的事情:
sub perform { my($self, $code) = @_; if (!defined(wantarray)) { $code->(); return; } return wantarray ? &cleanup($code->()) : &cleanup(scalar($code->())); }
答案 2 :(得分:1)
我想我会这样做:
sub perform {
my ($self, $code) = @_;
# do some initialization...
my @ret;
if (not defined wantarray) {
$code->();
} else {
@ret = wantarray ? $code->() : scalar $code->();
}
# do some cleanup...
return wantarray ? @ret : $ret[0];
}
你仍然有两个wantarray
项检查,但是你的cleanup
函数需要一个才能正确返回它传入的值。你不需要担心关于第二次检查中的undef
个案,因为在这种情况下,perform
返回的内容并不重要。