带有 $_ 的 Perl 多顺序 grep

Perl multi-order grep with $_

我正在努力学习 Perl 的 grep

我想查询散列的哪些键在数组中不是

my %args = ( fake => 1);
my @defined_args = ('color', 'colors', 'data', 'figheight', 'figwidth', 'filename', 'flip', 'grid', 'labelsize', 'logscale', 'minor_gridlines');
my @bad_args = grep { not grep {$_} @defined_args} keys %args;

where the list of bad args is in @bad_args最后一行显然是错误的。

我知道我可以用散列做同样的事情,但我希望能够用多顺序 grep 做到这一点,即 grep on grep。

我怎样才能像下面那样做到这一点?

my @bad_args = grep { not grep {$_ eq $_} @defined_args} keys %args;

我很困惑,因为会有两个 $_,我不能 运行 对其进行相等性测试。

首先是直接答案 -- grep 占用的那个块,您可以在其中放入任何代码。这就是块的要点,以及基于 returns.

最后一个语句的真实性的元素 passes/not
my @bad_args = grep { 
    my $key = $_; 
    @defined_args == grep { $key ne $_ } @defined_args
} keys %args;

这里我们测试一个键是否不等于数组元素,然后测试它是否不等于所有元素,由什么决定。另一种方法是测试它是否等于任何一个元素,

not grep { $key eq $_ } @defined_args;

这有点复杂,需要用否定来处理。

但这些都是常见的事情,而且有图书馆。

在上面直接改进

use List::Util 1.33 qw(none);  # before 1.33 it was in List::MoreUtils

my @bad_args = grep { 
    my $key = $_; 
    none { $key eq $_ } @defined_args 
} keys %args;

现在,所需的“否定”已包含在库的函数名称中,使其更易于查看。此外,none 将在发现失败后停止,而 grep 始终处理所有元素,因此这也更有效率。

与基于散列的方法(复杂度 O(NM-M2/2) 或所以)但这与小型阵列完全无关。问题中提到的使用散列来处理与存在相关的问题是一种标准;例如参见 [​​=20=]).


最后,虽然问题是关于(双重)过滤的,但应该提到我们正在寻找列表中的哪些元素不在另一个列表中;列表之间的“差异”。然后其他类型的库开始发挥作用。一些例子

使用Set::Scalar

use Set::Scalar;
...

my $keys = Set::Scalar->new(keys %args);
my $good = Set::Scalar->new(@defined_args);

my $keys_not_in_good = $keys->difference($good);
say $keys_not_in_good;

另请注意Set::Object在同一个营地。

还有专门用于数组比较的工具,比如List::Compare

use List::Compare;
...

my $lc = List::Compare->new('-u', '-a', \@defined_args, [keys %args]);

my @only_in_second = $lc->get_complement();

say "@only_in_second";

选项 -u-a 展示了一些模块功能,以加快速度;他们不是必需的。这个模块有很多,看文档。 另一端是简单的 Array::Utils.

还有更多。例如,请参阅 this page 以获得大量想法。

当您陷入这些纠结时,有时最好找到不同的方法。

有两件事需要考虑。如果你想嵌套使用 $_,你需要以某种方式保护外部的。由于要在同一个表达式中使用外层和内层,其中一个需要不同的名称:

grep {
  my $top = $_;
  my $count = grep { $top eq $_ } ...;
  ...
  } keys %args;

但是,内部 grep 有点奇怪。你想检查列表中是否有某物。这就是散列和 exists:

的工作
my %allowed_args = map { $_, 1 } @allowed_args;
my @found_bad_args = grep { ! exists $allowed_args{$_} } keys %args;