it-swarm.cn

有限状态机的例子

我正在寻找有限状态机的好例子。语言并不是特别重要,只是很好的例子。

代码实现很有用(通用伪代码),但是收集FSM的各种用法也非常有用。

示例不一定需要基于计算机,例如Mike Dunlavey的Railroad网络示例非常有用。

26
ocodo

安全(事件已触发)

  • States:多个“锁定”状态,一个“解锁”状态
  • 过渡:正确的组合键/键会将您从初始锁定状态移到更接近解锁状态的锁定状态,直到最终解锁。不正确的组合键/键会使您回到初始锁定状态(有时称为idle

交通灯(触发时间|触发传感器[事件])

  • 状态:红色,黄色,绿色(最简单的示例)
  • 转换:在计时器后,将RED更改为GREEN,将GREEN更改为YELLOW,将YELLOW更改为RED。也可以在各种(更复杂)状态下感应汽车时触发。

自动售货机(事件已触发,安全的变体)

  • 状态:IDLE,5_CENTS,10_CENTS,15_CENTS,20_CENTS,25_CENTS等,VEND,CHANGE
  • 转换:插入硬币,纸币后状态发生变化,在正确购买金额(或更多)后转换为VEND,然后转换为CHANGE或IDLE(取决于您的自动贩卖机有多道德
28
aqua

边界网关协议示例

BGP是支持Internet上核心路由决策的协议。它维护一个表来确定来自给定节点的主机的可达性,并使互联网真正地分散化。

在网络中,每个 [〜#〜] bgp [〜#〜] 节点都是对等节点,并使用有限状态机,状态机为六个状态之一Idle,- 连接活动OpenSentOpenConfirm已建立。网络中的每个对等连接都维护以下状态之一。

BGP协议确定发送到对等方以更改其状态的消息。

BPG状态图。

BGP statechart

第一个状态空闲。在这种状态下,BGP初始化资源,拒绝入站连接尝试,并启动与对等方的连接。

连接

第二种状态Connect。在此状态下,路由器等待连接完成,如果成功,则转换到OpenSent状态。如果不成功,它将重置ConnectRetry计时器,并在到期时转换为Active状态。

活性

活动状态下,路由器将ConnectRetry计时器重置为零,并返回到Connect状态。

开放发送

OpenSent状态下,路由器发送一个Open消息,并等待返回一个消息。交换Keepalive消息,并在成功接收后将路由器置于已建立状态。

成立时间

已建立状态下,路由器可以发送/接收:Keepalive;更新;与对等方之间的通知消息。

更多 有关BGP的信息在Wikipedia上

14
ocodo

它们对于建模各种事物很有用。例如,选举周期可以按照(正常政府)-选举称为->(早期竞选)-议会解散->(繁重竞选)-选举->(投票计数)的州来建模)。然后要么(投票计数)-不占多数->(联盟谈判)-达成协议->(正常政府),要么(投票计数)-多数->(正常政府)。我在带有政治子游戏的游戏中实现了该方案的变体。

它们也被用于游戏的其他方面:人工智能通常基于状态。菜单和级别之间的转换以及死亡或完成级别时的转换通常由FSM很好地建模。

7
Peter Taylor

jquery-csv 插件中使用的CSV解析器

这是一个基本的Chomsky Type III语法 解析器。

正则表达式标记器用于逐个字符地评估数据。当遇到控制字符时,代码将被传递到switch语句,以根据启动状态进行进一步评估。将非控制字符进行分组和批量复制,以减少所需的字符串复制操作次数。

标记器:

var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;

第一组匹配项是控制字符:值定界符(“),值分隔符(,)和条目分隔符(换行符的所有变体)。最后一个匹配项处理非控制字符分组。

解析器必须满足10条规则:

  • 规则#1-每行一个条目,每行以换行符结尾
  • 规则#2-省略文件末尾的尾随换行符
  • 规则#3-第一行包含标头数据
  • 规则4-将空格视为数据,并且条目不应包含结尾逗号
  • 规则#5-行可以或不可以用双引号定界
  • 规则#6-包含换行符,双引号和逗号的字段应用双引号引起来
  • 规则#7-如果使用双引号将字段括起来,则必须在字段内部出现双引号,并在其前面加上另一个双引号来对其进行转义
  • 修改#1-未引用字段可能会也可能会
  • 修改#2-引用的字段可能会也可能不会
  • 修改#3-条目中的最后一个字段可能包含或可能不包含空值

注意:前7条规则直接来自 IETF RFC 418 。添加了最后3个以覆盖现代电子表格应用程序(例如Excel,Google Spreadsheet)引入的Edge案例,这些案例默认情况下不会定界(即引用)所有值。我尝试将对RFC的更改回馈,但尚未听到对我的询问的回应。

结束了,如下图:

CSV parser finite state machine

状态:

  1. 条目和/或值的初始状态
  2. 开场报价
  3. 遇到第二个报价
  4. 遇到未报价的值

转换:

  • 一个。检查引号(1),未引号(3),空值(0),空条目(0)和新条目(0)
  • b。检查第二个报价字符(2)
  • c。检查转义的引号(1),值的结尾(0)和条目的结尾(0)
  • d。检查值的结尾(0)和条目的结尾(0)

注意:它实际上缺少状态。从'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语句。

作为人类,我们倾向于将低级操作简化为高级摘要,但是使用FSMis进行低级操作。尽管状态和过渡非常容易单独处理,但是固有地很难一次可视化全部内容。我发现,一遍又一遍地遵循各个执行路径,直到我可以直觉过渡如何进行,才是最容易的。就像学习基础数学一样,您将无法从更高层次评估代码,直到低层次的细节开始变得自动化为止。

旁:如果您查看实际的实现,则会遗漏许多细节。首先,所有不可能的路径都会引发特定的异常。击中它们应该是不可能的,但是如果有任何损坏,它们绝对会在测试运行程序中触发异常。其次,“合法” CSV数据字符串中允许的解析器规则非常宽松,因此处理许多特定Edge案例所需的代码。不管那个事实,这是在所有错误修复,扩展和微调之前模拟FSM的过程。

与大多数设计一样,它不是实现的精确表示,而是概述了重要部分。实际上,从该设计派生出实际上有3种不同的解析器功能:特定于CSV的行拆分器,单行解析器和完整的多行解析器。它们都以类似的方式操作,它们处理换行符的方式也有所不同。

4
Evan Plaice

我发现思考/建模电梯(电梯)是有限状态机的一个很好的例子。它几乎不需要引入任何内容,但是提供的实现条件却很琐碎。

这些状态例如是在一楼,一楼等,并且将地面移动到第一层,或者将第三层移动到底层,但当前在3到2层之间,依此类推。

电梯轿厢中和地板上的按钮效果本身提供了输入,其效果取决于同时按下的按钮和当前状态。

除顶部和底部外,每个楼层都有两个按钮:一个请求升降机上升,另一个请求下降。

3
jan

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,因为它们为其他情况提供了简单的解决方案。

3
Gary Rowe

好,这是一个例子。假设您要解析一个整数。它会像dd*,其中d是整数。

state0:
    if (!isdigit(*p)) goto error;
    p++;
    goto state1;
state1:
    if (!isdigit(*p)) goto success;
    p++;
    goto state1;

当然,正如@Gary所说,您可以通过switch语句和状态变量来伪装gotos。请注意,可以将此代码结构化,与原始正则表达式同构:

if (isdigit(*p)){
    p++;
    while(isdigit(*p)){
        p++;
    }
    // success
}
else {
    // error
}

当然,您也可以使用查找表来完成此操作。

有限状态机可以用多种方法制成,许多事物可以描述为有限状态机的实例。对于事物的思考,它不仅仅是一个概念。

铁路网示例

FSM的一个示例是铁路网络。

火车可以在两个轨道之一上行驶,开关数量有限。

连接这些开关的轨道数量有限。

在任何时候,一列火车都在一条轨道上,可以根据单个输入信息,通过一个开关将其发送到另一条轨道。

2
Mike Dunlavey

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

这就是分布式系统中单个计算节点的行为,它建立了基于链接的通信方案。或多或少。在“图形”形式中,它看起来像这样:

enter image description here

2
philosodad

查看此链接,获取一些简单的词法分析(FSM)示例:

http://ironbark.bendigo.latrobe.edu.au/subjects/SS/clect/clect03.html

您还可以查看“龙皮书”作为示例(阅读不浅)

http://zh.wikipedia.org/wiki/编译器:_原理,_技术,_和_工具

1
jmq

实际上,状态机通常用于:

  • 设计目的(为程序中的不同动作建模)
  • 自然语言(语法)解析器
  • 字符串解析

一个示例是状态机,它扫描字符串以查看其语法是否正确。例如,荷兰邮政编码的格式为“ 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)

资料来源: (实际上是(Finite-)状态机

0
theD