1若干概念
右线性文法生成的语言是右线性语言,有限自动机识别和接受的语言是正则语言。而正则文法是左线性文法和右线性文法的统称,所以右线性语言类与正则语言类是同一语言类。
定义1 设G[S]=(VN,VT,P,S)为CFG,若P中的产生或均有如下的形式:A→aB或A→a(A∈VN,a∈VT)则称G为右线性文法。例如,文法G1[S]=({S,A,B},{a,b},P1,S)其中P1={S→aA,A→aA,A→bB,A→b,B→bB,B→b}为右线性文法,G1所产生的正规集为L(G1)={aibj |i,j≥1}。
定义2 一个确定有限自动机(DFA)M是一个五元组 其中,∑是一个有穷字母表,它的每一个元素称为一个输入符号;S是一个有限状态集,它的每一个元素称为一个状态;
是转换函数,定义了从
上的一个单值映射,即
,指明当前的状态为p,当输入符号为a时,则转换到下一个状态q,q称为p的后继状态;
是一个唯一的初始状态;
是一个终止状态集。
定义3 DFA M所接受的符号串的集合称为DFA M所接受的语言,记为L(M),即换句话说,对于
中的任何一个串虬w,若存在一条从某一表示初态的结点到某一表示终态结点的通路,且这条路上所有弧的标记符依次连接成的符号串等于w,则称w可为DFA M所识别(读出或接受)。
在状态转移的每一步,根据有限自动机当前所处的状态和所面临的输入符号,便能唯一地确定有限自动机的下一个状态,即转换函数的值是唯一的,反映到状态转换图上,就是若 ,则任何结点的出边都有n条,且这些出边上的标记均不相同,这就是为什么我们把按上述方式定义的有限自动机称为确定的有限自动机的原因。
2构造思想:
迄今为止大多数右线性文法和有限自动机的等价性都是通过模拟构造来证明的。而近期也有新的证明方法提出,首先引入字母表上的右线性方程组及其最小解的概念,证明最小解的存在性与有效可解性,描述最小解的结构;其次通过右线性方程组及其最小解,证明右线性语言是正则语言,正则语言也是右线性语言,从而证明右线性文法和有限自动机的等价性。上述方法是在定理公式的理论上对问题进行证明。本文在最后会在实际上通过模拟构造来对右线性文法构造有穷自动机进行实验。
2.1右线性文法构造FA的思想:
非终结符+开始状态Z→状态
终结符→字母表
开始符号→开始状态
产生式→状态转换函数
右边含有非终结符的产生式:A→aB对应函数δ(A,a)=B
右边不含非终结符的产生式:A→a对应函数δ(A,a)=Z
2.2从右线性文法到FA
定理1:对每个右线性正则文法GR,都存在一个有穷自动机M,使得L(M)=L(GR)。
证明:设右线性文法GR=(VT,VN,S,P),将VN中的每个非终结符视为状态符号,并增加一个新的终止符号f,(f VN)。令M=(VN{f},VT,d,S,{f}),其中状态转换函数d由以下规则定义:
- 若对某个A VN及a VT{ε},P中有产生式A→a,则令d(A,a)=f;
- 对任意的A VN及a VT{ε},设P中左端为A右端第一个符号为a的所有产生式为A→aA1|aA2|…|aAK(不包括A→a),则令d(A,a)={A1,A2…,AK}。
显然M为一个NFA。到此已说明存在一个FAM与该右线性文法对应。
等价性证明(L(GR)=L(M)):
对右线性正规文法GR,在SW的左推导过程中,利用A→aB一次就相当于在M中从状态A出发经标记为a的箭弧到达状态B(包括a=ε的情形)。在推导过程的最后,利用A→a一次相当于在M中从状态A出发标记为a的箭弧到达终态f(包括a=ε的情形)。
综上所述,在正则文法GR中,S W的充要条件是:在M中,从状态S到状态f有一条通路,其上所有的箭弧的标记符号依次连接起来恰好等于W,这就是说,W L(GR)当且仅当W L(M),故L(GR)=L(M)。
显然M为一个NFA。到此已说明存在一个FAM与该左线性文法对应。
等价性证明(L(GL)=L(M)):
综上所述,在正则文法GL中,S W的重要条件是:在M中,从状态q到状态S有一条通路,其上所有箭弧的标记符号一次连接起来恰好等于W,这就是说,W L(GL)当且仅当W L(M),故L(GL)=L(M)。
定理2:对于每个有穷自动机M,都存在一个右线性正则文法GR与之等价。
证明:设DFAM=(S,Σ,d,S0,F),分以下两种情况进行证明:
(1)若S0 F,则令GR=(Σ,S,S0,P),其中P是由以下规则定义的产生式集合,对任何aΣ及A,B S,若d(A,a)=B,则:
- 当B F时,令A→aB;
- 当B F时,令A→aB|a;
显然,上述得到的文法为一个右线性正则文法。
等价性证明(L(M)=L(GR)):
在DFAM中,对任何wΣ*,不妨设w=a1a2…ak,其中aiΣ(i=1,2,…,k),若S W,则存在一个最左推导S0 a1A1 a1a2A2…a1…aiAi a1…aiai+1Ai+1…a1…ak,因而,在M中存在一条从S0出发一次经过A1,……,AK-1到达终态的通路,该通路上所有的箭弧的标记依次为a1,……,ak。反之亦然。所以,w L(GR)当且仅当w L(M),故L(M)= L(GR)。
(2)S0 F,因为d(S0,ε)= S0,所以ε L(M),但上面构造的L(GR)中不含ε。因此,需在文法中添加产生式SO→ε,这样,就有L(M)=L(GR)。
3算法思想
首先输入变量集、终结符集和开始符,输入变量集和终结符集时用逗号隔开,否则会提示出错。然后输入右线性文法的产生式,输入时即区分出产生式的左部和右部,点击添加产生式按钮提取产生式的左部和右部。如果产生式不符合要求,则提示出错,添加的产生式使用链表存储,每个节点表示一个产生式,每次点击添加后,程序将会新生成一个结点来表示产生式。产生式的具体数据结构如下所示:
typedef struct List
{
char leftVar[10]; //左侧字符
char rightVar[10]; //右侧字符
char endVar[10]; //终结符
char endRightVar[10]; //右侧后部字符
List *pNext; //next指针
}dfaList, *pdfaList;
添加完成后,程序将从产生式链表的第一个结点开始扫描,从右部提取出前端的终结符,具体方法是定义一个UCHAR型的指针,从前端向后扫描,如果是小写字母(默认是非终结符),则将小写字母放入终结符集中,当扫描完前端后,指针继续后移,此时判断条件改变,如果不为“\0”,即字符串没有结束,则将字符串中剩下的字符存储到右侧后部字符中。
转换成自动机时,将先前提取出的各字符串按照正确位置放置,并以函数的形式输出,例如:产生式V0—>aV1,分别提取出V0,a和V1,然后将提取出来的V0,a和V1输出为f(V0,a)={V1},如果产生式形如V1—>b类型,则将以f(V1,b)={Q}形式输出。
4代码实现
4.1右线性文法规范
文法表示:
RLgrammar ::= Tetrad \r\n productionList
Tetrad ::= G=<{ VarList },{ TerminalList }, StartVariable ,P>
productionList ::= production { \r\n production }
VarList ::= Variable { , Variable }
TerminalList ::= Terminal { , Terminal }
StartVariable ::= Variable
Production ::= Variable -> Terminal { Terminal }( Variable )
Variable ::= CapitalLetter {Number}
Terminal ::= ! CapitalLetter (非大写字母)
CapitalLetter ::= [A-Z]
Number ::= [0-9]
4.1.1语义分析
文法的开始变量StartVariable必须属于变量集合VarList中才能识别。每一条产生式中的的包含的变量必须属于变量集合,终结符必须属于终结符集。必须有至少一条产生式左边的变量为开始变量
4.1.2右线性文法四元组
private ArrayList variable=new ArrayList();//变量集合
private ArrayList terminal_symbol=new ArrayList ();//终结符
private string start_variable;//开始符
private ArrayList production=new ArrayList ();//产生式
4.2 FA
4.2.1 文法表示:
FA ::= quintuple \r\n transFuncList
Quintuple ::= M=<{ internalStateList },{ inputAlphabet },&, initialState , finalState >
transFuncList ::= transFunction { \r\n transFunction }
internalStateList ::= internalState { , internalState }
inputAlphabet ::= inputAlpha { , inputAlpha}
transFunction ::= &( internalState, inputAlpha { inputAlpha } )= internalState | finalState
initialState ::= internalState
4.2.2 FA五元组
private ArrayList internal_state=new ArrayList ();//内部状态有限集合
private ArrayList input_alphabet=new ArrayList ();//输入字母表
private SortedList transfer_function = new SortedList();//转移函数
private string initial_state;//初始态
private string final_state;//终态
4.3产生式右部拆分:
将产生式的右部终结符和非终结符分开,分别存储在链表每个节点的不同属性值中,具体方法就是判断字母符号的大小写,如果是小写,就放入终结符属性中,然后将其后的字符放入非终结符属性中。
//产生式右部拆分
void CDFADlg::GetRightStr(CString strRightTmp)
{
TCHAR *strTmp;
strTmp = strRightTmp.GetBuffer(0);
while (*strTmp>='a' && *strTmp<='z')
{
dfa.endvar+=*strTmp;
strTmp++;
}
while (*strTmp != '\0')
{
dfa.endrightvar += *strTmp;
strTmp++;
}
}
4.4转换自动机函数:
将先前提取出的各字符串按照正确位置放置,然后中间添加大括号,逗号等符号,以函数的形式输出。
//转换函数
void CDFADlg::OnTransfer()
{
UpdateData(TRUE);
CString temp2;
pdfaList p=dfa.pHead->pNext;
m_OutPut.Format("G=<{%s,Q},{%s},f,%s,Q>",m_Variable,m_EndSym,
m_StartSym);
while (p!=NULL)
{
if (strcmp(p->endRightVar,"")==0)
temp2.Format("\r\nf(%s,%s)={Q}",p->leftVar,p->endVar);
else
temp2.Format("\r\nf(%s,%s)={%s}",p->leftVar,p->endVar,
p->endRightVar);
m_OutPut+=temp2;
p = p->pNext;
}
UpdateData(FALSE);
}
4.5 四元组格式的判定
G=<{V0,V1},{a,b},V0,P>
两种情况下需要判定:
1 载入文法
判断文档中的文法是否符合规范。如果符合,分别讲右线性文法的四元组分别填入系统界面对应的输入框或列表中;否则报错。
2 执行转换
在用户载入文法或输入右线性文法后,点击执行转换。首先会对界面填入的右线性文法信息进行语法语义解析,然后再进行转换。
判定规则:见4.1 右线性文法描述
4.6 RLg中产生式到FA中转换函数的转化
将产生式A->xB左部A和右部的x和B截取出来,B可以为空。 判断A,B是否在变量集合中,判断x是否属于终结符集的闭包,x不能为空,执行转换。
5运行结果
6总结
由以上程序及证明可以看出,右线性文法可以转换为自动机。通过证明及程序的编写,我对形式语言与自动机有了更深一步的理解,巩固了我所学的知识,并为将来的发展奠定了一定的基础。
程序代码:https://download.youkuaiyun.com/download/m0_37840214/11209543