问题描述
我正在编写NFA(不确定性有限自动机)类,该类应解析给定的输入并返回所有可能的轨迹(从初始状态到最终状态的路径)。最终,我想计算给定自动机的歧义度。
不幸的是,我无法正确收集方法的返回值。此版本的代码返回None
,而使用yield
稍加修改后的代码仅返回第一个路径。
这个问题似乎有点含糊,但我希望有人能给我一些正确方向的提示。
谢谢。
class NFA(object):
__slots__ = [
'states','alphabet','transitions','initial_state','final_states',]
#
#
# -------- Init -----------
#
def __init__(
self,states: set,alphabet: set,transitions: dict,initial_state: str,final_states: set,):
"""
Initialize a complete automaton.
"""
self.states = states
self.alphabet = alphabet
self.transitions = transitions
self.initial_state = initial_state
self.final_states = final_states
#
#
# -------- process -----------
#
def process(self,word: str,trace: list = []) -> list:
"""
Check if the given string is accepted by this automaton.
Return all accepting paths.
"""
# processing finished returning the trace
if (not word):
print(trace)
return trace
# start processing with initial state
if (not trace):
trace.append(self.initial_state)
# get the current state transitions
state_transition: dict = self.transitions.get(trace[-1],None)
# input not accepted
if (not state_transition):
return False
# iterate over each possible transition
for state in state_transition.get(word[0],[]):
# create new sub trace,append current state
sub_trace: list = trace.copy()
sub_trace.append(state)
# start recursive function call
self.process(word[1:],trace=sub_trace)
from automata.nfa import NFA
config: dict = {
'states': ['q0','q1','q2'],'alphabet': ['a'],'transitions': {
'q0': {
'a': ['q1','q2']
},'q1': {
'a': ['q1']
}
},'initial_state': 'q0','final_states': ['q1'],}
testNFA = NFA(**config)
assert testNFA.process("a") == [['q0','q1'],['q0','q2']]
解决方法
听起来,您实际上是在要求基本的DFS。我认为这种设计令人困惑。
如果您List<?> hi = Arrays.asList("Hi",new Exception(),0);
hi.forEach(o -> {
o.toString() // it's ok to call Object methods and methods that don't need the contained type
});
hi.add(...) // nothing can be add here won't compile,we need to tell compiler what the data type is but we do not know
来自递归调用,则数据仅返回其直接调用者,并且需要一直传递到整个调用链,直到原始范围。如果您不想使用生成器,则可能需要某种主结果列表参数,在遇到return
已被消耗并且NFA处于接受状态。内部函数对此很有用,可以避免向调用方公开许多默认参数。
一种可能更好的方法是使用generator,它避免了管理结果列表和传递返回值的麻烦,而让调用者控制如何使用结果。
您提到您尝试使用生成器版本,但是将递归生成器use a yield from
应用于递归调用。
牢记这种方法,您显示的代码未考虑trace
,因此基于“从初始状态到最终状态的路径”的问题描述,您的预期输出似乎不正确。我期望word
是理想的输出,因为final_states
不是接受状态。但是在这两者之间做出决定是微不足道的,正如您将在下面的代码段中看到的那样。
作为一个实现细节,每个递归调用将字符串切片为O(n)。您可以使用索引来跟踪所有递归调用框架之间共享的单个字符串中的位置,从而避免了这种开销。同样,在每次通话中复制[['q0','q1']]
列表比保留一个要从中推送/弹出列表并仅在"q2"
上复制列表慢。
这是一个最小的完整示例:
trace