LISP中的NFA识别器
我必须在lisp中定义一个函数,给定一个正则表达式和一个e-NFA作为输入,如果该表达式被自动机接受,它将返回true 首先,我定义了一个函数,该函数使用以下运算符{|,*,+,…}从正则表达式生成e-NFA(作为cons单元格) 例如:使用表达式(或a b),输出将为: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
((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
方面取得一些进展,该函数采用以下参数:
(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)
iffM
接受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