【问题标题】:How to remove array elements equal to some element in a second array in perl如何在perl中删除等于第二个数组中某个元素的数组元素
【发布时间】:2011-11-23 06:09:47
【问题描述】:

只是想知道是否给了我两个数组 A 和 B,如何删除/删除 A 中也可以在 B 中找到的那些元素?最有效的方法是什么?

另外,作为一种特殊情况,如果 B 是 A 上 grep 之后的结果数组,该怎么做?当然,在这种情况下,我们可以在否定条件上执行grep。但是在 perl 中是否有类似对另一个数组进行补码的东西?

谢谢。

【问题讨论】:

标签: perl


【解决方案1】:

每当您想到found in 时,您可能正在寻找哈希。在这种情况下,您将创建 B 值的散列。然后你会 grep A,检查每个元素的哈希值。

my @A = 1..9;
my @B = (2, 4, 6, 8);
my %B = map {$_ => 1} @B;

say join ' ' => grep {not $B{$_}} @A; # 1 3 5 7 9

如您所见,perl 通常不会自己维护任何类型的found in 表, 所以你必须提供一个。上面的代码可以很容易地包装成一个函数,但为了效率,最好内联。

【讨论】:

  • mod +1。 map 本身令人印象深刻,但 grep 的使用令人惊叹。我花了一段时间才意识到它在做什么。在意识到您实际上并没有使用 grep 来匹配该行之前,我想知道您在 grepping 是什么。如果语句not $B{$_} 为真(并且它将适用于不在%B 中的所有键),则$_ 的值将保存在grep 命令返回的数组中。
  • 我喜欢这个,但是如果我在删除@B 中的所有元素后还想在@A 中维护订单怎么办? perl 中有没有像 java 中的 LinkedHashMap 这样的东西?
  • @Qiang Li,他的代码确实保持了@A 中元素的顺序。 (假设 main = 维护)
  • @Qiang Li,Tie::IxHash 是创建有序关联数组(如 LinkedHashMap)的一种方式,但我不明白这与您的问题或此解决方案有什么关系。跨度>
  • @Zaid:在这种情况下不会有任何自动复活。您应该在编写 cmets 之前尝试一下。你对 Perl 的了解并不像你想象的那么多。
【解决方案2】:

查看none、all、part、notall 可通过List::MoreUtils 获得的方法。您可以使用本模块中提供的方法执行几乎任何集合操作。

Perl Training Australia有一个很好的教程

【讨论】:

    【解决方案3】:

    如果您要求最有效的方式:

    my @A = 1..9;
    my @B = (2, 4, 6, 8);
    
    my %x;
    @x{@B} = ();
    my @AminusB = grep !exists $x{$_}, @A;
    

    但您会注意到我的和Eric Strom 的solution 之间的区别仅适用于更大的输入。

    您可以找到方便的这种函数式方法:

    sub complementer {
      my %x;
      @x{@_} = ();
      return sub { grep !exists $x{$_}, @_ };
    }
    
    my $c = complementer(2, 4, 6, 8);
    
    print join(',', $c->(@$_)), "\n" for [1..9], [2..10], ...;
    
    # you can use it directly of course
    print join(' ', complementer(qw(a c e g))->('a'..'h')), "\n";
    

    【讨论】:

    • 它并不比其他工作解决方案更有效(根据定义)。也许您的意思是高效?
    【解决方案4】:

    使用哈希可能会更好,但也可以使用smart matching。窃取Eric Strom的例子,

    my @A = 1..9;
    my @B = (2, 4, 6, 8);
    
    say join ' ' => grep {not $_ ~~ @B } @A; # 1 3 5 7 9
    

    【讨论】:

    • 它的扩展性不如 Eric Strom 的。他的解决方案是最坏情况 Θ(A+B),但你的解决方案是 Θ(A*B)
    【解决方案5】:

    同样,您可能最好使用散列,但您也可以使用Perl6::Junction。再次盗用Eric Strom的例子,

    use Perl6::Junction qw(none);
    
    my @A = 1..9;
    my @B = (2, 4, 6, 8);
    
    say join ' ' => grep {none(@B) == $_} @A; # 1 3 5 7 9
    

    【讨论】:

      【解决方案6】:

      正如Eric Strom 已经提到的,每当您需要搜索特定的内容时,如果您有哈希值总是会更容易。

      Eric 有一个更好的解决方案,但可能难以理解。我希望我的更容易理解。

      # Create a B Hash
      
      my %BHash;
      foreach my $element (@B) {
         $BHash{$element} = 1;
      }
      
      # Go through @A element by element and delete duplicates
      
      my $index = 0;
      foreach my $element (@A) {
         if (exists $BHash{$element}) { 
            splice @A, $index, 1;    #Deletes $A[$index]
            $index = $index + 1;
         }
      }
      

      在第一个循环中,我们简单地创建一个以@B 中的元素为键的散列。

      在第二个循环中,我们遍历@A 中的每个元素,同时跟踪@A 中的索引。

      【讨论】:

      • 它失败了,因为它修改了它迭代的数组。所有这些额外的复杂性都会影响它的可读性。
      • @ikegami:将数组复制到一个新数组,然后将其重命名回原来的数组是否会更易读?这不是我会这样做的方式。我正在努力提高可读性。
      • 你应该首先担心它的工作。 my @C; for my $e (@A) { push @C, $e if !$B{$e}; } @A = @C; 可以让它工作,但它是一种非常复杂的方法来做@A = grep { !$B{$e} } @A;。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多