12

在分析期间,我在List::UtilsBy中遇到了这个函数:

sub rev_nsort_by(&@) {
    my $keygen = shift;
    my @keys = map { local $_ = $_[$_]; scalar $keygen->( $_ ) } 0 .. $#_;
    return map { $_[$_] } sort { $keys[$b] <=> $keys[$a] } 0 .. $#_;
}

rev_nsort_by 根据一些关键谓词进行反向数字排序,例如:

my @objects = load_objects_from_database();
# sort by rating, highest first
@objects = rev_nsort_by { $_->rating } @objects;

我完全理解为什么rev_nsort_by,如上所示,按预期工作,但我想知道为什么它如此复杂。具体来说,我想知道为什么

my @keys = map { local $_ = $_[$_]; scalar $keygen->( $_ ) } 0 .. $#_;

没有写成

my @keys = map { scalar $keygen->( $_ ) } @_;

它看起来在功能上与我相同。我是否错过了$_这里的一些极端情况行为,较长的版本在某种程度上相当于?

4

1 回答 1

13

这里有一个微妙的边缘情况:在foreach循环或map表达式内部,默认变量$_别名为原始值。例如

@nums = 1..5;
@foo = map { $_ *= 2 } @nums;
# both @foo and @nums contain 2, 4, 6, 8, 10 now.

但是,常量不是有效的左值,所以我们不能这样做

@foo = map { $_ *= 2 } 1, 2, 3, 4, 5;
# Modification of read-only value

@_数组也被别名为原始值,因此想象以下边缘情况:

sub buggy (&@) { my $cb = shift; map $cb->($_), @_ };

buggy { $_ *= 2 } 1, 2, 3;   # Modification of read-only value attempted
buggy { $_[0] *= 2} 1, 2, 3; # ditto

my @array = 1 .. 5;
buggy { $_ *= 2 } @array;    # @array now is 2, 4, 6, 8, 10
buggy { $_[0] *= 2 } @array; # ditto

别名是可传递的,所以 inner$_[0]被别名为$_,它被别名为 outer $_[0],它是常量1/的别名$array[0]

那么,local $_ = $_[$_]这里做什么呢?

  • 它会复制该值,从而避免这种疯狂的别名行为
  • 它显示了使回调可见的意图。$_

确保复制语义(从而避免意外的副作用)对于 Perl 来说是很自然的,所以这个函数设计得很好,并且没有特别过度设计。

(注意:map {local $_ = $_; ...} @_制作副本就足够了)

于 2013-04-24T17:19:38.487 回答