How to represent a simple finite state machine in Ocaml?

后端 未结 4 1413
青春惊慌失措
青春惊慌失措 2021-02-04 08:24

I have written some state machine in C++ and Java but never in a functional language like Ocaml

Problem is I don\'t know if I can just adapt code from the object languag

4条回答
  •  没有蜡笔的小新
    2021-02-04 08:36

    I recently created an FSM module in OCaml which you can find here

    I have some special requirements for my FSM implementation which could make it not quite as nice to look at as some of the others pointed out here, however, I think the way you declare the FSM itself is kind of nice and declarative. The special requirement is that I need to be able to generate code in HDL (hardware description language) from a declarative description of the FSM in addition to being able to simulate the FSM's operation in the OCaml version. Because of this I needed to use predicate expressions instead of transition functions (otherwise, how would I translate a function to a string?) So mainly you want to focus on the FSM module there and the create and eval_fsm functions there.

    Here is an example of usage:

    (*********************************************************
     * 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...*)
    

    (Please excuse the ";;" endings - I wanted to be able to cut & paste this code into the REPL)

    Some of the code used here is found in the Logic project on my github (fsm.ml is part of that project). The predicate expression evaluates to either T or F (true or false). If true, then the transition is made from current state to next state. Const T means always transition. An expression such as:

    Bop(And, full, soap) 
    

    Means that if both full and soap are T (true) then the expression evaluates to true.

提交回复
热议问题