我一直想知道是否有一种更好,更简洁的方法将字符串拆分为字符
@characters = split //, $string
Run Code Online (Sandbox Code Playgroud)
并不难读,但不知何故,使用正则表达式对我来说太过分了.
我想出了这个:
@characters = map { substr $string, $_, 1 } 0 .. length($string) - 1
Run Code Online (Sandbox Code Playgroud)
但我发现它更丑陋且不太可读.将字符串拆分为字符的首选方法是什么?
我认为看一下在每个角色上分割字符串的方法有多快可能是个好主意.
我对我的计算机上碰巧遇到的几个Perl版本进行了测试.
use 5.010;
use Benchmark qw(:all) ;
my %bench = (
'split' => sub{
state $string = 'x' x 1000;
my @chars = split //, $string;
\@chars;
},
'split-string' => sub{
state $string = 'x' x 1000;
my @chars = split '', $string;
\@chars;
},
'split-capture' => sub{
state $string = 'x' x 1000;
my @chars = split /(.)/, $string;
\@chars;
},
'unpack' => sub{
state $string = 'x' x 1000;
my @chars = unpack( '(a)*', $string );
\@chars;
},
'match' => sub{
state $string = 'x' x 1000;
my @chars = $string =~ /./gs;
\@chars;
},
'match-capture' => sub{
state $string = 'x' x 1000;
my @chars = $string =~ /(.)/gs;
\@chars;
},
'map-substr' => sub{
state $string = 'x' x 1000;
my @chars = map { substr $string, $_, 1 } 0 .. length($string) - 1;
\@chars;
},
);
# set the initial state of $string
$_->() for values %bench;
cmpthese( -10, \%bench );
Run Code Online (Sandbox Code Playgroud)
for perl in /usr/bin/perl /opt/perl-5.10.1/bin/perl /opt/perl-5.11.2/bin/perl;
do
$perl -v | perl -nlE'if( /(v5\.\d+\.\d+)/ ){
say "## Perl $1";
say "<pre>";
last;
}';
$perl test.pl;
echo -e '</pre>\n';
done
Run Code Online (Sandbox Code Playgroud)
Rate split-capture match-capture map-substr match unpack split split-string
split-capture 296/s -- -20% -20% -23% -58% -63% -63%
match-capture 368/s 24% -- -0% -4% -48% -54% -54%
map-substr 370/s 25% 0% -- -3% -48% -53% -54%
match 382/s 29% 4% 3% -- -46% -52% -52%
unpack 709/s 140% 93% 92% 86% -- -11% -11%
split 793/s 168% 115% 114% 107% 12% -- -0%
split-string 795/s 169% 116% 115% 108% 12% 0% --
Rate split-capture map-substr match-capture match unpack split split-string
split-capture 301/s -- -31% -41% -47% -60% -65% -66%
map-substr 435/s 45% -- -14% -23% -42% -50% -50%
match-capture 506/s 68% 16% -- -10% -32% -42% -42%
match 565/s 88% 30% 12% -- -24% -35% -35%
unpack 743/s 147% 71% 47% 32% -- -15% -15%
split 869/s 189% 100% 72% 54% 17% -- -1%
split-string 875/s 191% 101% 73% 55% 18% 1% --
Rate split-capture match-capture match map-substr unpack split-string split
split-capture 300/s -- -28% -32% -38% -59% -63% -63%
match-capture 420/s 40% -- -5% -13% -42% -48% -49%
match 441/s 47% 5% -- -9% -39% -46% -46%
map-substr 482/s 60% 15% 9% -- -34% -41% -41%
unpack 727/s 142% 73% 65% 51% -- -10% -11%
split-string 811/s 170% 93% 84% 68% 12% -- -1%
split 816/s 171% 94% 85% 69% 12% 1% --
正如你所看到的那样,split是最快的,因为这是代码中的特殊情况split.
split-capture是最慢的,可能是因为它必须设置$1,以及其他几个匹配变量.
所以我建议选择普通的旧版本split //, ...,或大致相同的版本split '', ....
为什么使用正则表达式会"过度杀伤"?许多人担心Perl中的正则表达式过度,因为他们认为运行它们涉及高度复杂和缓慢的正则表达式算法.这并非总是如此:实现是高度优化的,并且特别处理了许多简单的情况:看起来像正则表达式实际上可以执行的操作与简单的子字符串搜索一样.如果这种类型的split优化,我也不会感到惊讶.split 是比你更快的map在一些测试中,我跑.unpack似乎比...略快split.
我推荐,split因为它是"惯用"的方式.你可以在perldoc,很多书中找到它,任何优秀的Perl程序员都应该知道它(如果你不确定你的读者会理解它,你总是可以像有人建议的那样在代码中添加注释.)
OTOH,如果正则表达式"过度杀伤"只是因为语法难看,那么对我来说这太过主观了.;-)
它比使用split函数分割字符串要清晰得多.我想你可以说零点模式是不直观的; 虽然我觉得很清楚.如果你想要一个"干净"的替代方案将它包装在一个sub中:
my @characters = chars($string);
sub chars { split //, $_[0] }
Run Code Online (Sandbox Code Playgroud)
对于不太可读和更简洁(仍然使用正则表达式矫枉过正):
@characters = $string =~ /./g;
Run Code Online (Sandbox Code Playgroud)
(我从打代码高尔夫中学到了这个习语.)