我正在寻找有限状态机的好例子。语言并不是特别重要,只是很好的例子。
代码实现很有用(通用伪代码),但是收集FSM的各种用法也非常有用。
示例不一定需要基于计算机,例如Mike Dunlavey的Railroad网络示例非常有用。
我正在寻找有限状态机的好例子。语言并不是特别重要,只是很好的例子。
代码实现很有用(通用伪代码),但是收集FSM的各种用法也非常有用。
示例不一定需要基于计算机,例如Mike Dunlavey的Railroad网络示例非常有用。
Answers:
安全(事件已触发)
交通灯(触发时间|触发传感器[事件])
自动售货机(事件触发,保险箱的变体)
BGP是支持Internet上核心路由决策的协议。它维护着一个表,用于确定来自给定节点的主机的可达性,并使互联网真正分散。
在网络中,每个BGP节点是对等体,并且使用有限状态机,其中一个六个状态空闲,连接,活动,OpenSent,OpenConfirm,和建立。网络中的每个对等连接都维护以下状态之一。
BGP协议确定发送到对等方以更改其状态的消息。
第一状态为空闲。在这种状态下,BGP初始化资源,拒绝入站连接尝试,并启动与对等方的连接。
第二种状态Connect。在此状态下,路由器等待连接完成,如果成功,则转换到OpenSent状态。如果不成功,它将重置ConnectRetry计时器,并在到期时转换为活动状态。
在活动状态下,路由器将ConnectRetry计时器重置为零,并返回到Connect状态。
在OpenSent状态下,路由器发送一个Open消息并等待返回一条消息。交换Keepalive消息,并在成功接收后将路由器置于“已建立”状态。
在建立状态下,路由器可以发送/接收:Keepalive;更新;与对等方之间的通知消息。
jquery-csv插件中使用的CSV解析器
这是基本的Chomsky Type III语法分析器。
正则表达式标记器用于逐个字符地评估数据。当遇到控制字符时,代码将被传递到switch语句,以便根据启动状态进行进一步评估。将非控制字符进行分组和批量复制,以减少所需的字符串复制操作次数。
标记器:
var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;
第一组匹配项是控制字符:值定界符(“),值分隔符(,)和条目分隔符(换行符的所有变体)。最后一个匹配项处理非控制字符分组。
解析器必须满足10条规则:
注意:前7条规则直接来自IETF RFC 4180。添加了最后3个以覆盖现代电子表格应用程序(例如Excel,Google Spreadsheet)引入的边缘情况,这些情况默认情况下不会定界(即引用)所有值。我曾尝试将对RFC所做的更改归还给我,但还没有收到对我的询问的回应。
结束了,下面是示意图:
状态:
过渡:
注意:实际上缺少状态。从'c'->'b'应该有一行标有状态'1'的行,因为第二个定界符转义表示第一个定界符仍处于打开状态。实际上,最好将其表示为另一个过渡。创造这些是一门艺术,没有单一的正确方法。
注意:它也缺少退出状态,但是在有效数据上,解析器始终在过渡'a'结束,并且由于没有剩余要解析的状态,所以不可能有任何状态。
状态与过渡之间的差异:
状态是有限的,这意味着只能将其推断为意味着一件事。
过渡表示状态之间的流动,因此可能意味着很多事情。
基本上,状态->过渡关系是1-> *(即一对多)。状态定义了“它是什么”,过渡定义了“它的处理方式”。
注意:不用担心状态/转换的应用不是直观的,而是不直观的。在我最终坚持这个概念之前,花了一些时间与我比一个聪明得多的人进行了广泛的交流。
伪代码:
csv = // csv input string
// init all state & data
state = 0
value = ""
entry = []
output = []
endOfValue() {
entry.push(value)
value = ""
}
endOfEntry() {
endOfValue()
output.push(entry)
entry = []
}
tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm
// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
switch(state) {
case 0:
if(opening delimiter)
state = 1
break
if(new-line)
endOfEntry()
state = 0
break
if(un-delimited data)
value += match
state = 3
break
case 1:
if(second delimiter encountered)
state = 2
break
if(non-control char data)
value += match
state = 1
break
case 2:
if(escaped delimiter)
state = 1
break
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
case 3:
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
}
}
注意:这是要点,实际上还有很多需要考虑的地方。例如,错误检查,空值,尾随空白行(即有效)等。
在这种情况下,状态是正则表达式匹配块完成迭代时的条件。过渡表示为case语句。
作为人类,我们必须简化低级操作到更高级别的抽象,但与FSM工作的倾向正在工作的低水平操作。尽管状态和过渡非常容易单独处理,但是固有地很难一次可视化全部内容。我发现一遍又一遍地遵循各个执行路径,直到我可以直觉过渡如何进行为止。就像学习基础数学一样,您将无法从更高层次评估代码,直到低层次的细节开始变得自动化为止。
撇开:如果您看一下实际的实现,则遗漏了许多细节。首先,所有不可能的路径都会引发特定的异常。击中它们应该是不可能的,但是如果有任何损坏,它们绝对会在测试运行程序中触发异常。其次,“合法” CSV数据字符串中允许使用的解析器规则非常宽松,因此处理许多特定边缘情况所需的代码。不管那个事实,这是在所有错误修复,扩展和微调之前用来模拟FSM的过程。
与大多数设计一样,它不是实现的精确表示,而是概述了重要部分。在实践中,实际上从此设计派生了3种不同的解析器功能:特定于CSV的行拆分器,单行解析器和完整的多行解析器。它们都以相似的方式操作,它们处理换行符的方式也有所不同。
Java中的简单FSM
int i=0;
while (i<5) {
switch(i) {
case 0:
System.out.println("State 0");
i=1;
break;
case 1:
System.out.println("State 1");
i=6;
break;
default:
System.out.println("Error - should not get here");
break;
}
}
妳去 好的,它并不出色,但是可以说明这个想法。
您经常会在电信产品中找到FSM,因为它们为其他情况提供了简单的解决方案。
好,这是一个例子。假设您要解析一个整数。它会去像dd*
这里d
是一个整数位。
state0:
if (!isdigit(*p)) goto error;
p++;
goto state1;
state1:
if (!isdigit(*p)) goto success;
p++;
goto state1;
当然,正如@Gary所说,您可以goto
通过switch语句和状态变量来伪装这些。请注意,可以将此代码结构化,与原始正则表达式同构:
if (isdigit(*p)){
p++;
while(isdigit(*p)){
p++;
}
// success
}
else {
// error
}
当然,您也可以使用查找表来完成此操作。
有限状态机可以用多种方法制成,许多事物可以描述为有限状态机的实例。对于事物的思考,它不仅仅是一个概念。
FSM的一个示例是铁路网络。
火车可以在两个轨道之一上行驶,开关数量有限。
连接这些开关的轨道数量有限。
在任何时候,一列火车都在一条轨道上,可以根据单个输入信息,通过一个开关将其发送到另一条轨道。
Ruby中的有限状态机:
module Dec_Acts
def do_next
@now = @next
case @now
when :invite
choose_round_partner
@next = :wait
when :listen
@next = :respond
when :respond
evaluate_invites
@next = :update_in
when :wait
@next = :update_out
when :update_in, :update_out
update_edges
clear_invites
@next = :exchange
when :exchange
update_colors
clear_invites
@next = :choose
when :choose
reset_variables
choose_role
when :done
@next = :done
end
end
end
这就是分布式系统中单个计算节点的行为,它建立了基于链接的通信方案。或多或少。在“图形”形式中,它看起来像这样:
查看此链接,获取一些简单的词法分析(FSM)示例:
http://ironbark.bendigo.latrobe.edu.au/subjects/SS/clect/clect03.html
您也可以查看“龙书”作为示例(阅读不浅)
实际上,状态机通常用于:
一个示例是状态机,它扫描字符串以查看其语法是否正确。例如,荷兰邮政编码的格式为“ 1234 AB”。第一部分只能包含数字,第二部分只能包含字母。可以编写一个状态机来跟踪它是处于NUMBER状态还是处于LETTER状态,并且如果遇到错误的输入,则将其拒绝。
此受体状态机具有两个状态:数字状态和字母状态。状态机以数字状态启动,并开始读取要检查的字符串字符。如果在任何状态下都遇到无效字符,该函数将返回False值,并拒绝输入为无效。
Python代码:
import string
STATE_NUMERIC = 1
STATE_ALPHA = 2
CHAR_SPACE = " "
def validate_zipcode(s):
cur_state = STATE_NUMERIC
for char in s:
if cur_state == STATE_NUMERIC:
if char == CHAR_SPACE:
cur_state = STATE_ALPHA
elif char not in string.digits:
return False
elif cur_state == STATE_ALPHA:
if char not in string.letters:
return False
return True
zipcodes = [
"3900 AB",
"45D6 9A",
]
for zipcode in zipcodes:
print zipcode, validate_zipcode(zipcode)
资料来源: 实践中的(有限)状态机