perl代码块

时间:2012-05-08 15:18:07

标签: 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;
       ...
   }

但我们假设sort函数更复杂,因此它不能完全适合上面的代码。

如果我尝试使用一个函数(在for循环的范围内本地定义),我会继续“在哈希元素中使用未初始化的值”。

我认为这是因为sub被解析一次,而不是为for循环的evry迭代重新创建。我试图了解如何实现每次迭代将重新解释的代码块,或者可能如何传递参数

3 个答案:

答案 0 :(得分:9)

由于某些原因,您没有显示有问题的代码,但我认为它类似于

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

   my @newArr = sort sort_func @oldArr;
}
我试图弄清楚这种机制是如何运作的。 [...]我假设这是因为sub被解析一次,而不是为for循环的evry迭代重新创建。

不完全。以下仅解析和编译sub一次,但它可以工作:

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

   my @newArr = sort $cmp_func @oldArr;
}

重要的是捕获$val的内容。评估$val时会捕获sub { ... }。记住

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

答案 1 :(得分: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中复制。

答案 2 :(得分: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;
}

这在任何方面都不是更有效,更具可读性或更有价值,但在某些实际有用的地方了解该技术可能会很有趣。