使用kleene星从正则表达式构造DFA时的模糊性

时间:2014-10-06 12:48:24

标签: regex regular-language dfa automata

如何从[ab]*ab等正则表达式直接构建DFA时,如何避免歧义?

在一个简单的实现中,[ab]*的周期转换吃掉了所有a或b,它当然不同于正则表达式的现有实现,它们也考虑了最后的“ab”。因此,实现自动机必须以某种方式知道何时从第一个转换周期跳转到最后两个转换“a到b”。

任何想法如何实现这一目标?

我对直接DFA构建的答案感兴趣,而不是对转换为DFA的NFA感兴趣。 Adrian McCarthy's answer可能是重点:

  

“简单的回答是自动机只有一个状态   字面意思是它还不知道采取了哪条路径。“

- 但没有详细说明。

1 个答案:

答案 0 :(得分:2)

使用http://hackingoff.com/compilers/regular-expression-to-nfa-dfa 使用正则表达式字符串:

    (a|b)*ab

NFA生成的DFA有三种状态:

    S0: initial state
    S1: after an 'a'
    S2: after a 'b' that doesn't follow an a (follows a 'b' or is initial 'b')
    S3: after a 'b' in S1
    S3: accept

正则表达式的现有实现通常允许反向引用(不是RE2),并使用回溯而不是NFA - > DFA,因此不是DFA(回溯逻辑添加状态,类似于递归调用堆栈添加状态)。如果你不想使用回溯,解析这样的表达式的一种方法可能是从接受状态向后退出:

    ab
    S0 -a-> S1 -b-> S2 (accept)

    [ab]*ab
    S0 -a-> S1 -b-> S2 (accept)
    Sn -a-> S1
    S0,S2 -b-> S3

NFA减少允许看到'a'的情况不同(因为它是现有最终序列的部分匹配),我们应该转到状态S1。上面的相同链接为更长的字符串提供了大于必要的DFA图(它们的状态2和3应该组合成单个状态,任何'b'或'c'保持在状态,'a'到状态1):

    (a|b|c)*abc

要自己创建DFA,您可以先为abc生成原始DFA:

    S0 -a-> S1 -b-> S2 -c-> S3 (accept)

然后填写与此曲目不匹配的内容(前一个最高优先级,覆盖下一个内容):

    S0 -a-> S1 -b-> S2 -c-> S3 (accept)
    Sn -a-> S1            (get on our track of final string / ambiguity state)
    Sn -b,c-> S4          (b or c; get off our track)

这填写为:

    S0 -a-> S1 -b-> S2 -c-> S3 (accept)
    S1,S2,S3,S4 -a-> S1
    S0,S3,S4 -b,c-> S4
    S1 -c-> S4
    S2 -b-> S4

如果我们改为:

    (a|c)*aac

你应该想到类似于abc的接受轨道:

    S0 -a-> S1 -a-> S2 -c-> S3   (accept)

但也要注意序列'aa'。模糊性仍然相对简单,因为看到任何这些都应该让你处于状态S2:

    aa   aaa    aaaa    aaaaa...a

含糊不清的是:

  1. 'a'是我们接受轨道的第一个'a'吗?
  2. 'aa'中的第二个'a'是我们接受轨道的第一个还是第二个'a'?
  3. 我们可以从轨道中分层填写状态,但这次歧义也与S2重叠:

        S0 -a-> S1 -a-> S2 -c-> S3 (accept)
        S2 -a-> S2            (additional here, the second ambiguity)
        Sn -a-> S1            (get on our track of final string / ambiguity state)
        Sn -c-> S4            (get off our track)
    

    这填写为:

        S0 -a-> S1 -a-> S2 -c-> S3 (accept)
        S2 -a-> S2
        S3,S4 -a-> S1
        S0,S1,S3,S4 -c-> S4
    

    我的一般观察(作为教授编译器设计的人):

    • 对于简单的正则表达式,通常有一种方法可以理解重叠的状态 来自NFA的DFA
    • 对于较小的情况,您可以创建简单的机制
    • 任何较大的案例仍然需要NFA - > DFA处理

    除了简单的情况,要使用反向引用分组并保持这种状态,你不再谈论简单的DFA了。它仍然可以转换为DFA,但是如果你没有回溯(不是标准DFA的一部分,回溯逻辑会增加额外的状态),你的状态空间将呈指数级大,并且没有一般的简单方法可以直接转换为DFA,你应该首先构建NFA。

    请参阅How do backreferences in regexes make backtracking required?