我对Perl很陌生,并且很难将其包裹在非常隐含的类型系统中.我想要实现的是一个简单的包装函数,它与它包装的函数具有相同的签名,因此可以在它的位置使用它.
所以,假设我有一个orig我想要包装的现有函数.我接受了许多输入参数,并根据这些参数具有不同的返回类型.只要我按以下方式编写包装器,返回类型与原始函数相同,它们都可以正常工作:
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
return orig(@_);
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我想在执行之后在包装器中执行一些代码,orig我不知道如何保留类型.根据我的理解,对perl函数的输入始终是一个标量数组,输出也是如此.所以解决方案应该是这样的:
sub wrapper {
my ($first) = @_;
print "before. first argument: $first\n";
my @result = orig(@_);
print "after";
return @result;
}
Run Code Online (Sandbox Code Playgroud)
但这似乎并没有像预期的那样奏效.我错过了什么?如何编写这样的包装函数,它适用于任意返回类型?
根据我的理解,对perl函数的输入始终是一个标量数组,输出也是如此.
不,不完全.
可以在列表上下文,标量上下文或void上下文中调用Perl函数.
some_function(@args); # void
my $result = some_function(@args); # scalar
my @results = some_function(@args); # list
Run Code Online (Sandbox Code Playgroud)
Perl的许多内置函数根据它们被调用的上下文采取不同的行为.例如,grep返回列表上下文中的结果列表,以及标量上下文中的结果计数.
如果您正在编写自己的函数并希望在不同的上下文中表现不同,则该函数可以使用该wantarray关键字来检测它被调用的上下文.wantarray对于列表上下文返回true,对于标量上下文返回false,对于void上下文返回undef.
即使你不是有意识地想要根据上下文编写一个行为不同的函数,你最终也可能通过返回一个上下文相关的表达式(例如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];
}
Run Code Online (Sandbox Code Playgroud)
现在,如果你的包装器不需要改变@_,并且不需要改变返回值,那么Class :: Method :: Modifiers可以使这更容易:
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";
};
Run Code Online (Sandbox Code Playgroud)