I am trying to implement NFA in Python to recognize words but my code doesn't work,

拥有回忆 提交于 2020-01-24 23:57:09

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!