2013-04-24 16 views
12

Podczas profilowania, natknąłem się na tej funkcji w 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 robi odwrotnego rodzaju numeryczną opartą na niektórych kluczowych orzecznika, na przykład:

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

Rozumiem doskonale dlaczego rev_nsort_by, jak pokazano powyżej, działa zgodnie z przeznaczeniem, ale zastanawiam się, dlaczego jest tak skomplikowany. Konkretnie, zastanawiam się dlaczego

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

nie został napisany jako

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

który wygląda funkcjonalnie równoważne do mnie. Czy w tym przypadku brakuje mi jakiegoś zachowania w narożnikach z $_, której dłuższa wersja w jakiś sposób się liczy?

Odpowiedz

13

Istnieje subtelna sprawa krawędź tutaj: Wewnątrz foreach pętle lub map wyrażenia, domyślnie zmienna $_ jest alias do pierwotnej wartości. Na przykład.

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

Jednak stałe nie są ważne lwartościami, więc nie mogliśmy zrobić jak

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

Tablica @_ też jest aliasem do oryginalnych wartości, więc sobie wyobrazić następujące przypadki brzegowe:

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 

Pseudo się przechodnia, więc wewnętrzna $_[0] jest synonim $_, który alias tego zewnętrznego $_[0], która alias dla stała 1/$array[0].

Co robi tutaj local $_ = $_[$_]?

  • To sprawia skopiować wartości, unikając tym nienormalną zachowanie aliasingu
  • Pokazuje zamiaru do $_ widoczny dla zwrotnego.

Zapewnienie semantyki kopiowania (unikając nieoczekiwanych efektów ubocznych) wydaje się naturalne dla Perla, więc ta funkcja jest dobrze zaprojektowana, a nie szczególnie zbyt zaawansowana.

(Uwaga: map {local $_ = $_; ...} @_ byłby wystarczający, aby kopia)

+0

Nailed go. Dzięki! –

Powiązane problemy