在Perl中正确地转发返回值

在Perl中正确地转发返回值,perl,return-type,Perl,Return Type,我对Perl非常陌生,很难理解它的隐式类型系统。我想要实现的是一个简单的包装函数,它与它包装的函数具有相同的签名,因此可以在其位置使用它 假设我有一个要包装的现有函数orig。我接受许多输入参数,并根据这些参数具有不同的返回类型。只要我以以下方式编写包装器,返回类型与原始函数相同,并且运行良好: sub wrapper { my ($first) = @_; print "before. first argument: $first\n"; return orig(@_)

我对Perl非常陌生,很难理解它的隐式类型系统。我想要实现的是一个简单的包装函数,它与它包装的函数具有相同的签名,因此可以在其位置使用它

假设我有一个要包装的现有函数
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会根据不同的值返回不同的值,所以这个包装器不是完全透明的。