在速记键盘上,有键STKPWHRAO*EUFRPBLGTSDZ
。用户按下几个键,然后在提起时一次性注册键。它类似于在钢琴上弹奏和弦。示例笔划为KAT
,TPHOEUGT
。
我有一个正则表达式,用于测试有效的steno和弦。它可以是任意数量的这些键,但它们必须按顺序排列。我的解决方案是qr/S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?/
,但由于此正则表达式被调用数百次,因此可变长度可能是速度瓶颈。由于所有?
对此有更快的正则表达式方法吗?如果密钥出现故障,我需要正则表达式失败。
答案 0 :(得分:5)
要检查字符串是否是有效的和弦,您实际上需要
/^(?=.)S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s
一个简单的优化就是确保匹配是可能的。
/^(?=[STKPWHRAO*EUFBLGDZ])S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s
下一步是消除回溯。那是时间的流逝。
/
^
(?=[STKPWHRAO*EUFBLGDZ])
S?+ T?+ K?+ P?+ W?+ H?+ R?+ A?+ O?+ \*?+ E?+
U?+ F?+ R?+ P?+ B?+ L?+ G?+ T?+ S?+ D?+ Z?+
\z
/x
幸运的是,即使S
,T
,P
和R
出现两次,也可以毫无困难地完全消除回溯。这实际上几乎没有匹配的时间。
如果即使这还不够快,下一步就是编写一个专门的C函数。启动正则表达式匹配引擎是昂贵的,并且可以通过简单的功能完全避免。
请注意,上述优化仅在模式不匹配时才有用。当模式匹配时,它们应该是中性的。另一方面,C函数即使在模式匹配时也会有所帮助。
基准:
use strict;
use warnings;
use feature qw( say );
use Benchmark qw( cmpthese );
my %tests = (
orig => q{ $s =~ /^(?=.)S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s},
new => q{ $s =~
/
^
(?=[STKPWHRAO*EUFBLGDZ])
S?+ T?+ K?+ P?+ W?+ H?+ R?+ A?+ O?+ \*?+ E?+
U?+ F?+ R?+ P?+ B?+ L?+ G?+ T?+ S?+ D?+ Z?+
\z
/x
},
);
$_ = 'use strict; use warnings; our $s; ' . $_
for values %tests;
{ say "Matching:"; local our $s = "STAODZ"; cmpthese(-3, \%tests); }
{ say "Not matching:"; local our $s = "STPRSTPR"; cmpthese(-3, \%tests); }
输出:
Matching:
Rate new orig
new 509020/s -- -29%
orig 712274/s 40% --
Not matching:
Rate orig new
orig 158758/s -- -73%
new 579851/s 265% --
这意味着什么 匹配从1.40μs减慢到1.96μs(在这种情况下),和 非匹配速度从6.30μs到1.72μs(在这种情况下)。
要检查字符串是否是一系列有效和弦,您只需要
/^[STKPWHRAO*EUFBLGDZ]+\z/
如果你想提取一个字符串中的所有和弦,我首先要提取下面匹配的序列,然后在提取的序列中找到和弦:
/([STKPWHRAO*EUFBLGDZ]+)/
答案 1 :(得分:0)
可变长度可能是速度瓶颈
你不应该那样工作
首先,编写并调试您的程序
然后,如果它的速度不够快, profile 你的程序找到瓶颈的地方
然后优化瓶颈
为了善良,不要花费多少时间来猜测瓶颈在哪里并在代码完成之前优化它们,因为你很可能会发现你猜错了并且浪费了很多时间
在任何情况下,正则表达式引擎都是用C语言编写的,速度非常快。我非常怀疑你所写的短模式是否需要花费大量时间来测试
由于所有
,正则表达式中的每一步都是越来越大的可能性?
这也不是真的。在正则表达式中的每个点上只有一个要测试的字符。字符串中的下一个字符要么匹配,要么不匹配。两者都很好,正则表达式引擎只是进入模式的下一步。无论要匹配的字符串如何,匹配过程都将保持不变。