[编译原理实验2]python导入Graphviz实现NFA的确定化
实验要求
- 从txt文件读入NFA;
- 用Graphviz实现自动机的图形化;
- 输出NFA和DFA图像;
输入文件
data.txt
第一行为初态集合,第二行为终态集合
其余是状态转换情况(ps. 本实验$代表空ε)
0
10
0 $ 1
1 $ 2
1 $ 4
2 a 3
3 $ 6
4 b 5
5 $ 6
6 $ 7
7 a 8
8 b 9
9 b 10
6 $ 1
0 $ 7
数据结构
- Move_NFA ,list类型的list,存放NFA状态变化情况.
- _closure ,字典,存放NFA中每个状态的空闭包集合
- Res,字典,存放当前状态遇到每个输入字符的后续状态集,如{‘a’:[‘4’,‘1’,‘2’,‘3’,‘7’,‘6’, ‘8’]}
- State0 ,list类型,存放NFA的所有状态
- Str0,list类型,存放NFA的字母表
- StateList,list类型的列表,存放DFA的所有状态集合.
- Begin0,End0,list类型,Begin0为DFA的初态集合,End0为DFA的终态集
关键代码
1.文件读入处理
with open('data.txt', 'r') as r:
NFA = [line.rstrip('\n') for line in r.readlines()]
Move_NFA = NFA[2::]
for i in range(len(Move_NFA)):
# print(Move_NFA[i])
Move_NFA[i] = Move_NFA[i].split() # 以空格为分隔符分开
2.画NFA图像
需要导入Graphviz库及下载Graphviz.
from graphviz import Digraph
def graph_nfaprint(): # 画NFA的图像
g = Digraph('G', filename='NFA.gv', format='png')
for i in range(len(Move_NFA)):
g.edge(Move_NFA[i][0], Move_NFA[i][2], label=Move_NFA[i][1])
for i in range(len(Begin)):
g.node(Begin[i], color='red') # 开始节点红色
for j in range(len(End)):
g.node(End[j], shape='doublecircle') # 结束节点双层
g.view()
输出:
3.找到所有NFA状态的空闭包集合
求出ε-closure(s),ε-closure(s)表示由状态s经由条件ε可以到达的 所有状态的集合
for i in range(len(State0)):
res = [State0[i]]
for j in range(len(Move_NFA)):
if Move_NFA[j][0] == State0[i] and Move_NFA[j][1] == '$': # 查找空闭包
res.extend(Move_NFA[j][2])
_closure[State0[i]]=list(set(res))
# print(_closure )
4.求NFA状态的等价状态
输入为单个状态,返回一个List
def Findclosure(AA): # 输入一个状态,找这个状态的所有等价状态
A = _closure[AA] # 查询输入状态的空闭包
A0 = []
for ch in A:
A0 = A0 + _closure[ch]
A0 = list(set(A0))
while set(A) != set(A0): # 循环直到闭包集合不再变化,得到最终的结果
A = A0
A0 = []
for ch in A:
A0 = A0 + _closure[ch]
A0 = list(set(A0))
return A0
5.DFA的状态遇到输入字符的状态转换情况
构造状态转换表,普遍称为称move函数
如本例:
ε-closure(0) = A
ε-closure(move(A,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(A,b)) = ε-closure(5) = {1,2,4,5,6,7} = C
ε-closure(move(B,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(B,b)) = ε-closure({5,9}) = {1,2,4,5,6,7,9} = D
ε-closure(move(C,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(C,b))=ε-closure(5) = {1,2,4,5,6,7} = C
ε-closure(move(D,a)) = ε-closure({3,8}) = {1,2,3,4,6,7,8} = B
ε-closure(move(D,b)) = ε-closure(5) = {1,2,4,5,6,7} = C
a | b | |
---|---|---|
A | B | C |
B | B | D |
C | B | C |
D | B | C |
def FindNewState(AB): # 找DFA状态的单步集合
res = dict() # 字典,形如{'a': ['4', '1', '2', '3', '7', '6', '8'], 'b': ['5', '4', '1', '2', '7', '6']}
for ch in Str0:
RES = []
for i in range(len(AB)):
for j in range(len(Move_NFA)):
if Move_NFA[j][0] == AB[i] and Move_NFA[j][1] == ch:
RES.append(Move_NFA[j][2])
result = []
for k in RES:
result = result+Findclosure(k)
res[ch] = list(set(result))
return res
6.画DFA图像
DFA的初始状态是NFA的初始状态的等价状态,终止状态是含有NFA终止状态的所有集合
def graph_dfaprint(StateList, Begin0, End0): # 画DFA图像
g = Digraph('G', filename='DFA.gv', format='png')
Begin1 = []
End1 = []
for i in range(len(Begin0)):
Begin1.append(" ".join(sorted(Begin0[i])))
for j in range(len(End0)):
End1.append(" ".join(sorted(End0[j]))) # 从小到大排序
for i in range(len(StateList)):
Dictt = FindNewState(StateList[i])
for j in Str0:
if StateList[i] != [] and Dictt[j] != []: # 防止重复
g.edge(" ".join(sorted(StateList[i])), " ".join(sorted(Dictt[j])), label=j)
for k in range(len(Begin1)):
g.node(Begin1[k],color='red', shape='circle') # 初态结点为红色
for i in range(len(StateList)):
g.node(" ".join(sorted(StateList[i])), shape='circle') # 所有节点为圆形
for j in range(len(End1)):
g.node(End1[j], shape='doublecircle') # 终态结点为双层
g.view()
输出:
完整python代码
from graphviz import Digraph
import re
# $代表空ε
def graph_nfaprint(): # 画NFA的图像
g = Digraph('G', filename='NFA.gv', format='png')
for i in range(len(Move_NFA)):
g.edge(Move_NFA[i][0], Move_NFA[i][2], label=Move_NFA[i][1])
for i in range(len(Begin)):
g.node(Begin[i], color='red') # 开始节点红色
for j in range(len(End)):
g.node(End[j], shape='doublecircle') # 结束节点双层
g.view()
def graph_dfaprint(StateList, Begin0, End0): # 画DFA图像
g = Digraph('G', filename='DFA.gv', format='png')
Begin1 = []
End1 = []
for i in range(len(Begin0)):
Begin1.append(" ".join(sorted(Begin0[i])))
for j in range(len(End0)):
End1.append(" ".join(sorted(End0[j]))) # 从小到大排序
for i in range(len(StateList)):
Dictt = FindNewState(StateList[i])
for j in Str0:
if StateList[i] != [] and Dictt[j] != []:
g.edge(" ".join(sorted(StateList[i])), " ".join(sorted(Dictt[j])), label=j)
for k in range(len(Begin1)):
g.node(Begin1[k],color='red', shape='circle')
for i in range(len(StateList)):
g.node(" ".join(sorted(StateList[i])), shape='circle')
for j in range(len(End1)):
g.node(End1[j], shape='doublecircle')
g.view()
def Findclosure(AA): # 输入一个状态,找这个状态的所有等价状态
A = _closure[AA] # 查询输入状态的空闭包
A0 = []
for ch in A:
A0 = A0 + _closure[ch]
A0 = list(set(A0))
while set(A) != set(A0): # 循环直到闭包集合不再变化,得到最终的结果
A = A0
A0 = []
for ch in A:
A0 = A0 + _closure[ch]
A0 = list(set(A0))
return A0
def FindNewState(AB): # 找DFA状态的单步集合
res = dict() # 字典,形如{'a': ['4', '1', '2', '3', '7', '6', '8'], 'b': ['5', '4', '1', '2', '7', '6']}
for ch in Str0:
RES = []
for i in range(len(AB)):
for j in range(len(Move_NFA)):
if Move_NFA[j][0] == AB[i] and Move_NFA[j][1] == ch:
RES.append(Move_NFA[j][2])
result = []
for k in RES:
result = result+Findclosure(k)
res[ch] = list(set(result))
return res
# -----------------------
with open('data.txt', 'r') as r:
NFA = [line.rstrip('\n') for line in r.readlines()]
Move_NFA = NFA[2::]
for i in range(len(Move_NFA)):
# print(Move_NFA[i])
Move_NFA[i] = Move_NFA[i].split() # 以空格为分隔符分开
# print(Move_NFA)
State = []
Str = []
for i in range(len(Move_NFA)):
State.append(Move_NFA[i][0])
State.append(Move_NFA[i][2])
Str.append(Move_NFA[i][1])
State0 = list(set(State)) # 去重
State0.sort(key=State.index) # 排序
Str0 = list(set(Str))
Str0.sort(key=Str.index)
if '$' in Str0:
Str0.remove('$') # 字符列表去掉ε
# print(State0)
_closure = dict() # 建立一个空字典,表示由每个状态经由条件ε可以到达的 所有状态的集合
End = NFA[1].split()
Begin = NFA[0].split()
if __name__ == '__main__':
graph_nfaprint()
for i in range(len(State0)):
res = [State0[i]]
for j in range(len(Move_NFA)):
if Move_NFA[j][0] == State0[i] and Move_NFA[j][1] == '$': # 查找空闭包
res.extend(Move_NFA[j][2])
_closure[State0[i]]=list(set(res))
# print(_closure )
A = Findclosure(State0[0])
# print(A) # A为初始状态等价集合
number = 0
length = 1
StateList = []
StateList .append(A)
while number < length:
A2 = FindNewState(StateList[number])
number = number+1
for ch in Str0:
temp = 1
for p in range(length):
if set(A2[ch]) == set(StateList[p]):
temp = 0
break
if temp == 1:
StateList .append(A2[ch])
length = length+1
Begin0 = []
End0 = [] # 存放DFA开始状态和结束状态
for i in range(len(Begin)):
Begin0.append(Findclosure(Begin[i]))
for j in range(len(End)):
for k in range(len(StateList)):
if End[j] in StateList[k]:
if StateList[k] not in End0:
End0.append(StateList[k])
while [] in StateList:
StateList.remove([]) # 删除空状态,防止y 0 $的情况
print('DFA状态表为:', StateList )
print('DFA开始状态:', Begin0)
print('DFA终止状态:', End0)
graph_dfaprint(StateList, Begin0, End0) # 画出DFA
DFA状态表为: [['0', '4', '1', '7', '2'], ['3', '8', '6', '4', '1', '7', '2'], ['5', '6', '4', '1', '7', '2'], ['9', '5', '6', '4', '1', '7', '2'], ['5', '6', '10', '4', '1', '7', '2']]
DFA开始状态: [['0', '4', '1', '7', '2']]
DFA终止状态: [['5', '6', '10', '4', '1', '7', '2']]
测试样例2
文件读入
data1.txt
x
z
x 0 z
y 0 x
y 0 y
z 0 x
z 0 z
x 1 x
z 1 y
输出
NFA图像
DFA图像
DFA状态表为: [['x'], ['z'], ['z', 'x'], ['y'], ['y', 'x'], ['z', 'y', 'x']]
DFA开始状态: [['x']]
DFA终止状态: [['z'], ['z', 'x'], ['z', 'y', 'x']]
小结
本实验实现了NFA到DFA的转换,并用Graphviz实现图像化.
- 若在转换中出现空状态要记得把空状态从StateList 中删除,不然会出现空节点.
- 处理时要注意状态顺序,set可以顺序无关的匹配相等列表
- 本实验使用python,导入Graphviz库进行图像生成,学会了Graphviz的基本使用方法.
- DFA的初始状态是NFA的初始状态的等价状态,终止状态是含有NFA终止状态的所有集合