在Perl中正确地转发返回值
我对Perl非常陌生,很难理解它的隐式类型系统。我想要实现的是一个简单的包装函数,它与它包装的函数具有相同的签名,因此可以在其位置使用它 假设我有一个要包装的现有函数在Perl中正确地转发返回值,perl,return-type,Perl,Return Type,我对Perl非常陌生,很难理解它的隐式类型系统。我想要实现的是一个简单的包装函数,它与它包装的函数具有相同的签名,因此可以在其位置使用它 假设我有一个要包装的现有函数orig。我接受许多输入参数,并根据这些参数具有不同的返回类型。只要我以以下方式编写包装器,返回类型与原始函数相同,并且运行良好: sub wrapper { my ($first) = @_; print "before. first argument: $first\n"; return orig(@_)
orig
。我接受许多输入参数,并根据这些参数具有不同的返回类型。只要我以以下方式编写包装器,返回类型与原始函数相同,并且运行良好:
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
return orig(@_);
}
但是,如果我想在执行orig
之后在包装器中执行一些代码,我不知道如何保存类型。根据我的理解,perl函数的输入总是一个标量数组,输出也是如此。因此,解决方案应该是:
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
my @result = orig(@_);
print "after";
return @result;
}
但这似乎并不像预期的那样奏效。我错过了什么?如何编写这样的包装器函数,使其能够正确地处理任意返回类型
据我所知,perl函数的输入也是如此
输出总是一个标量数组
不完全是
Perl函数可以在列表上下文、标量上下文或void上下文中调用
some_function(@args); # void
my $result = some_function(@args); # scalar
my @results = some_function(@args); # list
许多Perl的内置函数根据调用它们的上下文的不同而不同。例如,grep
返回列表上下文中的结果列表,以及标量上下文中的结果计数
如果您正在编写自己的函数,并且希望在不同的上下文中有不同的行为,那么该函数可以使用wantarray
关键字来检测调用它的上下文wantarray
对于列表上下文返回true,对于标量上下文返回false,对于无效上下文返回undef
some_function(@args); # void
my $result = some_function(@args); # scalar
my @results = some_function(@args); # list
即使您不想有意识地编写一个根据上下文表现不同的函数,也可能会意外地返回上下文敏感的表达式,如grep
,或map
,或数组(标量上下文中的数组返回其长度)
在不破坏上下文的情况下包装函数的正确方法是这样的。是的,我意识到这并不漂亮
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
my @result =
wantarray ? orig(@_) :
defined wantarray ? scalar orig(@_) :
do { orig(@_); () };
print "after";
wantarray ? @result : $result[0];
}
现在,如果您的包装器不需要更改@
,也不需要更改返回值,那么可以让这更容易一些:
use Class::Method::Modifiers;
sub wrapper { orig(@_) } # do nothing in the wrapper itself
before wrapper => sub {
my ($first) = @_;
print "before. first argument: $first\n";
};
after wrapper => sub {
print "after";
};
我怀疑有一种更好的方法可以在Perl中实现您想要的功能,但是由于您只指定了机制,所以我只能告诉您如何使它工作 Perl子例程的返回值取决于调用的上下文。在子例程中,您可以使用内置操作符
wantarray
来检测上下文——如果调用在列表上下文中,它将返回真值,否则返回假值
因此,要传递另一个子例程在相同上下文中返回的值,您必须像这样编写包装器
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
if (wantarray) {
my @result = orig(@_);
print "after";
return @result;
}
else {
my $result = orig(@_);
print "after";
return $result;
}
}
但请记住,给定的子例程可能是在标量或列表上下文中调用的。根据上下文编写行为不同的内容是很少见的,因此根据orig
的行为,通常只需要这两个条件分支中的一个。进行wantarray调度使其工作正常。但是,我将尝试使用Class::Method::Modifiers的方法,因为这将是最优雅的方法!wantarray
和!已定义的wantarray
。事实上,一些sub会根据不同的值返回不同的值,所以这个包装器不是完全透明的。