【问题标题】:perl code blocksperl 代码块
【发布时间】:2012-05-08 15:18:07
【问题描述】:

我对 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 循环范围内本地定义),我不断收到“在哈希元素中使用未初始化的值”。

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

【问题讨论】:

  • my @newArr = sort { $a &lt;=&gt; $b } @oldArr; @oldArr 根据这个是什么意思?
  • "但是让我们假设排序函数更复杂" -> 也许你应该发布这个排序函数的代码。

标签: perl


【解决方案1】:

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

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

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

不完全是。下面只解析和编译一次 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;
}

【讨论】:

  • 请原谅篇幅,但您想知道它是如何工作的,而不仅仅是为了解决方案。
  • 谢谢,我正在考虑使用代码引用而不是函数,但我不确定这是否会有所不同。
  • 在你的最后一个例子中,是否有必要通过并重新声明$a$b
  • @TLP,我猜不是(如果它们都在同一个包中)。不过,似乎真的很脏。
【解决方案2】:

除了$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 中不容易复制。

【讨论】:

    【解决方案3】:

    扩展暴民的答案,这是“聪明但不一定聪明”的品种之一。如果你反对额外的参数,你可以使用柯里化来代替。

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

    这实际上并没有更高效、更易读或更有价值,但在实际有用的地方了解该技术可能会很有趣。

    【讨论】:

    • 我也是从这条路开始的,当我得到它时将答案更改为sort { make_sorter($hash,$val)-&gt;($a,$b) } @oldArr ...
    猜你喜欢
    • 2017-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多