在Perl中计算字符串中单词数的最快方法是什么?

Eli*_*Eli 7 perl

我有一些函数,我在各种文本上运行了一百多万次,这意味着这些函数的小改进转化为总体上的巨大收益.目前,我注意到我所有涉及字数的函数都比其他所有函数都要花费更长的时间,所以我想我想尝试以不同的方式进行字数统计.

基本上,我的函数所做的是获取一些具有与之关联的文本的对象,验证该文本与某些模式不匹配,然后计算该文本中的单词数.该功能的基本版本是:

my $num_words = 0;
for (my $i=$begin_pos; $i<=$end_pos; $i++) {
   my $text = $self->_getTextFromNode($i);
   #If it looks like a node full of bogus text, or just a number, remove it.
   if ($text =~ /^\s*\<.*\>\s*$/ && $begin_pos == $end_pos) { return 0; }
   if ($text =~ /^\s*(?:Page\s*\d+)|http/i && $begin_pos == $end_pos) { return 0; }
   if ($text =~ /^\s*\d+\s*$/ && $begin_pos == $end_pos) { return 0; }
   my @text_words = split(/\s+/, $text);
   $num_words += scalar(@text_words);
   if ($num_words > 30) { return 30; }
}
return $num_words;
}
Run Code Online (Sandbox Code Playgroud)

我正在进行大量的文本比较,类似于我在其他地方在我的代码中所做的,所以我猜我的问题必须与我的字数一样.有没有比分裂更快的方法\s+呢?如果是这样,它是什么,为什么它更快(所以我可以理解我做错了什么,并可以将这些知识应用于以后的类似问题).

Eri*_*rom 12

使用带正则表达式的while循环是我发现计算单词的最快方法:

my $text = 'asdf asdf asdf asdf asdf';

sub count_array {
   my @text_words = split(/\s+/, $text);
   scalar(@text_words);
}

sub count_list {
    my $x =()= $text =~ /\S+/g;       #/
}

sub count_while {
    my $num; 
    $num++ while $text =~ /\S+/g;     #/
    $num
}

say count_array; # 5
say count_list;  # 5
say count_while; # 5

use Benchmark 'cmpthese';

cmpthese -2 => {
    array => \&count_array,
    list  => \&count_list,
    while => \&count_while,
}

#          Rate  list array while
# list  303674/s    --  -22%  -55%
# array 389212/s   28%    --  -42%
# while 675295/s  122%   74%    --
Run Code Online (Sandbox Code Playgroud)

while循环更快,因为不需要为每个找到的单词分配内存.正则表达式也在布尔上下文中,这意味着它不需要从字符串中提取实际匹配.


hex*_*der 5

如果单词仅用单个空格分隔,则计数空间很快。

sub count1
{
    my $str = shift;
    return 1 + ($str =~ tr{ }{ });
}
Run Code Online (Sandbox Code Playgroud)

更新基准:

my $text = 'asdf asdf asdf asdf asdf';

sub count_array {
   my @text_words = split(/\s+/, $text);
   scalar(@text_words);
}

sub count_list {
   my $x =()= $text =~ /\S+/g;       #/
}

sub count_while {
   my $num; 
   $num++ while $text =~ /\S+/g;     #/
   $num
}

sub count_tr {
    1 + ($text =~ tr{ }{ });
}

say count_array; # 5
say count_list;  # 5
say count_while; # 5
say count_tr; # 5

use Benchmark 'cmpthese';

cmpthese -2 => {
    array => \&count_array,
    list  => \&count_list,
    while => \&count_while,
    tr    => \&count_tr,
}

#            Rate  list while array    tr
# list   220911/s    --  -24%  -44%  -94%
# while  291225/s   32%    --  -26%  -92%
# array  391769/s   77%   35%    --  -89%
# tr    3720197/s 1584% 1177%  850%    --
Run Code Online (Sandbox Code Playgroud)