2012-05-08 73 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