Perl 将标量/列表上下文传递给被调用的子例程
我正在尝试编写一个接受coderef参数的sub。我的sub执行一些初始化,调用coderef,然后执行一些清理 我需要使用调用sub的相同上下文(标量、列表、void上下文)调用coderef。我能想到的唯一办法是这样:Perl 将标量/列表上下文传递给被调用的子例程,perl,Perl,我正在尝试编写一个接受coderef参数的sub。我的sub执行一些初始化,调用coderef,然后执行一些清理 我需要使用调用sub的相同上下文(标量、列表、void上下文)调用coderef。我能想到的唯一办法是这样: sub perform { my ($self, $code) = @_; # do some initialization... my @ret; my $ret; if (not defined wantarray) {
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;
}
}
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];
}
显然,这段代码中有大量冗余。有没有办法减少或消除这种冗余
EDIT我后来意识到我需要在eval
块中运行$code->()
,这样即使代码死了,清理也会运行。添加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->()) });
}
这消除了冗余,但不幸的是现在控制流有点难以遵循。您可以排除
!很早就定义了wantarray
案例,因为不需要进行清理(因为$code->()
的结果(如果有的话)没有存储)。这将从剩余函数中删除一个案例,使其更简单
其次,您可以将清理内容移动到它自己的函数中。我想到了这样一件事:
sub perform
{
my($self, $code) = @_;
if (!defined(wantarray)) {
$code->();
return;
}
return wantarray ? &cleanup($code->()) : &cleanup(scalar($code->()));
}
替补表演
{
我的($self,$code)=@;
如果(!已定义(wantarray)){
$code->();
返回;
}
返回wantarray?&cleanup($code->()):&cleanup(scalar($code->());
}
我想我会这样做:
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;
}
}
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
函数需要一个检查才能正确返回传入的值。在第二次检查中,您不必担心unde
的情况,因为在这种情况下,perform
返回什么并不重要。在CPAN上检查模块。我认为它可以让你做你想做的事情(可能还有更多)。我还应该指出,如果不需要做一些清理…
,整个函数可以简化为return$code->()
,上下文将自动被调用的子例程继承。但不幸的是,清理是必要的。“执行”吗?这是马戏团的代码吗?不幸的是,即使从void上下文调用,清理也需要始终进行。但是我喜欢你的建议,即使用一个单独的清理子例程来自动为被调用的子例程提供上下文。尽管这是一个常见的奇怪的Damianized接口,但它确实解决了这方面的问题,即使它有些过分。我不知道Context::Return如何添加除了另一个依赖项之外的任何东西。这真的不是它试图解决的问题。它不能帮助您将正确的上下文传递给coderef,而coderef实际上是这个问题中较难的一半。和返回wantarray@ret:$ret[0]
不够复杂,不足以保证加载一个模块来“简化”它。@cjm:谢谢您的编辑……CPAN页面何时才能提供指向任何模块的通用链接?这是一个令人讨厌的问题——我还没有记住在没有版本的情况下访问特定模块的“正确”方法,而遵循您的链接最终会得到我提供的链接(今天;我知道它随时都可能改变)。如果这意味着有一个规范的链接,这将是有益的,如果该网站会给一种方式来获得它!如果您查看CPAN页面的右上角(作者图片的正上方),您将看到单词“permalink”。这就是你想要的链接。