LISP中的NFA识别器

LISP中的NFA识别器,lisp,common-lisp,automata,finite-automata,lispworks,Lisp,Common Lisp,Automata,Finite Automata,Lispworks,我必须在lisp中定义一个函数,给定一个正则表达式和一个e-NFA作为输入,如果该表达式被自动机接受,它将返回true 首先,我定义了一个函数,该函数使用以下运算符{|,*,+,…}从正则表达式生成e-NFA(作为cons单元格) 例如:使用表达式(或a b),输出将为: ((INITIAL 0) (DELTA 0 EPSILON 1) (DELTA 0 EPSILON 3) (DELTA 2 EPSILON 5) (DELTA 4 EPSILON 5) (DELTA 1 A 2) (DELTA

我必须在lisp中定义一个函数,给定一个正则表达式和一个e-NFA作为输入,如果该表达式被自动机接受,它将返回true

首先,我定义了一个函数,该函数使用以下运算符{|,*,+,…}从正则表达式生成e-NFA(作为cons单元格)

例如:使用表达式(或a b),输出将为:

((INITIAL 0) (DELTA 0 EPSILON 1) (DELTA 0 EPSILON 3) (DELTA 2 EPSILON 5) (DELTA 4 EPSILON 5) (DELTA 1 A 2) (DELTA 3 B 4) (FINAL 5))
我想到了这个想法:我编写了函数recognizer or(它处理or案例):

如果我这样调用函数:

(nfa-recognize-or (cdr fa) '(a) 0 5)
它返回“堆栈溢出”。问题是,在一些调用之后,fa的值=

(DELTA 1 A 2) (DELTA 3 B 4) (FINAL 5))
如前所述,初始值=2,最终值=5。此时,程序应该考虑的下一个状态应该是

(DELTA 2 EPSILON 5)
为了返回TRUE,但这是不可能的,因为此时NFA已“消耗”,我无法回溯它以验证其他状态


你有什么建议吗?

我想我已经知道你想做什么了

编辑:我以为你在尝试将正则表达式转换为e-NFA,但似乎你想做其他事情。不管怎样,我现在就把这个留下

让我们尝试在创建函数
re->enfa
方面取得一些进展,该函数采用以下参数:

  • 某种Lisp格式的正则表达式
  • 一种状态符号,该状态将有一个epsilon转换到对应于该正则表达式的sub-e-NFA
  • 对应离开状态的符号
  • 我们将使用符号来识别状态,这样我们就可以很容易地找到唯一的标识符。 我们将编写一些小函数,以防我们改变主意

    (defun new-state () (gensym))
    (defun transition (from to on) `((delta ,from ,on ,to)))
    (defun nfa-union (&rest args) (apply #'append args))
    
    现在让我们看两种情况,我们将把它们放在函数中。一个用于连接两个正则表达式,另一个用于交替

    (defun concat-nfa (a b s0 sn)
      (let ((m (new-state)))
        (nfa-union (re->enfa a s0 m) (re->enfa a m sn))))
    (defun or-nfa (a b s0 sn)
      (let ((s1 (new-state))
            (s2 (new-state))
            (sk (new-state))
            (sl (new-state)))
        (nfa-union (transition s0 s1 'epsilon)
                   (transition s0 s2 'epsilon)
                   (transition sk sn 'epsilon)
                   (transition sl sn 'epsilon)
                   (re->enfa a s1 sk)
                   (re->enfa b s2 sl))))
    
    然后,您可以定义
    re->enfa
    的外观:

    (defun re->enfa (x s0 sn)
      (if (atom x) (transition s0 sn x)
        (case (car x)
          (or (if (cddr x) (or-nfa (cadr x) (cons 'or (cddr x) s0 sn)
                  (re->enfa (cadr x) s0 sn)))
          (concat  (if (cddr x) (concat-nfa (cadr x) (cons 'concat (cddr x) s0 sn)
                  (re->enfa (cadr x) s0 sn)))
          (* (kleene-nfa (cadr x) s0 sn))
          (+ (re->nfa `(concat ,(cadr x) (* ,(cadr x))))))))
    
    这应该给你一个起点,留下一些东西来填补。你也可以做一些改变。例如,您可能希望实现
    +
    ,只需要对内部表达式计算一次e-NFA


    您还需要添加一个函数,该函数添加初始和最终状态的标记,并包装
    re->enfa

    我将从较高的级别开始,深入到细节。我不会给出一个完整的答案,因为它并不短,但希望它足以让你走上正确的道路

    给定一个正则表达式和一个e-NFA作为输入,如果自动机接受该表达式,则返回true

    为简单起见,我将假设正则表达式的形式与字母
    E
    加上通常的
    (,),+,*
    中的字符串兼容。我还假设
    e-NFA
    以一种包含所有必要信息的形式给出,这些信息构成了
    e-NFA
    的正式定义中所期望的所有信息。将不考虑在实际格式和我喜欢的格式之间进行翻译的困难

    我总是建议在编写任何代码之前,先弄清楚如何手工解决问题。你会如何在测试中解决这个问题?如何判断
    e-NFA
    是否接受正则表达式?更根本的是,对这一要求的合理解释是什么?我的解释如下:

    如果
    L(r)
    L(M)
    的子集,则
    e-NFA
    M
    接受正则表达式
    r

    换句话说,如果
    w
    是与
    r
    匹配的字符串,则
    M
    应接受该字符串。这第一步很重要,因为它将问题转化为我们可以开始正式解决的问题。我们需要看看一种语言是否是另一种语言的子集。据我所知,对于正则表达式,没有直接的正式机制来直接回答这个问题。然而,对于有限自动机,有一种著名的构造可以回答类似这样的问题:我将这种构造称为笛卡尔积机器构造,它用于生成一个有限自动机,该有限自动机接受另两个有限自动机的语言。特别是:如果
    L\R=0
    (其中
    0
    为空集,
    \
    为设置差),则
    L
    R
    的子集。笛卡尔积机器构造允许我们为
    L\R
    构造机器,为
    L
    R
    构造给定机器。我们已经有一个用于
    M
    ;它是给定的。我们所需要的只是一台用于
    L(r)
    的机器,我们已经准备好使用确定性算法为不同语言生成一台机器。然后,剩下的就是检查结果语言是否为空。有关笛卡尔积机器构造的详细信息,请参阅我的另一个答案。不要担心你会有
    NFA
    s;施工工程与上述DFAs相同

    给定一个正则表达式
    r
    ,有一个算法用于生成接受
    L(r)
    NFA
    。我没有一个现成的链接,所以我会掩饰它。基本上,我们定义了一些递归规则,用于基于正则表达式的每个项构造
    e-NFA
    s。规则如下:

    1. Alphabet symbol a: M(a)
    
    --->()-a->(O)
    
    
    2. Concatenation rs: M(rs)
    
    --->[M(r)]-e->[M(s)]
    
    * Note: one -e-> from each accepting state of M(r) to initial state of M(s)
            initial state is that of M(r)
            accepting states are those of M(s)
    
    
    3. M(r+s):
    
    -->()-e->[M(r)]
        \-e->[M(s)]
    
    * Note: new initial state added
            accepting states are those of M(r) and M(s)
    
    4. M(r*):
    
         /--e--\
        V       \
    --->()-e->[M(r)]
    
    * Note: one -e-> from each accepting state of M(r) to initial state
            new initial state
            accepting state is only the new initial state
    
    现在我们有了一个正则表达式的NFA,我们知道如何构造笛卡尔乘积机来处理这个差异。我们最终得到的是一个大的
    e-NFA
    ,表示
    L(r)
    L(M)
    的区别。我们已经说过,这些语言的区别是空的,如果
    L(r)
    L(M)
    的子集,
    L(r)
    L(M)
    iff
    M
    接受
    r
    。剩下的唯一问题是:我们的笛卡尔积机器的语言是emp吗
    1. Alphabet symbol a: M(a)
    
    --->()-a->(O)
    
    
    2. Concatenation rs: M(rs)
    
    --->[M(r)]-e->[M(s)]
    
    * Note: one -e-> from each accepting state of M(r) to initial state of M(s)
            initial state is that of M(r)
            accepting states are those of M(s)
    
    
    3. M(r+s):
    
    -->()-e->[M(r)]
        \-e->[M(s)]
    
    * Note: new initial state added
            accepting states are those of M(r) and M(s)
    
    4. M(r*):
    
         /--e--\
        V       \
    --->()-e->[M(r)]
    
    * Note: one -e-> from each accepting state of M(r) to initial state
            new initial state
            accepting state is only the new initial state