问题描述
我有一个相当复杂的状态机,需要在Python中实现。到目前为止,我尝试过的所有操作都非常混乱,包含一百万个循环和if
语句。我大约有100个节点,每个节点可以有多个传入和传出弧。
首先,我已经有了状态机的设计,这不是需要学习的东西。其次,我不想使用任何scikit-learn
之类的软件包。显然,pandas
和numpy
很好,但是我想用Python对其进行草绘,然后将其引入C#,因此它不能太依赖于Python包。
我也尝试过使用图形,但是感觉不正确,因为存在循环,并且遍历算法取决于决策,而不是成本。我也在考虑编写一种领域特定的语言,但是在投入大量时间之前,我想确保自己已经做过一些研究。
用于存储状态机的典型数据结构是什么?它需要能够考虑周期和下沉。 DSL可以走吗?如果是这样,您有任何指针吗?
谢谢!
解决方法
状态机的最简单表示形式是图形。
在大多数语言中,可以使用其索引表示状态且其元素是到其他状态的转换列表的数组。将人类可读的状态名称与每个索引相关联以支持调试通常很方便。过渡需要两个部分:过渡条件和目标状态号。
通过命名所需的图形构造函数和元素,我们在python中设计了一个“内部” DSL。我们可以定义一个状态机(原谅我的Python,我不是专家):
Class Transition // defines a single transition
def __init__(self,condition,target):
self.condition = condition // store a python lambda here
self.target = target // may be a state name (optimize to a state index)
Class State
def __init__(self,state_name,transitions):
self.state_name=state_name // text name of state
self.transitions=transitions // a "set" (array) of transitions
Class StateMachine:
def __init__(self,states):
self.states = states // a "set" (array) of named states
def AddState(self,state):
self.states.append(state)
def compile(self):
// replaces state names with state indexes; left as reader exercise
我们可以构造一个状态机来识别典型的标识符名称(与正则表达式“ [A-Z] [A-Z0-9] *”相同):
MyIdentifierRecognizer = StateMachine([])
MyIdentifierRecognizer.AddState(State("ScanFirstIdentifierCharacter",[Transition(lambda x: IsLetter(x),"ScanNthIdentifierCharacter"),Transition(lambda x: not(IsLetter(x)),"NotAnIdentifier)]))
MyIdentifierRecognizer.AddState(State("ScanNthIdentifierCharacter",[Transition(lambda x: IsLetterOrDigit(x),Transition(lambda x: not(IsLetterOrDigit(x)),"EndOfValidIdentifier)]))
MyIdentifierRecognizer.AddState(State("NotAnIdentifier",[])) // no further transitions posssible
MyIdentifierRecognizer.AddState(State("EndOfValidIdentifier",[])) // no further transitions possible
运行状态机很容易。我们首先将current_state设置为状态机的初始状态:
current_state = "ScanFirstIdentifierCharacter"
然后输入一个字符:
character = ...
我们在状态机中找到current_state,然后处理转换以查找满足更改current_state的转换:
for s in StateMachine.states // find the actual state matching current_state
if s.name == current_state
state = s
break
for t in state.transitions // process all transitions (assumed mutually exclusive)
if t.condition(character)
current_state=t.target
break // adding this line means "take the first valid transition"
您可以通过在定义状态机后运行一些“编译器”来提高效率,该状态机用数组索引替换文本状态名。然后状态查询可以是直接数组索引。
如果解释该状态机的速度不够快,则可以将各个状态编译为等效的目标语言代码,然后导入该代码。如果做得正确,这实际上会产生非常快的状态机。
[注意:我完成此回答后,作者在他的问题中添加了示例流程图。我把它留给他,以这种状态机符号转换他的流程图。]
OP最初提出的与决策树有关的另一个考虑因素:过渡谓词如何存储?在我这里的版本中,它们是“不透明的” Python lambda,可以满足某些需求。 如果OP希望以非透明的方式存储复杂的转换谓词, 他将需要“ transition”字段来包含表示感兴趣的布尔条件的抽象语法树。有关如何解析(布尔)和执行布尔表达式的详细信息,请参见https://stackoverflow.com/a/2336769/120163