在Perl中正确转发返回值

Jan*_*net 3 perl return-type

我对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)

但这似乎并没有像预期的那样奏效.我错过了什么?如何编写这样的包装函数,它适用于任意返回类型?

tob*_*ink 7

根据我的理解,对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)