【问题标题】:Perl multi-order grep with $_带 $_ 的 Perl 多阶 grep
【发布时间】:2021-11-07 06:13:12
【问题描述】:

我正在尝试更好地学习 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;

错误参数列表在@bad_args的位置最后一行显然是错误的。

我知道我可以使用哈希执行相同的操作,但我希望能够使用多阶 grep 执行此操作,即 grep on grep。

我怎样才能这样做?

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

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

【问题讨论】:

    标签: perl grep


    【解决方案1】:

    首先直接回答——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 以获得大量想法。

    【讨论】:

    • 很好的答案。使用集合的想法很棒。但是哈希可以用作集合。请参阅 bdf 的答案。
    • 当然,哈希是这里的标准。由于这个问题专门围绕我只提供了一个基于哈希的解决方案的链接,作为最后一句话;我现在把它移到了第一部分的末尾(无论如何它都属于那里)。谢谢
    【解决方案2】:

    当你陷入这种纠结时,有时最好找到不同的方式。

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

    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;
    

    【讨论】:

      猜你喜欢
      • 2019-10-23
      • 2012-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多