perl6正则表达式子规则和命名正则表达式比显式正则表达式慢很多; 如何使它们同样快速?

lis*_*tor 9 regex performance perl6

我有一个1608240行的数据文件.该文件是分段的.每个部分在开始行中都有一个唯一的单词,所有部分在该部分的最后一行中都有相同的单词"doneSection".

我试图通过执行以下操作来删除某些部分(由原始帖子中的@raiph重新格式化代码,以使代码更易于解释):

# using named subrules/regex is EXTREMELY slow;
# it reads about 2 lines per second, and grinds to halt
# after about 500 lines: (>> is the right word boundary)
perl6 -e 'my regex a { [ <{<iron copper carbon>.join("||")}> ] };
          my $x = 0;
          for "/tmp/DataRaw".IO.lines {
            $*ERR.print( "$x 1608240 \r" );
            ++$x;
            .say if m/:i beginSection \s+ <a> >>/ or
                    (m/:i \s+ <a> \s+ /
                     ff
                     m/:i doneSection/);
          }'

# however, if I explicitly write out the regex instead of using a subrule,
# it reads about 1000 lines per second, and it gets the job done:
perl6 -e 'my $x = 0;
          for "/tmp/DataRaw".IO.lines {
            $*ERR.print( "$x 1608240 \r" );
            ++$x;
            .say if m/:i beginSection \s+
                         [ iron || copper || carbon ] >>/ or
                    (m/:i \s+
                         [ iron || copper || carbon ] \s+ /
                     ff
                     m/:i doneSection/);
          }'
Run Code Online (Sandbox Code Playgroud)

我的问题是,如何使子规则与显式正则表达式一样快,或者至少不会停止?我更喜欢使用更高级别的抽象.这是一个正则表达式引擎内存问题吗?我也试过用:

my $a=rx/ [ <{ < iron copper carbon > .join("||") }> ] /
Run Code Online (Sandbox Code Playgroud)

它同样很慢.

我无法发布我的数据文件的160万行,但您可能会生成类似的文件用于测试目的.

谢谢你的任何提示.

rai*_*iph 6

问题不在于使用子规则/命名正则表达式.这是什么里面的正则表达式.它的:

[ <{<iron copper carbon>.join("||")}> ]
Run Code Online (Sandbox Code Playgroud)

VS

[ iron || copper || carbon ]
Run Code Online (Sandbox Code Playgroud)

以下应该消除速度差异.请尝试并评论您的结果:

my regex a { || < iron copper carbon > }
Run Code Online (Sandbox Code Playgroud)

注意前导空格< iron copper ...而不是<iron copper ...>.后者表示iron使用参数copper等调用的子规则.前者表示"引用词"列表文字,就像它在主要语言中一样(尽管主要语言中的前导空格是可选的).1

匹配器列表可以放在数组变量中:

my @matchers = < iron copper carbon >;
my regex a { || @matchers }
Run Code Online (Sandbox Code Playgroud)

匹配器@matchers可以是任意的正则表达式而不仅仅是字符串:

my @matchers = / i..n /, / cop+er /, / carbon /;
my regex a { || @matchers }
Run Code Online (Sandbox Code Playgroud)

警告:上面的工作,但在写这个答案时,我遇到了,并且现在高尔夫球问题,@数组插值不会回溯.

如何使子规则与显式正则表达式一样快

这不是明确的.这是关于涉及运行时评估的正则表达式插值.

在一般情况下,P6正则表达式都写在自己的正则表达式语言1在编译时编译默认.

但是P6正则表达式语言包括注入代码的能力,然后在运行时对其进行评估(前提是它没有危险).2

这可能很有用,但会产生运行时开销,有时可能很重要.

(也有可能你已经有了一些不好的大O算法peformance事情关系到你使用运行时的评价.如果是的话它会变得更糟不仅仅是运行时间内插,因为它是那么大O问题.我已经不要分析,因为根据我上面的代码,最好只使用完全编译的正则表达式.)

我也试过用:

my $a=rx/ [ <{ < iron copper carbon > .join("||") }> ] /
Run Code Online (Sandbox Code Playgroud)

这仍然无法避免运行时插值.这个结构:

<{ ...  }>
Run Code Online (Sandbox Code Playgroud)

通过在运行时评估大括号内的代码然后将其注入外部正则表达式进行插值.

脚注

1 P6"语言"实际上是一个交织在一起的DSL集合.

2除非你明确地写一个use MONKEY-SEE-NO-EVAL;(或只是use MONKEY;)编译采取了注入攻击的责任,包含注射串的正则表达式的插值,在编译时限制,以确保注入攻击是不可能的,P6会拒绝运行,如果它的代码是.您编写的代码不受攻击,因此编译器允许您像编写代码一样编写代码并编译代码而不会大惊小怪.