2013-04-24 72 views
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->($_) } @_; 

看起來功能上等同於我。我在這裏錯過了一些$_的角落案例行爲,其中較長的版本在某種程度上是多少?

回答

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 

別名傳遞的,所以內$_[0]是別名爲$_,其上面重疊到外$_[0],這是一個別名常數1/$array[0]

那麼,local $_ = $_[$_]在這裏做什麼?

  • 它使一個複製的價值,從而避免了這個瘋狂的混淆行爲
  • 它顯示了意圖使$_回調可見。

確保複製語義(從而避免意外的副作用)對於Perl來說很自然,所以這個函數設計良好,而且沒有特別的過度工程。

(注:map {local $_ = $_; ...} @_就已經足夠使複印件)

+0

釘它。謝謝! – 2013-04-25 12:25:17