String 在perl中增加字符串的最快方法是什么?
我想以一种快速的方式在循环中附加一个perl字符串,而不必为每次迭代复制字符串。我想从Java或C#中寻找类似StringBuilder的东西 目前,为了实现“a+=b”,我考虑了以下备选方案String 在perl中增加字符串的最快方法是什么?,string,perl,String,Perl,我想以一种快速的方式在循环中附加一个perl字符串,而不必为每次迭代复制字符串。我想从Java或C#中寻找类似StringBuilder的东西 目前,为了实现“a+=b”,我考虑了以下备选方案 a.=b#concat a=连接(“”,a,b)#加入 推动@a,b#阵列推动 我对将所有字符串复制到另一个字符串不感兴趣。我每次需要复制一个字符,或者在每次迭代中附加小字符串。我试图解决以下问题:将输入字符串“aaabbcc”压缩为“3a2b3c”。因此,我们的想法是迭代输入字符串,检查有多少重复字符,
我试图解决的问题。不过我有点不同。我已经用几种方法执行了以下基准测试:
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw(cmpthese);
my $dna;
$dna .= [qw(G A T C)]->[rand 4] for 1 .. 10000;
sub frequency_concat {
my $result = '';
for my $idx (0 .. length($dna) - 1) {
$result .= substr($dna, $idx, 1);
}
return $result;
}
sub frequency_join {
my $result = '';
for my $idx (0 .. length($dna) - 1) {
$result = join '', $result, substr($dna,$idx,1);
}
return $result;
}
sub frequency_list_push {
my @result = ();
for my $idx (0 .. length($dna) - 1) {
push @result, substr($dna,$idx,1);
}
return join '', @result;
}
sub frequency_list_prealloc {
my @result = (' ' x length($dna));
for my $idx (0 .. length($dna) - 1) {
$result[$idx] = substr($dna,$idx,1);
}
return join '', @result;
}
cmpthese(-1, # Run each for at least 1 second(s) {
concat => \&frequency_concat,
join => \&frequency_join,
list_push => \&frequency_list_push,
list_list_prealloc => \&frequency_list_prealloc
}
);
下面的结果表明,concat(a.b)是最快的操作。我不明白为什么,因为这需要制作多个字符串副本
Rate join list_push list_list_prealloc concat
join 213/s -- -38% -41% -74%
list_push 342/s 60% -- -5% -58%
list_list_prealloc 359/s 68% 5% -- -56%
concat 822/s 285% 140% 129% --
为了比较,我尝试测试不同的版本来解决压缩字符串的实际问题。这是我的测试脚本
test.pl
:
use strict;
use warnings;
use Benchmark qw(cmpthese);
use Inline C => './compress_c.c';
my $str_len = 10000;
my @chars = qw(a b c d);
my $str;
$str .= [@chars]->[rand 4] for 1 .. $str_len;
cmpthese(
-1,
{
compress_array => sub { compress_array( $str ) },
compress_regex => sub { compress_regex( $str ) },
compress_str => sub { compress_str( $str ) },
compress_c => sub { compress_c( $str ) },
}
);
# Suggested by @melpomene in the comments
sub compress_regex {
return $_[0] =~ s/([a-z])\1+/($+[0] - $-[0]) . $1/egr;
}
sub compress_array {
my $result = '';
my @chrs = split //, $_[0];
my $prev = $chrs[0];
my $count = 1;
my @result;
for my $i ( 1..$#chrs ) {
my $char = $chrs[$i];
if ( $prev eq $char ) {
$count++;
next if $i < $#chrs;
}
if ( $count > 1) {
push @result, $count, $prev;
}
else {
push @result, $prev;
}
if ( ( $i == $#chrs ) and ( $prev ne $char ) ) {
push @result, $char;
last;
}
$count = 1;
$prev = $char;
}
return join '', @result;
}
sub compress_str {
my $result = '';
my $prev = substr $_[0], 0, 1;
my $count = 1;
my $lastind = (length $_[0]) - 1;
for my $i (1 .. $lastind) {
my $char = substr $_[0], $i, 1;
if ( $prev eq $char ) {
$count++;
next if $i < $lastind;
}
if ( $count > 1) {
$result .= $count;
}
$result .= $prev;
if ( ( $i == $lastind ) and ( $prev ne $char ) ) {
$result .= $char;
last;
}
$count = 1;
$prev = $char;
}
return $result;
}
Rate compress_array compress_str compress_regex compress_c
compress_array 311/s -- -42% -45% -99%
compress_str 533/s 71% -- -6% -98%
compress_regex 570/s 83% 7% -- -98%
compress_c 30632/s 9746% 5644% 5273% --
运行perl test.pl
的结果:
use strict;
use warnings;
use Benchmark qw(cmpthese);
use Inline C => './compress_c.c';
my $str_len = 10000;
my @chars = qw(a b c d);
my $str;
$str .= [@chars]->[rand 4] for 1 .. $str_len;
cmpthese(
-1,
{
compress_array => sub { compress_array( $str ) },
compress_regex => sub { compress_regex( $str ) },
compress_str => sub { compress_str( $str ) },
compress_c => sub { compress_c( $str ) },
}
);
# Suggested by @melpomene in the comments
sub compress_regex {
return $_[0] =~ s/([a-z])\1+/($+[0] - $-[0]) . $1/egr;
}
sub compress_array {
my $result = '';
my @chrs = split //, $_[0];
my $prev = $chrs[0];
my $count = 1;
my @result;
for my $i ( 1..$#chrs ) {
my $char = $chrs[$i];
if ( $prev eq $char ) {
$count++;
next if $i < $#chrs;
}
if ( $count > 1) {
push @result, $count, $prev;
}
else {
push @result, $prev;
}
if ( ( $i == $#chrs ) and ( $prev ne $char ) ) {
push @result, $char;
last;
}
$count = 1;
$prev = $char;
}
return join '', @result;
}
sub compress_str {
my $result = '';
my $prev = substr $_[0], 0, 1;
my $count = 1;
my $lastind = (length $_[0]) - 1;
for my $i (1 .. $lastind) {
my $char = substr $_[0], $i, 1;
if ( $prev eq $char ) {
$count++;
next if $i < $lastind;
}
if ( $count > 1) {
$result .= $count;
}
$result .= $prev;
if ( ( $i == $lastind ) and ( $prev ne $char ) ) {
$result .= $char;
last;
}
$count = 1;
$prev = $char;
}
return $result;
}
Rate compress_array compress_str compress_regex compress_c
compress_array 311/s -- -42% -45% -99%
compress_str 533/s 71% -- -6% -98%
compress_regex 570/s 83% 7% -- -98%
compress_c 30632/s 9746% 5644% 5273% --
这表明正则表达式版本比字符串版本稍快。然而,C版本是最快的,大约是正则表达式版本的50倍
注意:我在我的Ubuntu 16.10笔记本电脑(英特尔Core i7-7500U CPU@2.70GHz)上测试了这一点。您的“列表预分配”案例没有预分配阵列。它只分配一个元素,这是一个长字符串。IMHO可以更好地对整个问题进行基准测试-例如,计算字符串中某个字符的出现次数。我在我的Ubuntu 16.10笔记本电脑(Intel Core i7-7500U CPU@2.70GHz)上测试了这一点,并得到了类似的结果。我删除了对
substr
的调用,取而代之的是一个常量1字符字符串。现在,concat
速度最快,为3229/s。我还实现了一个带有预分配(使用)的C版本,其速度大约是concat
版本(284350/s)的90倍。我删除substr
调用的原因是为了能够更容易地将C版本与Perl版本进行比较x长度($dna)是一个字符串。我已经用('')x长度($dna)修复了它,这将预先分配数组。基准测试结果的顺序相同。您是否考虑过s/([a-z])\1+/($+[0]-$-[0])$1/eg
?如果你有aabbaa
你会把它压缩到2a2b2a
还是4a2b
?如果我有aabbaa,那么它应该压缩到2a2b2a。好:)如果你有单字符abb
你会使用1还是什么都不用,即1a2b
或a2b
?如果你有单字符,什么都不用。所以压缩('aaabcddd')='3abc3d'。)@ikegami感谢您的编辑和评论!我想知道如何摆脱malloc
调用,这样我就不必分配两次缓冲区了。我有一些问题:1。当我们使用SvPVbyte(str\u sv,len)
时,是否需要SvGETMAGIC(str\u sv)
?根据SvPV()。2.为什么在SvGETMAGIC()
之后加括号的块?我看不出有任何理由在这里添加本地作用域。3.“删除Unicode错误”(答案修订历史记录中的注释)是什么意思?。。。4.注释“修复了for
的内部的编译错误(int
)”。对于C99中的
循环规范,在中声明变量应该是有效的,请参阅。我已经为我试图解决的原始问题提供了基准测试。这是一个稍微不同的问题。不过我得到了同样的结果。哎呀,关于SvPVbyte
你说得对,你已经在做SvGETMAGIC
。2.根据C的版本,不能混合声明和代码。Var声明只能在一个块的开头或另一个Var声明之后找到。3.我(有效地)用SvPVbytes替换了SvPV。如果不单独检查UTF8
标志,则无法使用SvPV。4.将gcc
切换到C99模式需要一个在构建myperl
时未使用的开关。