【问题标题】:How to represent a simple finite state machine in Ocaml?如何在 Ocaml 中表示一个简单的有限状态机?
【发布时间】:2012-03-15 02:21:03
【问题描述】:

我用 C++ 和 Java 写过一些状态机,但从来没有用像 Ocaml 这样的函数式语言

问题是我不知道我是否可以从对象语言版本中调整代码,因为在 Ocaml 中,记录和变体比类更强大;

所以,我需要一个事件驱动的有限状态机(类似于 UML 中的分层),易于配置

有经验的人可以发布一个简单的示例吗?只是为了避免最常见的陷阱

谢谢:)

编辑 16/03:没有可变状态是否可以做到? 而且我想把它正确地封装在“FSM”这个名字下,我应该选择一个模块还是一个类?

【问题讨论】:

标签: functional-programming ocaml state fsm


【解决方案1】:

这取决于您必须如何操作 FSM,例如,您是否需要能够存储其状态并稍后继续,或者您是否只想立即执行它。在后一种情况下,将其作为一堆尾递归函数来完成是微不足道的。

例如,假设正则表达式C((A|B)*CD)*——以下相互递归函数是相应 FSM 的直接实现,它识别与此正则表达式匹配的列表(如果我没有犯任何错误:)):

type alphabet = A | B | C | D

let rec s1 = function
  | C :: rest -> s2 rest
  | _ -> false

and s2 = function
  | [] -> true
  | (A | B) :: rest -> s2 rest
  | C :: rest -> s3 rest
  | _ -> false

and s3 = function
  | D :: rest -> s2 rest
  | _ -> false

每个函数都对应于自动机的一个状态,并实现了它的转换函数。应用 s1 : alphabet list -> bool 将对参数运行 FSM。

PS:请注意这是一个展示尾调用优化的好处和优雅的应用程序...

【讨论】:

    【解决方案2】:

    通常,您创建对应于自动机状态的记录,并且您有另一种类型的事件触发转换到另一种状态。在状态记录中,您有一张地图可以找到每个事件的新状态。

    假设你的转换是由字符串触发的:

    type event = string
    
    module EventMap = Map.Make(struct
        type t = event
        let compare = compare
      end)
    
    type state = {
      state_info : ...; (* the content of that state, id, comment, etc. *)
      mutable state_transitions : state EventMap.t;
    }
    
    let next_state current_state event =
      try
        EventMap.find event current_state.state_transitions
      with Not_found -> current_state
    

    在这里,我假设未知事件保持在相同的状态,但您可能在记录中有错误状态...

    【讨论】:

    • 是否可以在没有可变状态的情况下做到这一点?而且我想以“FSM”的名义正确封装它,我应该选择一个模块还是一个类?
    【解决方案3】:

    这里有一个很好的答案,展示了 OCaml 在表示有限状态机方面的表现力和优雅性:

    automata in ocaml

    对于更严肃的使用,您可以尝试查看一些有限状态机库,例如 fsm library here

    【讨论】:

      【解决方案4】:

      我最近在 OCaml 中创建了一个 FSM 模块,您可以找到 here

      我对我的 FSM 实现有一些特殊要求,这可能使它看起来不像其他一些人在此处指出的那么好,但是,我认为您声明 FSM 本身的方式有点好和声明性。特殊要求是除了能够在 OCaml 版本中模拟 FSM 的操作之外,我还需要能够从 FSM 的声明性描述中生成 HDL(硬件描述语言)代码。正因为如此,我需要使用谓词表达式而不是转换函数(否则,我将如何将函数转换为字符串?)所以主要你想关注那里的 FSM 模块以及 create 和 eval_fsm 函数在那里。

      这是一个使用示例:

      (*********************************************************
       * FSM testing *******************************************
      *)
      
      (* inputs to the FSM *)
      let full         = Var({name ="full"; value  = F});;
      let ten_minutes  = Var({name = "ten_minutes"; value = F});;
      let empty        = Var({name = "empty"; value = F});;
      let five_minutes = Var({name = "five_minutes"; value =F});;
      
      
      (* T is true,    F is false *)
      let _ = 
        assign full         F ;
        assign ten_minutes  F ;
        assign empty        F ;
        assign five_minutes F ;;
      
      (* outputs from the FSM *)
      let water_on     = Var({name = "water_on";    value = F});;
      let agitate      = Var({name = "agitate";     value = F});;
      let drain        = Var({name = "drain"  ;     value = F});;
      let start_timer  = Var({name = "start_timer"; value = F});;
      let motor_on     = Var({name = "motor_on";    value = F});;
      let washed       = Var({name = "washed";    value = F});;
      let soap         = Var({name = "soap";        value = F});;
      
      let reset_actions = 
        assign water_on      F;
        assign agitate       F;
        assign drain         F;
        assign start_timer   F;
        assign motor_on      F;;
      
      module WashStates = 
        struct
         type t =  START | FILL | WASH | DRAIN |  RINSE | SPIN | STOP
           deriving(Show, Enum)    
         let start_state = START
        end 
      
      module LogicExp = 
        struct
          type t     = boolean Logic.bexp
          type var_t = boolean Logic.variable
          let eval_exp exp = to_bool (Logic.eval exp)
          let var_to_s     = var_to_s
        end
      
      module WashFSM = FSM(WashStates)(LogicExp) 
      
      open WashStates
      
      (* declare the state table *)
      (*   CS,   PREDICATE,               NS,    ACTIONs *)
      let my_fsm = [
        (START, Const(T),                 FILL, [(water_on,   T); 
                                                 (soap,       T)]);
        (FILL,  Bop(And,full,soap),       WASH, [(water_on,   F);
                                                 (agitate,    T);
                                                 (washed,     T);
                                                 (start_timer,T)]);
        (WASH,  ten_minutes,              DRAIN,[(agitate,    F);
                                                 (start_timer,F); 
                                                 (empty,      T)]); 
        (DRAIN, Bop(And,empty,soap),      FILL, [(drain,      F); 
                                                 (soap,       F);
                                                 (water_on,   T)] );
        (FILL,  Bop(And,full,Not(soap)),  RINSE,[(water_on,   F); 
                                                 (soap,       F);
                                                 (empty,      F);
                                                 (agitate,    T)]);
        (RINSE, ten_minutes,              DRAIN, [(agitate,   F);
                                                  (empty,     T)] );
        (DRAIN, Bop(And,empty,Not(soap)), SPIN,  [(motor_on,  T);
                                                  (start_timer,T)]);
        (SPIN,  five_minutes,             STOP,  [(water_on,  F);
                                                  (drain,     F);
                                                  (start_timer,F);
                                                  (motor_on,  F)]);
        (STOP,  Const(T),                 STOP,  [(motor_on,  F)]);
      ];; 
      
      
      let st_table, current_state = WashFSM.create my_fsm in
      
      let _ = assign full T in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let _ = assign ten_minutes T in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let _ = (assign ten_minutes F);(assign empty T) in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      
      let _ = assign five_minutes T in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let _ = assign five_minutes F in
      let _ = assign ten_minutes T in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let current_state = WashFSM.eval_fsm st_table current_state  in
      let _ = assign five_minutes T in
      let _ = WashFSM.eval_fsm st_table current_state  in
      (*...and so on...*)
      

      (请原谅“;;”结尾 - 我希望能够将此代码剪切并粘贴到 REPL 中)

      这里使用的一些代码可以在我的 github 上的 Logic project 中找到(fsm.ml 是该项目的一部分)。谓词表达式的计算结果为 T 或 F(真或假)。如果为真,则从当前状态转换到下一个状态。 Const T 表示总是转换。如下表达式:

      Bop(And, full, soap) 
      

      表示如果 fullsoap 均为 T (true),则表达式的计算结果为 true。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-20
        • 2012-03-09
        • 2011-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多