由于子字符串问题,这将非常困难。最初,我正在考虑对我的列表进行排序,cat
并且catfish
会彼此相邻,但请查看此列表::
bug
bear
calf
catbug
catbear
对此列表进行排序将无济于事。另外,这个呢?
concatenate
cat
bear
bug
我要离开cat
吗?它已经在这个词了concatenate
?
那这个呢:
cat
concatenate
bear
bug
在这种情况下,单词cat和concatenate都在列表中,因为cat在concatenate之前是列表中的第一个。由于没有单词已经是concatenate的一部分,因此它进入了列表。
除非我需要同时检查两种方式。我要添加到列表中的单词是否已经在列表中,并且是列表中已经包含在我正在查看的单词中的单词。
这不仅是一个定义不明确的问题,而且是一个乱七八糟的代码。编码实际上很简单,但它最终生成了一个 O 2类型的算法。这意味着列表大小翻倍会导致处理时间增加四倍。如果我能在一秒钟内处理 100 个单词,我需要 4 秒来完成 200 个单词,8 秒来完成 400 个单词,16 秒来完成 800 个单词。差不多20秒做1000字。
这是使用您的定义,其中顺序很重要。也就是说,如果cat
来之前catbug
,两者都在您的批准列表中,但如果catbug
来之前cat
,则cat
不会进入列表:
#! /usr/bin/env perl
#
use strict;
use warnings;
use autodie;
use feature qw(say);
use Data::Dumper;
use constant {
LIST_FILE => "text.txt",
};
open my $list_fh, "<", LIST_FILE;
my @approved_list;
while ( my $new_word = <list_fh> ) {
chomp $new_word;
my $new_word_in_list = 0;
for my $word_already_in_list ( @approved_list ) {
if ( $word_already_in_list =~ /\Q$new_word\E/ ) {
# Word is already in the list or in a word in the list
$new_word_in_list = 1;
last;
}
}
if ( not $new_word_in_list ) {
push @approved_list, $new_word;
}
}
say Dumper \@approved_list;
冥想
我之前意识到我可以使用grep
而不是内部循环:
#! /usr/bin/env perl
#
use strict;
use warnings;
use autodie;
use feature qw(say);
use Data::Dumper;
use constant {
LIST_FILE => "text.txt",
};
open my $list_fh, "<", LIST_FILE;
my @approved_list;
while ( my $new_word = <$list_fh> ) {
chomp $new_word;
if ( not grep { /\Q$new_word\E/ } @approved_list ) {
push @approved_list, $new_word;
}
}
say Dumper \@approved_list
该程序看起来更短,似乎只需要一个循环,但grep
隐藏了内部循环。为了使 grep 工作,它仍然需要遍历数组中的每一个条目。这就是为什么我决定不使用grep
,而是让内部循环更加明确。
但是,如果我可以使用字符串而不是数组来保留单词,并且我用某个字符分隔单词,我可以保证它不在单词中,该怎么办?也许我可以在字符串上使用正则表达式。这样会更有效率吗?
#! /usr/bin/env perl
#
use strict;
use warnings;
use autodie;
use feature qw(say);
use Data::Dumper;
use constant {
LIST_FILE => "text.txt",
};
open my $list_fh, "<", LIST_FILE;
my $approved_list = "";
while ( my $new_word = <$list_fh> ) {
chomp $new_word;
if ( not $approved_list =~ /\Q$new_word\E/ ) {
$approved_list = ( $approved_list ) ? "$approved_list\0$new_word" : $new_word;
}
}
say Dumper split /\0/, $approved_list;
在上面,我将批准的单词列表放在一个名为$approved_list
. 我假设单词不包含该字符,将单词与NUL 字符NUL
分开。现在,我可以用新词 grep 标量。如果它还没有在 中$approved_list
,我会在它前面附加NUL
字符 ( \0
)。我稍后可以拆分NUL
以再次返回列表。
使用正则表达式会更快吗?如果我批准的列表包含 1000 个单词,平均每个单词 5 个字符(可能更长,因为较长的单词比较短的单词更有可能)。那是一个 6000 个字符的字符串,我正在做一个正则表达式。这样更有效率吗?很难说。
有三种解决方案:
- 第一个使用隐式内部循环。
- 第二个用于
grep
隐藏内部循环。
- 第三个将单词列表串成一个由一个字符分隔的字符串,我绝对确定不在字符串中。(我的钱在
NUL
)。
唯一的判断方法是在所有三个上使用Benchmark::Timer之类的东西,看看哪个最有效——这可能会根据列表大小、单词等而改变。