2012-05-08 77 views
7

我有一個關於perl代碼塊的問題。給定以下代碼:perl代碼塊

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

使用代碼塊作爲參數。

,我可以把它改寫爲:

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

我試圖找出這個機制是如何工作的。 目前我需要實現一種複雜的排序功能,在代碼塊中看起來很亂,但它取決於一些局部變量。 例如:

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

但是讓我們假設排序功能更加複雜,所以它不會完全適合上面的代碼。

如果我嘗試使用的功能(在for循環範圍的本地定義),我不斷收到「散列元素未初始化值的使用」。

我想那是因爲子被解析一次,而不是重新爲循環的埃夫裏迭代。我正在嘗試瞭解如何實現將在每次迭代中重新解釋的代碼塊,或者如何傳遞參數

+0

'我@newArr =排序{$一個<=> $ B} @oldArr;'根據本什麼的@oldArr是什麼意思? –

+0

「但讓我們假設排序函數更復雜」 - >也許你應該發佈你的代碼這種排序功能。 – TLP

回答

9

你沒有表現出由於某種原因,有問題的代碼,但是我覺得它像

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

    my @newArr = sort sort_func @oldArr; 
} 
我試圖找出這個機制是如何工作的。 [...]我認爲這是因爲子被解析一次,並沒有重新創建for循環的evry迭代。

不完全。以下內容僅解析和編譯一次次,但它的工作原理:

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

    my @newArr = sort $cmp_func @oldArr; 
} 

要緊的當捕捉什麼$val是。在評估sub { ... }時捕獲$val。牢記

sub foo { ... } 

是一樣的在這方面的下面,

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

在我的代碼,它捕獲從foreach循環的$val。在你的編譯時捕獲,所以它捕獲編譯時存在的$val。這不是你想要的變量。

既然您已經知道如何使其工作,您可以根據需要將複雜代碼移出(不在循環中)。

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; 
} 

或者,您可以將必要的值傳遞給比較函數,而不是捕獲它們。

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

赦免了這個長度,但你問了解它是如何工作的,而不僅僅是解決方案。 – ikegami

+0

謝謝, 我正在考慮使用代碼引用,而不是函數,但我不確定這是否會做任何不同的事情。 – Smartelf

+0

在你的最後一個例子中,是否需要傳遞並重新聲明'$ a'和'$ b'? – TLP

8

您想要使用除$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; 
    ... 
} 

Perl的機制用於使用碼塊以sort有點特殊,並且在純的Perl不容易複製。

5

暴民上的回答延伸,這是對「聰明,但不一定聰明」的品種之一。如果你反對額外的參數,你可以使用currying來代替。

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; 
} 

這不是更高效,更可讀,或以任何真正的方式更有價值,但它可能是有趣的瞭解了某個地方的技術,其中也有實際的用處。

+0

我也開始了這條路,當我把它交給'sort {make_sorter($ hash,$ val) - >($ a,$ b)} @ oldArr'時,我改變了我的答案... – mob