2

在 Perl 中,我想从数组中删除所有元素,其中同一数组的另一个元素是所述元素的非空子字符串。

说我有数组

@itemlist = ("abcde", "ab", "khi", "jklm");

在这种情况下,我希望"abcde"删除该元素,因为"ab"它是"abcde".

我可以制作数组的副本(也许作为哈希?),对其进行迭代,尝试使用原始数组的每个元素进行索引并将其删除,但必须有一种更优雅的方式,不是吗?

谢谢你的帮助!

为清楚起见进行了一些编辑。

4

6 回答 6

3

您可以从所有项目构建一个正则表达式并丢弃任何匹配的内容:

$alternation = join('|', map(quotemeta, @itemlist));
@itemlist = grep !/($alternation).|.($alternation)/, @itemlist;

事情只是确保一个项目与().|.()自己不匹配。

于 2013-01-17T15:53:29.237 回答
1

好吧,我不会称之为优雅,但这里有:

#!usr/bin/perl
use strict;
use warnings;

my @itemlist = ("abcde", "ab", "khi", "jklm");

@itemlist = grep { 
    @itemlist ~~ sub {$_ !~ /(?:.\Q$_[0]\E|\Q$_[0]\E.)/} 
} @itemlist;

print "@itemlist";

它依赖于智能匹配的一个相当模糊的行为:如果左参数是一个数组,而右参数是一个 sub,它会为每个元素调用 sub,并且只有当 sub 为每个元素返回 true 时,最终结果才为 true。

解释:对于数组的每个元素,它检查没有其他元素是该元素的子字符串(需要至少一个额外的字符,以便元素不会匹配自己)。

注意: wdebeaum 的答案可能是我在现实世界中更喜欢的答案。尽管如此,使用智能匹配可以做一些奇怪的事情还是很有趣的。

于 2013-01-17T15:47:37.763 回答
0

wdebeaum 的答案是使用的解决方案,而不是下面的解决方案,但我通过这样做学到了一些东西,也许其他人也会这样做。在我写完我的之后,我决定在几千个元素的列表上测试它。

b.pl:

#!/usr/bin/perl

use strict;
use warnings;

my @itemlist = <>;
for(@itemlist) { chomp; }
my $regex;

if(defined $ENV{wdebeaum}) {
    # wdebeaum's solution
    my $alternation = join('|', map(quotemeta, @itemlist));
    $regex = qr/(?:$alternation).|.(?:$alternation)/;
} else {
    # my solution
    $regex = join "|", map {qq{(?:\Q$_\E.)|(?:.\Q$_\E)}} @itemlist;
}

my @result = grep !/$regex/, @itemlist;
print scalar @itemlist, "\t", scalar @result, "\n";

我生成了一个包含 5000 个随机单词的列表。

sort -R /usr/share/dict/american-english|head -5000 > some-words

对于小型列表,两种解决方案似乎都很好。

$ time head -200 some-words | wdebeaum=1 ./b.pl
200 198

real    0m0.012s
user    0m0.004s
sys     0m0.004s

$ time head -200 some-words | ./b.pl
200 198

real    0m0.068s
user    0m0.060s
sys     0m0.004s

但对于较大的列表,wdebeaum 显然更好。

$ time cat some-words | wdebeaum=1 ./b.pl 
5000    1947

real    0m0.068s
user    0m0.064s
sys     0m0.000s

$ time cat some-words | ./b.pl 
5000    1947

real    0m8.305s
user    0m8.277s
sys     0m0.012s

我认为差异的原因是,即使两个正则表达式具有相同数量的可能路径,我的正则表达式也有更多路径需要尝试,因为它与.路径具有相同数量的 s,而 wdebebaum 只有两个.

于 2013-01-17T17:15:23.727 回答
0

您可以使用哈希来计算所有单词的子字符串。列表中计数高于一个的任何单词都是另一个单词的子字符串。在此示例中,子字符串的最小长度为 2:

use strict;
use warnings;
use feature 'say';

my @list = qw(abcde ab foo foobar de oba cd xs);

my %count;

for my $word (@list) {
    my $len = length $word;
    $count{$word}++;
    for my $start (0 .. $len - 2) {
        for my $long (2 .. $len - 2) {
            my $sub = substr($word, $start, $long);
            $count{$sub}++;
        }
    }
}
say for grep $count{$_} == 1, @list;

输出:

abcde
foobar
xs
于 2013-01-17T17:19:03.047 回答
0

以下将从数组中删除子字符串。

#!/usr/bin/perl
use strict;
use warnings;

my @ar=("asl","pwe","jsl","nxu","sl","baks","ak");
foreach my $i (@ar){
  my $p = grep /$i/, @ar;
  if ( $p == 1 ){
    print "$i" , "\n";
  }
} 
于 2013-01-30T12:19:59.387 回答
0

我遇到了相反的问题:从列表中删除其他字符串的子字符串。这是我不太优雅的解决方案。

sub remove_substrings_from_list {
    my @list = @_;
    my @vals_without_superstrings;

    my %hash_of_others;
    for ( 0 .. $#list ) {
        my $a = shift @list;
        $hash_of_others{$a} = [ @list ];
        push @list, $a;
    }
    foreach my $k ( keys %hash_of_others ) {
        push @vals_without_superstrings, $k unless grep { index( $_, $k ) != -1 } @{ $hash_of_others{$k} };
    }
    return @vals_without_superstrings;
}
于 2019-05-25T18:03:08.467 回答