2013-04-24 27 views
12

Trong hồ sơ, tôi đã xem qua chức năng này trong 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 hiện một loại số ngược lại dựa trên một số vị quan trọng, ví dụ:

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

Tôi hiểu một cách hoàn hảo tại sao rev_nsort_by, như thể hiện ở trên, hoạt động như dự định, nhưng tôi tự hỏi tại sao nó quá phức tạp. Cụ thể, tôi tự hỏi tại sao

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

không được viết như

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

trông chức năng tương đương với tôi. Tôi có thiếu một số hành vi góc trường hợp của $_ ở đây, phiên bản dài hơn có tính theo một cách nào đó không?

Trả lời

13

Có một trường hợp cạnh tinh tế ở đây: Bên trong foreach vòng, hoặc map biểu thức, biến mặc định $_aliased với giá trị ban đầu. Ví dụ.

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

Tuy nhiên, hằng số không lvalues ​​hợp lệ, vì vậy chúng tôi không thể làm điều đó như

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

Các @_ mảng quá là bí danh của các giá trị ban đầu, vì vậy hãy tưởng tượng trường hợp cạnh sau:

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 

biệt hiệu được bắc cầu, vì vậy nội $_[0] là bí danh của $_, đó là bí danh của bên ngoài $_[0], đó là một bí danh cho hằng số 1/$array[0].

Vì vậy, local $_ = $_[$_] làm gì ở đây?

  • Nó làm cho một sao chép giá trị, như vậy tránh răng cưa hành vi điên rồ này
  • Nó cho thấy ý định để làm $_ thể nhìn thấy gọi lại.

Đảm bảo sao chép ngữ nghĩa (do đó tránh tác dụng phụ không mong muốn) cảm thấy tự nhiên đối với Perl, vì vậy chức năng này được thiết kế tốt và không đặc biệt quá tải.

(Lưu ý: map {local $_ = $_; ...} @_ sẽ đủ để tạo bản sao)

+0

Đóng đinh nó. Cảm ơn! –

Các vấn đề liên quan