在分析期间,我在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->( $_ ) } @_;
看起来在功能上等同于我。我在这里错过了$_
的一些角落案例行为,较长版本在某种程度上是多少?
答案 0 :(得分:13)
此处有一个微妙的边缘情况:内部foreach
循环或map
表达式,默认变量$_
别名为原始值。 E.g。
@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
别名是可传递的,因此内部$_[0]
别名为$_
,其别名为外$_[0]
,这是常量1
/ {{1的别名}}
那么,$array[0]
在这做什么?
local $_ = $_[$_]
对回调可见。确保复制语义(从而避免意外的副作用)对Perl来说很自然,因此这个功能设计得很好,而且不是特别过度设计。
(注意:$_
足以制作副本)