2012-05-08 31 views
7

Tôi có câu hỏi liên quan đến các khối mã trong perl. Với mã sau:khối mã perl

my @newArr = sort { $a <=> $b } @oldArr; 

sử dụng khối mã làm đối số.

tôi có thể viết lại nó như:

sub sortFunc { 
     return $a <=> $b; 
    } 
    my @newArr = sort sortFunc @oldArr; 

Tôi cố gắng để tìm ra cách cơ chế này hoạt động. Hiện tại, tôi cần triển khai một loại hàm sắp xếp phức tạp trông có vẻ lộn xộn trong một khối mã, Tuy nhiên nó phụ thuộc vào một số biến cục bộ. Ví dụ:

foreach my $val (@values){ 
     my @newArr = sort { $hash{$a}{$val}<=> $hash{$b}{$val} } @oldArr; 
     ... 
    } 

nhưng giả sử rằng chức năng sắp xếp phức tạp hơn, vì vậy nó sẽ không gọn gàng phù hợp với mã ở trên.

Nếu tôi cố gắng sử dụng hàm (được định nghĩa cục bộ trong phạm vi vòng lặp for), tôi tiếp tục nhận "Sử dụng giá trị chưa được khởi tạo trong phần tử băm".

Tôi cho rằng đó là vì phụ được phân tích cú pháp một lần và không được tạo lại để lặp lại lặp lại của vòng lặp for. Tôi đang cố gắng hiểu cách triển khai khối mã sẽ được giải thích lại mỗi lần lặp lại hoặc có thể cách chuyển các tham số

+0

'my @newArr = sort {$ a <=> $ b} @oldArr;' @ArArr nghĩa là gì theo điều này? –

+0

"nhưng hãy giả sử rằng hàm sắp xếp phức tạp hơn" -> có lẽ bạn nên đăng mã của mình cho hàm sắp xếp này. – TLP

Trả lời

9

Bạn không hiển thị mã vấn đề đối với một số lý do, nhưng tôi nghĩ đó là một cái gì đó giống như

for my $val (@values) { 
    sub sort_func { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    } 

    my @newArr = sort sort_func @oldArr; 
} 
Tôi cố gắng để tìm ra cách cơ chế này hoạt động. [...] Tôi cho rằng đó là vì phụ được phân tích cú pháp một lần và không được tái tạo để lặp lại lặp lại của vòng lặp for.

Không hoàn toàn. Sau đây chỉ phân tích cú pháp và biên dịch phụ một lần, nhưng nó hoạt động:

for my $val (@values) { 
    my $cmp_func = sub { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    }; 

    my @newArr = sort $cmp_func @oldArr; 
} 

Điều gì quan trọng khi chụp $val. $val được chụp khi sub { ... } được đánh giá.Hãy nhớ rằng

sub foo { ... } 

cũng giống như sau trong vấn đề này,

BEGIN { *foo = sub { ... }; } 

Trong mã của tôi, nó còn thể hiện $val từ vòng lặp foreach. Trong máy của bạn, nó chụp tại thời gian biên dịch, vì vậy nó bắt giữ $val tồn tại ở thời gian biên dịch. Và đó không phải là biến mà bạn muốn.

Bây giờ bạn đã biết cách làm cho nó hoạt động, bạn có thể di chuyển mã phức tạp ra khỏi con đường (ngoài vòng lặp) theo ý muốn.

sub make_cmp_func { 
    my ($hash, $val) = @_; 
    return sub { 
     return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
    }; 
} 

for my $val (@values) { 
    my $cmp_func = make_cmp_func(\%hash, $val); 
    my @newArr = sort $cmp_func @oldArr; 
} 

Hoặc, bạn có thể chuyển các giá trị cần thiết cho hàm so sánh thay vì chụp chúng.

sub cmp_func { 
    my ($hash, $val, $a, $b) = @_; 
    return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
} 

for my $val (@values) { 
    my @newArr = sort { cmp_func(\%hash, $val, $a, $b) } @oldArr; 
} 
+0

Tha thứ dài, nhưng bạn đã hỏi để biết nó hoạt động như thế nào, không chỉ cho một giải pháp. – ikegami

+0

Cảm ơn, Tôi đã cân nhắc sử dụng tham chiếu mã thay vì một hàm, nhưng tôi không chắc liệu điều đó có làm được gì khác không. – Smartelf

+0

Trong ví dụ cuối cùng của bạn, có cần thiết phải chuyển và khai báo lại '$ a' và' $ b' không? – TLP

8

Bạn muốn sử dụng hàm lấy đối số ngoài $a$b.

sub my_sort_func { 
    my ($val, $a, $b) = @_; 
    return $hash{$a}{$val} <=> $hash{$b}{$val}; 
} 

foreach my $val (@values) { 
    my @newArr = sort { my_sort_func($val,$a,$b) } @oldArr; 
    ... 
} 

cơ chế Perl cho sử dụng một khối mã với sort là hơi đặc biệt, và không dễ dàng nhân rộng trong tinh khiết Perl.

5

Mở rộng câu trả lời của mob, đây là một trong những loại "thông minh nhưng không nhất thiết phải thông minh". Nếu bạn phản đối tham số bổ sung, bạn có thể sử dụng currying thay thế.

sub make_sorter { 
    my ($hashref, $val) = @_; 
    return sub { 
      $hashref->{$a}{$val} <=> $hashref->{$b}{$val} 
    }; 
} 

for my $val (@values) { 
    my $sorter = make_sorter(\%hash, $val); 
    my @newArr = sort $sorter @oldArr; 
} 

Điều này không hiệu quả, dễ đọc hơn hoặc có giá trị hơn theo bất kỳ cách nào, nhưng có thể thú vị khi biết về kỹ thuật ở nơi nào đó thực sự hữu ích.

+0

Tôi bắt đầu xuống con đường này, quá, và thay đổi câu trả lời của tôi khi tôi nhận nó để 'sort {make_sorter ($ băm, $ val) -> ($ a, $ b)} @ oldArr' ... – mob