在 Perl 中,我想从数组中删除所有元素,其中同一数组的另一个元素是所述元素的非空子字符串。
说我有数组
@itemlist = ("abcde", "ab", "khi", "jklm");
在这种情况下,我希望"abcde"
删除该元素,因为"ab"
它是"abcde"
.
我可以制作数组的副本(也许作为哈希?),对其进行迭代,尝试使用原始数组的每个元素进行索引并将其删除,但必须有一种更优雅的方式,不是吗?
谢谢你的帮助!
为清楚起见进行了一些编辑。
您可以从所有项目构建一个正则表达式并丢弃任何匹配的内容:
$alternation = join('|', map(quotemeta, @itemlist));
@itemlist = grep !/($alternation).|.($alternation)/, @itemlist;
事情只是确保一个项目与().|.()
自己不匹配。
好吧,我不会称之为优雅,但这里有:
#!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 的答案可能是我在现实世界中更喜欢的答案。尽管如此,使用智能匹配可以做一些奇怪的事情还是很有趣的。
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 只有两个.
您可以使用哈希来计算所有单词的子字符串。列表中计数高于一个的任何单词都是另一个单词的子字符串。在此示例中,子字符串的最小长度为 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
以下将从数组中删除子字符串。
#!/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";
}
}
我遇到了相反的问题:从列表中删除其他字符串的子字符串。这是我不太优雅的解决方案。
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;
}