在Perl中,我想从数组中删除所有元素,其中同一数组的另一个元素是所述元素的非空子字符串。
说我有阵列
@itemlist = ("abcde", "ab", "khi", "jklm");
在这种情况下,我希望删除元素"abcde"
,因为"ab"
是"abcde"
的子字符串。
我可以制作一个数组的副本(可能是一个哈希?),迭代它,尝试索引原始数组的每个元素并删除它,但是必须有一个更优雅的方式,不是吗? / p>
感谢您的帮助!
为了清晰起见而进行了编辑。
答案 0 :(得分:3)
你可以从所有项目中构造一个正则表达式并抛弃任何匹配的东西:
$alternation = join('|', map(quotemeta, @itemlist));
@itemlist = grep !/($alternation).|.($alternation)/, @itemlist;
().|.()
只会确保项目与自身不匹配。
答案 1 :(得分: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";
它依赖于smart match的一个相当模糊的行为:如果左参数是一个数组而右参数是一个sub,它会为每个元素调用sub,并且只有当sub返回时,最终结果才为真每个元素都为true。
说明:对于数组的每个元素,它检查没有其他元素是该元素的子字符串(至少需要一个附加字符,以便元素本身不匹配)。
注意: wdebeaum的答案可能是我在现实世界中更喜欢的答案。尽管如此,聪明的比赛可以做一些奇怪的事情。
答案 2 :(得分: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只有两个。
答案 3 :(得分:0)
您可以使用哈希来计算所有单词的子字符串。列表中任何数字大于1的单词都是另一个单词的子字符串。在此示例中,子串的最小长度为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
答案 4 :(得分: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";
}
}
答案 5 :(得分: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;
}