什么是\&和$ expr->()

时间:2016-10-13 03:32:44

标签: perl

sub reduce(&@) {

    my $expr = \&{shift @ARG};

    my $result = shift @ARG;
    while (scalar @ARG > 0) {
        our $a = $result;
        our $b = shift @ARG;
        $result = $expr->();
    }

    return $result;
}

我无法理解这段代码中的一些语法。有人可以向我解释一下吗?例如\&$result = $expr->()

2 个答案:

答案 0 :(得分:8)

\&name返回对名为name的子例程的引用。

$code_ref->()调用$code_ref引用的子例程。

$ perl -e'
    sub f { CORE::say "Hi" }
    my $code_ref = \&f;
    $code_ref->();
'
Hi

在您的情况下,shift @ARG返回子例程引用,所以

my $expr = \&{shift @ARG};

本来可以写成

my $expr = shift @ARG;

有点像做$y = $x+1-1;

请注意,reduce的原型允许将其称为

reduce { ... } ...

但实际执行的是

reduce(sub { ... }, ...)

请注意,reduce的此版本存在问题。您应该使用List::Util提供的那个。

    应该使用
  • local $alocal $b来避免破坏其调用者在$a$b中可能拥有的值。
  • 此版本的reduce期望其回调编译在与reduce本身相同的包中。否则,回调子将无法使用$a$b
  • 在此版本的情况下,使用our声明变量实际上完全没用,因为$a$b免于use strict;检查,并且未声明使用{{1 }和$a将访问完全相同的包变量。

答案 1 :(得分:1)

查看一些List::Util::reduce()示例可能会有所帮助。

我们来看第一个:

$foo = reduce { $a > $b ? $a : $b } 1..10;

所以reduce接受一个BLOCK后跟一个LIST,函数签名声明:sub reduce(&@) {。在我们的例子中,块是语句$a > $b ? $a : $b,而列表是1..10。来自文档:

  

通过在标量上下文中多次调用“BLOCK”来减少@list,   每次设置$ a和$ b。第一个电话将是$ a和$ b设置为   列表的前两个元素,后续调用将由   将$ a设置为上一次调用的结果,将$ b设置为下一个元素   在列表中。

     

返回上次调用“BLOCK”的结果。如果@list为空则      返回“undef”。如果@list只包含一个元素,那么该元素      返回并且不执行“BLOCK”。

现在到代码的注释版本:

$foo = reduce { $a > $b ? $a : $b } 1..10;  # $foo will be set to 10

sub reduce(&@) {
    # reduce() takes a BLOCK followed by a LIST
    my $expr = \&{shift @ARG};
    # $expr is now a subroutine reference, i.e.
    # $expr = sub { $a > $b ? $a : $b };

    # Start by setting $result to the first item in the list, 1
    my $result = shift @ARG;

    # While there are more items in the list...
    while (scalar @ARG > 0) {
        # Set $a to the current result
        our $a = $result;

        # Set $b to the next item in the list
        our $b = shift @ARG;

        # Set $result to the result of $a > $b ? $a : $b
        $result = $expr->();
    }

    # List has now been reduced by the operation $a > $b ? $a : $b
    return $result;
}