从另一个数组中删除特定的数组元素

时间:2013-09-02 08:13:28

标签: regex perl

问题 - 我有两个数组如下。

my @arr1 = qw( jon won don pon );
my @arr2 = qw( son kon bon won kon don pon won pon don won);

我需要从@ arr2中删除@ arr1的第一个匹配元素,即在上面的示例中,我需要从@ arr2中删除won。

目前我的逻辑如下。

#!/usr/bin/perl
my @arr1 = qw( jon won don pon );
my @arr2 = qw( son kon bon won kon don pon won pon don won);
my @remove_indices = ();
my $remove_element;
my $first_remove_index;
OUTER_FOR: for my $i (0..@arr2) {
    $outer_element = $arr2[$i];
    foreach my $innr_element ( @arr1 ) {
        if($innr_element eq $outer_element) {
            push(@remove_indices, $i);
            $first_remove_index = $i;
            $remove_element = $innr_element;
            last OUTER_FOR;
        }
    }
}

for my $i ($first_remove_index+1..@arr2) {
    $outer_element = $arr2[$i];
    if($remove_element eq $outer_element) {
        push(@remove_indices, $i);
    }
}

if (@remove_indices > 0) {
        map {splice (@arr2, $_, 1)} reverse(@remove_indices);
                                    }

print "@arr2";

但它似乎是典型的C / C ++风格逻辑。我不能使用哈希。有没有perl方法做同样的事情?

4 个答案:

答案 0 :(得分:1)

这是另一种方式(假设您要删除所有出现的第一个匹配元素):

use strict;
use warnings;

my @arr1 = qw(jon won don pon);
my @arr2 = qw(son kon bon won kon don pon won pon don won);

for my $elem (@arr2){
    if(grep { $_ eq $elem } @arr1){
        @arr2 = grep { $_ ne $elem } @arr2;
        last;
    }
}

print "@arr2";

输出:

son kon bon kon don pon pon don

答案 1 :(得分:1)

病毒,当然你可以使用哈希来解决这个问题。

如果扩展了示例中一个或两个数组的大小,它实际上会为您提供更好的CPU使用率。

use strict;

my @arr1 = qw(jon won don pon);
my @arr2 = qw(son kon bon won kon don pon won pon don won);

my $i;
my %h;
for (@arr2) { push @{$h{$_} }, $i++ }
for my $a (@arr1) {
    if (exists $h{$a}) {
        for (@{$h{$a}}) {
            $arr2[$_] = '';
        }
        last;
    }
}

@arr2 = grep { length } @arr2;
print "@arr2\n";

我很好奇不同提出的解决方案的相对效率,并编写了一个测试程序来试用它们。您会很高兴地知道您的程序在使用测试数据时非常好。但是当数组的大小开始增长时就不行了!

下面的内容有点疯狂,我知道,但无论如何都要进行。但是,如果您每天要运行数百万次应用程序,那么通过对硬件进行基准测试可能会让您受益匪浅。所以忍受我:)。

以下是5个解决方案中每个解决方案按上面发布顺序的相对CPU时间(最经济的显示为“1”)。第一个结果列使用示例中使用的数据保存CPU时间,并且后面的三列通过在前面添加50个元素来增加两个数组中的一个或两个,其中的内容在另一个中找不到。

First array                @arr1     @arr1   @arr1+50  @arr1+50
Second array               @arr2   @arr2+50    @arr2   @arr2+50

Your program                 1         7         3        45
Grep approach 1              1         6         3        43
Grep approach 2              3         9        33       160
Convert to string            2         4         3         6
Using hash                   2         6         2         7

在测试数据上运行时,哈希解决方案的CPU密集度是您的两倍。但是如果你将第二个数组扩展50个元素,那么散列会更好一点,因为你的CPU时间现在已经增加到7而散列方法已经从2变为6.但如果两个数组都更大,你的程序需要45倍以上CPU完成时间比原始数据需要的时间长,而哈希程序只需要3.5倍(从2到7)。

显然所有都需要更多的CPU时间,因为阵列的大小增长但不是相同的比例,并且它们的减速也不是线性的。它们都可能都被调整了一下,我想结果会在不同的硬件平台上发生变化,但这些应该合理地表明它们的相对效率。以下是原始数组增长100个元素而不是50个元素的时间。

First array                @arr1     @arr1  @arr1+100  @arr1+100
Second array               @arr2  @arr2+100    @arr2   @arr2+100

Your program                 1        12         5       173
Grep approach 1              1        12         4       162
Grep approach 2              3        16        68       612
Convert to string            2         7         5        12
Using hash                   2        11         3        12

因此,对于是否应该更喜欢第4种方法(将测试数组转换为单个字符串然后通过它进行正则表达式,一种死于羊毛的perl程序员的解决方案)和第5种方法,这是一个折腾的问题。它使用哈希)。从边缘来看,“转换为字符串”方法更好,这对许多程序员来说都是违反直觉的。它也简短易读。

底线...如果您将使用类似示例中的数据集,那么您的代码就可以了(尽管您应该在INNER_LOOP修复“(0 .. @ arr2)”:读取“(0) .. $#ARR2)“)。

否则,根据您的口味使用第4或第5。我个人会选择不是我的,“转换为字符串”程序,只要我100%确定加入的字符不会出现在数据中。

我想我现在最好回去做一些有效的事情:)

答案 2 :(得分:0)

您可以使用简单的for循环,并在找到匹配项时使用last退出循环。使用grep删除关键字。

use strict;
use warnings;

my @arr1 = qw( jon won don pon );
my @arr2 = qw( son kon bon won kon don pon won pon don won);
my @out;
for my $word (@arr1) {
    my @new = grep !/^\Q$word\E$/, @arr2;
    if (@new != @arr2) {
        print "'$word' found\n";
        @out = @new;
        last;
    }
}
print "@out";

请注意,我使用\Q ...\E来禁用可能的正则表达式元字符。 !=比较会将数组大小相互比较,当找到差异时,我们知道我们找到了匹配。

答案 3 :(得分:0)

我确信有很多方法可以做到这一点。这是一个...

my @arr1 = qw( jon won don pon );
my @arr2 = qw( son kon bon won kon don pon won pon don won);

my $s2 = join '|', @arr2;


my $item;
foreach $item (@arr1) {
        last unless $s2 !~ s/$item//g;
}
$s2 =~ s/\|\|/\|/g;
@arr2 = split /\|/, $s2;

print Dumper( @arr2 );