在 Perl 中作为引用传递给子例程时访问数组

Accessing array when passed to subroutine as reference in Perl

我对在 Perl 中使用大括号引用有误解。 我构建了一个基本函数来检查数组是否包含值。

示例 1:

sub array_contains
{
    my ($arr_ref, $search, $strict) = @_;
    if ($strict) {
        return (grep {$_ eq $search} @$arr_ref);
    } else {
        return grep {index($_, $search) != -1} @$arr_ref;
    }
}

后来想把它写得简洁一些

示例 2:

sub array_contains
{
    return (!$_[2] ? 
       (grep {$_ eq $_[1]} @{$_[0]}) : 
       (grep {index($_, $_[1]) != -1} @{$_[0]})
    );
}

然后,我花了一些时间才意识到由于某些原因我不能使用 @$_[0],就像在第一个示例中一样,而必须使用 @{$_[0]}


Perl高手解释一下,为什么必须在第二个例子@{$_[0]}上,不能在@$_[0]上,就像在第一个示例,我使用了 @$arr_ref。它与将 @_ 分配给局部范围的变量有什么关系吗?

@$arr@$_[0] 中的前缀取消引用运算符绑定非常紧密 - 因此后者被解析为 @{ $_ }[0]。这是一个 数组切片 ,它使用 $_ 作为数组引用。这与 @_ 数组无关。一个元素的切片通常写为标量访问 ${ $_ }[0]$_->[0].

由于前缀取消引用运算符的解析令人困惑,我建议使用自 Perl 5.24 起可用的后缀取消引用语法。那么@{ $_[0] }可以写成$_[0]->@*.

注意代码要清晰,不一定等同于简短。命名您的参数而不是直接访问 @_ 数组更易于维护。如果只有一个参数(例如在访问器方法 sub foo { shift->{foo} } 中),或者当我对代码进行基准测试并确定避免复制将节省我真正需要的半微秒时,我只会直接访问 @_。 (不会经常发生。)

如果你的函数的目标是布尔检查而不是选择所有匹配的元素,我会这样写:

use List::Util 'any';

sub array_contains {
  my ($arr, $search, $is_strict) = @_;
  return any { $_ eq $search } @$arr if $is_strict;
  return any { -1 != index $_, $search } @$arr;
}

考虑这一点的方法是解引用 "operator" 比数组索引具有更高的 "precedence"(这里使用引号是因为解引用不是 Perl 的正式部分 operator precedence table) .

use warnings;
use strict;

my $foo = ['arrayref elem 0','arrayref elem 1'];
my @foo = (['array of arrayrefs elem 0',' one']);

print  @$foo[0] , "\n";  # prints "arrayref elem 0"

# is the same as
print @{$foo}[0], "\n";  # prints "arrayref elem 0"

# is NOT the same as
print @{$foo[0]}, "\n";  # prints "array of arrayrefs elem 0 one"

也就是说,在前两个示例中,首先将存储在 $foo 中的数组引用作为数组解引用,然后 然后 该数组用 [0](虽然这是使用 slice 语法,但 $$foo[0]${$foo}[0] 在这里更有意义)。只有在最后一个示例中,数组 @foo 元素 [0] 被访问 first,并且 then 该元素被取消引用为数组,导致打印该数组的所有元素。 $_/@_.

的行为相同

另请参阅 Using References in perlref@{...} 解引用样式始终有效,而大括号 在简单情况下可以省略,例如 @$arr_ref.

如果你有 Perl 5.24 或更新版本,你可以使用 5.20 和 5.22 中的 Postfix Dereference Syntax (it was experimental:

use 5.024;
my @bar = (['Hello,',' World!']);
print $bar[0]->@*, "\n";  # prints "Hello, World!"