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

问题描述 投票:9回答:1

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

我试图通过执行以下操作来删除某些部分(来自original post的@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/);
          }'

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

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

它同样很慢。

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

谢谢你的任何提示。

regex performance perl6
1个回答
6
投票

问题不在于使用子规则/命名正则表达式。这就是正则表达式中的内容。它的:

[ <{<iron copper carbon>.join("||")}> ]

VS

[ iron || copper || carbon ]

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

my regex a { || < iron copper carbon > }

注意< iron copper ...中的前导空格而不是<iron copper ...>。后者意味着一个名为iron的子规则,其参数为copper等。前者意味着“引用词”列表文字只是as it does in the main language(尽管主要语言中的前导空格是可选的).1

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

my @matchers = < iron copper carbon >;
my regex a { || @matchers }

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

my @matchers = / i..n /, / cop+er /, / carbon /;
my regex a { || @matchers }

警告:以上工作,但在写这个答案时,我遇到了,现在高尔夫球问题@ symbol'd array interpolation doesn't backtrack

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

这不是明确的。这是关于regex interpolation涉及运行时评估。

通常,P6正则表达式是用它们自己的正则表达式语言1编写的,默认情况下在编译时编译。

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

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

(也有可能你有一些与你使用运行时评估相关的糟糕的Big O算法性能。如果是这样,它变得比运行时插值更糟糕,因为它是一个大O问题。我没有打算分析一下,因为最好只按照上面的代码使用完全编译的正则表达式。)

我也试过用:

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

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

<{ ...  }>

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

Footnotes

1 P6“语言”实际上是DSLs的交织集合。

2除非你明确写use MONKEY-SEE-NO-EVAL;(或只是use MONKEY;pragma来承担注入攻击的责任,否则包含注入字符串的正则表达式的插值在编译时是有限的,以确保注入攻击是不可能的,P6将拒绝运行代码如果是。您编写的代码不受攻击,因此编译器允许您像编写代码一样编写代码并编译代码而不会大惊小怪。

© www.soinside.com 2019 - 2024. All rights reserved.