问题
I am trying to implement a method that recognizes words. I have written the following code and have tried to follow my code on paper and execute it step by step with example inputs, but I can't find the reason why my code is not doing what I want him to do. Does anyone see the flaw? I can't see it and I am confused on why it doesn't work.
from collections import defaultdict
class NFA:
def __init__(self, initial, trns, final):
self.initial = initial
self.final = set(final)
self.trns = defaultdict(set)
for (src, char, tgt) in trns:
self.trns[src, char].add(tgt)
def recognizewords(self, strng):
strang = [char for char in strng]
strang.reverse()
visited = set()
agenda = [self.initial]
while strang and agenda:
currentletter = strang.pop()
current = agenda.pop()
visited.add(current)
if (current, currentletter) in self.trns.keys():
state = self.trns[(current, currentletter)]
for st in state:
if strang == [] and state in self.final:
return True
for i in self.trns[(current, currentletter)]:
agenda.append(i)
return False
exampleO = NFA(0, [(0,'o',1), (1,'k',2), (2,'i',1), (2,'!',3)], [3])
print(exampleO.recognizewords("ok!"))
It should return True, because at one point my list "strang" will be empty (when I assigned currentletter to "!") and at the same time 3 is in self.final, because self.final is [3] for my object exampleO....
回答1:
This isn't quite a fix to your code because the solution to this type of problem is easier to visualize (at least for me) when you use recursion rather than an explicit stack. And as I mentioned in a comment, an NFA actually allows transitions to multiple states on a given input character including and especially the empty string. So I have modified the input specification to allow a list of new states to be specified for each transition. Here state 0 transitions on the empty string to either state 1 or state 5 (which recognizes OK
). State 2 can trasition on k
to either state 3 or 4.
from collections import defaultdict
class NFA:
def __init__(self, initial, trns, final):
self.initial = initial
self.final = set(final)
self.trns = defaultdict(set)
self.epsilon_states = set()
for (src, char, tgt) in trns:
if char == '':
self.epsilon_states.add(src)
for state in tgt:
self.trns[src, char].add(state)
def recognize_next_char(self, state, strng, index):
ch = '' if state in self.epsilon_states else strng[index]
if (state, ch) in self.trns.keys():
next_states = self.trns[(state, ch)]
if ch != '':
index += 1
for next_state in next_states:
if index == len(strng):
if next_state in self.final:
return True
elif self.recognize_next_char(next_state, strng, index):
return True
return False
else:
return False
def recognizewords(self, strng):
if len(strng) == 0:
if self.initial in self.final:
return True
if self.initial not in self.epsilon_states:
return false
return self.recognize_next_char(self.initial, strng, 0)
exampleO = NFA(0, [(0,'',(1,5)), (1,'o',(2,)), (2,'k',(3,4)), (3,'i',(2,)), (4,'!',(99,)), (5,'O', (6,)), (6,'K',(99,))], [99])
print(exampleO.recognizewords("okikik!"))
print(exampleO.recognizewords("ok!"))
print(exampleO.recognizewords("OK"))
print(exampleO.recognizewords("ok"))
print(exampleO.recognizewords("oki"))
print(exampleO.recognizewords("okx"))
Prints:
True
True
True
False
False
False
Example of Using Epsilon Transitions to Recognize ab(cd|ef)gh
回答2:
So as it turns out that the non-recursive solution is a bit more complicated than what you had to do backtracking correctly (it requires more stacks). I have changed a few variable names, which for me are more logical. There are two versions below. The second one modifeds the first with just a few slight changes to support transitions on empty strings:
from collections import defaultdict
class NFA:
def __init__(self, initial, trns, final):
self.initial = initial
self.final = set(final)
self.trns = defaultdict(set)
for (src, char, tgt) in trns:
self.trns[src, char].add(tgt)
def recognizewords(self, strng):
strlen = len(strng)
if strlen == 0:
return self.initial in self.final
index = 0
next_states = [self.initial]
next_states_stack = []
index_stack = []
while index < strlen:
current_letter = strng[index]
if next_states:
state = next_states.pop()
if (state, current_letter) in self.trns.keys():
new_next_states = self.trns[(state, current_letter)]
if new_next_states & self.final:
# did we use up all the characters?
return index == strlen - 1
next_states_stack.append(next_states)
index_stack.append(index)
next_states = list(new_next_states)
index += 1
elif next_states_stack:
next_states = next_states_stack.pop()
index = index_stack.pop()
else:
return False
return False
# ab(d|e)
exampleO = NFA(0, [(0,'a',1), (1,'b',2), (1,'b',3), (2,'d',4), (3,'e',4)], [4])
print(exampleO.recognizewords("abd"))
print(exampleO.recognizewords("abe"))
Prints:
True
True
Variation That Supports Transitions on Empty Strings
from collections import defaultdict
class NFA_Epsilon:
def __init__(self, initial, trns, final):
self.initial = initial
self.final = set(final)
self.trns = defaultdict(set)
self.epsilon_states = set()
for (src, char, tgt) in trns:
if char == '':
self.epsilon_states.add(src)
self.trns[src, char].add(tgt)
def recognizewords(self, strng):
strlen = len(strng)
if strlen == 0:
return self.initial in self.final
index = 0
next_states = [self.initial]
next_states_stack = []
index_stack = []
while index < strlen:
if next_states:
state = next_states.pop()
current_letter = '' if state in self.epsilon_states else strng[index]
if (state, current_letter) in self.trns.keys():
new_next_states = self.trns[(state, current_letter)]
if new_next_states & self.final:
# did we use up all the characters?
return index == strlen - 1
next_states_stack.append(next_states)
index_stack.append(index)
next_states = list(new_next_states)
if current_letter != '':
index += 1
elif next_states_stack:
next_states = next_states_stack.pop()
index = index_stack.pop()
else:
return False
return False
# ab(cd|ef)gh
example1 = NFA_Epsilon(0, [
(0,'a',1),
(1,'b',2),
(2,'',3),
(2,'',6),
(3,'c',4),
(4,'d',5),
(5,'',9),
(6,'e',7),
(7,'f',8),
(8,'',9),
(9,'g',10),
(10,'h',11)
],[11])
print(example1.recognizewords('abcdgh'))
print(example1.recognizewords('abefgh'))
Prints:
True
True
来源:https://stackoverflow.com/questions/58892357/i-am-trying-to-implement-nfa-in-python-to-recognize-words-but-my-code-doesnt-wo