带有LR解析的置换短语


16

排列短语是对标准(E)BNF上下文无关语法定义的扩展:排列短语包含n个生成词(或等效地,非末尾词)A 1A n。在置换词组的位置,我们希望只看到一次所有这些产生式,但是我们对这些非末端的顺序不感兴趣。{一种1个一种ñ}ñ一种1个一种ñ

例如:

S <- X { A, B, C } Y

等效于:

S <- X  A B C  Y
S <- X  A C B  Y
S <- X  B A C  Y
S <- X  B C A  Y
S <- X  C A B  Y
S <- X  C B A  Y

该概念似乎在“用置换短语扩展上下文无关文法”中引入。其中还描述了如何使用LL(1)解析器在线性时间内解析这些短语。

论文“解析置换短语”描述了一种使用解析器组合器解析置换短语的方法。这是我发现的仅有两篇关于置换短语以及如何解析它们的论文。

看到我们可以使用基于LL(1)的解析器轻松解析这些置换短语,我的猜测是我们可以使用LR(1)样式解析器进行相同的处理。因此,我的问题是:

可以在保持合理大小的表的同时,使用LR(1)机制以线性的方式在输入字符串的大小中解析包含置换短语的语法吗?

排列短语并不能扩展上下文无关语言的功能:在上面的示例中,人们可以简单地列举所有可能的排列。但是,语法会爆炸,因为生成的语法的大小可能为。这样可以进行线性时间解析,但是语法的大小变得太大。Ø|G|

上面的方法适用于任何解析算法(尽管它没有用),所以也许我们可以针对特定算法做得更好。我们可以将爆炸率降低到“仅”指数(通过将短语编码到LR表中,2 | G |):我们可以让LR项目编码哪些产品尚未出现,从而减少对所有子集的爆破排列短语。Ø2|G|

尽管这样做更好,但它当然还不够好-排列短语为30个项目会使语法无法使用。LR解析的一部分仍未涉及,这是用于解析的实际基于堆栈的过程。我想将计数器存储在堆栈中也许可以解决该问题,但是我不确定该怎么做。

我当前正在实现一个解析器生成器,在问题域中,排列短语将是天赐之物。当我使用LR(1)机制时,出现了上述问题。


LR(1)解析的复杂性在没有排列短语的语法大小上已经是指数级的了-除非您对解析器实施“即时”计算,但是感觉更像是Earley解析器而不是正版LR(1)一本。
西尔万

2
关于其余问题:cstheory.stackexchange.com/questions/4962/…显示了置换CFG大小的指数下限,并且通过PDA的CFG的通常多项式构造,这意味着PDA的大小也是如此。
西尔万

1
我没有看过关于LL(1)的论文。实际上,实现的解析器不再是PDA。我仍然不相信“合理大小的表”的存在,因为可交换上下文无关语法的成员资格是NP完全的(请参阅例如dx.doi.org/10.3233/FI-1997-3112),但这是事实硬实例可能不是LR(1)。
西尔万

2
@Sylvain:您能否详细说明问题4962与这一问题的关系?在问题4962中,排列对于每个输入长度都是固定的,并且要排列的字符串会更改。在当前问题中,我们不解决排列问题。所以我看不到它们之间有任何真正的联系。
伊藤刚(Tsuyoshi Ito)

2
@Tsuyoshito Ito:在LR(1)中,首先构造与输入语法等效的DPDA,然后对字符串进行识别。由于存在一种线性大小的CFG,其中包含每种置换语言的置换短语,因此Yuval Filmus的论文(比他在cstheory上的答案更全面:请参阅cs.toronto.edu/~yuvalf/CFG-LB.pdf)显示没有这样的DPDA可以具有输入语法大小的多项式大小。
西尔万

Answers:


1

您是否考虑过将其转换为语义问题?代替针对非终结符{A,B,C}的所有排列的语法规则,只需使用一个规则来识别(A | B | C)^ 3以及特殊的内部代码,该代码可确保仅识别每个字符中的一个,否则将声明一个错误。我将在上述子句之前插入一个空生产,其减少将触发您用于计算A,B和C的任何内容的初始化,然后在其减少触发计数器检查并(如果需要)断言错误。(当然,如果语法通过A,B和/或C递归,则可能会有些棘手)


0

我认为没有人需要反击。本质上,您只检查所有排列但会中断

伪代码:

perm-match(input, pattern)
     if pattern = nil return true

     foreach(rule in pattern)
         if (match(input, rule))
             perm-match(input - matchedpart, pattern - rule)
             break
         end
     end
     return false
end

这是一个更具体的例子

假设我们尝试匹配abcd的任何排列,并且我们的字符串为bcda

  • 步骤1:找到第一个匹配的符号。在这种情况下是b
  • 步骤2:从模式中删除该符号并减少字符串:例如,剩下acd和cda
  • 步骤3:对新字符串重复步骤1
    • c中cda中的匹配项,这给我们留下了ad和da
    • da中的匹配项使d和d
    • d与d中的匹配项使两个字符串都为nil

因此,您看到这种简单的算法可以通过简单地比较“字符串”来轻松地检查排列。请注意,函数的复杂度为O(n!)最差情况,O(1)最佳情况。从某种意义上说,我们通过将要匹配的符号存储在数组中来保持计数。我认为这通常是“快速的”,因为在大多数情况下不会处理非常大的n。


2
ññ=50
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.