首先直接回答——grep 占用的那个块,你可以在里面放任何代码。这就是块的重点,一个元素是否通过/不基于返回的最后一个语句的真实性。
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) 左右),但这与小阵列。使用问题中提到的散列来解决与存在相关的问题是一种标准;例如,请参见 this post,或下面讨论的所有库中使用的方法的来源 (simplest example)。
最后,虽然问题是关于(双重)过滤,但应该提到我们正在寻找列表中的哪些元素不在另一个列表中;列表之间的“差异”。然后其他类型的库开始发挥作用。一些例子
使用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 以获得大量想法。