我正在努力更好地学习 Perl grep。
我想 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;
Run Code Online (Sandbox Code Playgroud)
@bad_args 最后一行中的坏参数列表显然是错误的。
我知道我可以用哈希做同样的事情,但我希望能够用多阶 grep 来做到这一点,即 grep on grep 。
我怎样才能像下面这样做到这一点?
my @bad_args = grep { not grep {$_ eq $_} @defined_args} keys %args;
Run Code Online (Sandbox Code Playgroud)
我很困惑,因为会有两个$_,我无法对其进行相等测试。
zdi*_*dim 10
首先是直接的答案——那个块grep,你可以在里面放入任何代码。这就是该块的要点,元素的传递/不传递基于最后返回的语句的真实性。
my @bad_args = grep {
my $key = $_;
@defined_args == grep { $key ne $_ } @defined_args
} keys %args;
Run Code Online (Sandbox Code Playgroud)
这里我们测试一个键是否不等于数组元素,然后测试它是否不等于所有元素,由什么决定。另一种方法是测试它是否等于任何一个元素,
not grep { $key eq $_ } @defined_args;
Run Code Online (Sandbox Code Playgroud)
这有点复杂,需要使用否定。
但这些都是常见的事情,而且还有图书馆。
直接改进上述
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;
Run Code Online (Sandbox Code Playgroud)
现在,所需的“负数”已被吸收到库的函数名称中,这使得它更容易查看。此外,none一旦发现失败就会停止,同时grep始终处理所有元素,因此这也更有效。
与基于散列的方法(复杂度O(NM-M 2 /2)左右)相比,这些方法的效率不是很高,但这对于小型数组来说完全无关。问题中提到的使用哈希值来处理与存在相关的问题是一个标准;例如,请参阅 这篇文章,或下面讨论的所有库中使用的方法的来源(最简单的示例)。
最后,虽然问题是关于(双重)过滤,但应该提到的是,我们正在寻找列表中的哪些元素不在另一个列表中;列表之间的“差异”。然后其他类型的库就开始发挥作用。一些例子
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;
Run Code Online (Sandbox Code Playgroud)
另请注意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";
Run Code Online (Sandbox Code Playgroud)
选项-u并-a展示一些模块功能,以加快速度;他们没有必要。这个模块有很多,请参阅文档。另一端是简单的Array::Utils。
还有更多。例如,请参阅此页面以获取大量想法。