Подтвердить что ты не робот

Как представить простой конечный автомат в Ocaml?

Я написал некоторую машину состояний в С++ и Java, но никогда не на функциональном языке, как Ocaml

Проблема в том, что я не знаю, могу ли я просто адаптировать код из версий объектов, поскольку в Ocaml записи и варианты более мощные, чем класс;

Итак, мне нужен конечный конечный автомат, управляемый событиями (иерархический, как в UML), легко настраиваемый

Может ли кто-нибудь, кто испытал на поле, опубликовать простой пример? Просто чтобы избежать наиболее распространенных ловушек

спасибо:)

EDIT 16/03: Можно ли это сделать без изменчивого состояния?      И я хотел бы инкапсулировать его правильно под именем "FSM", должен ли я выбрать модуль или класс?

4b9b3361

Ответ 1

Обычно вы создаете запись, соответствующую состоянию автоматов, и у вас есть другой тип события, инициирующего переход в другое состояние. В записи состояния у вас есть карта, чтобы найти для каждого события новое состояние.

Предположим, что ваши переходы запускаются по строкам:

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

Здесь я предположил, что неизвестные события остаются в одном состоянии, но вы можете иметь состояние ошибки в записи...

Ответ 2

Это зависит от того, как вы должны управлять FSM, например, если вам нужно сохранить его состояние и продолжить позже, или если вы просто хотите его немедленно выполнить. В последнем случае тривиально делать это как кучу хвостовых рекурсивных функций.

Например, предположим, что regexp 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: Обратите внимание, как это приложение, демонстрирующее преимущество и элегантность оптимизации хвостового вызова...

Ответ 3

Существует отличный ответ, который демонстрирует выразительность и элегантность OCaml в представлении конечного конечного автомата здесь:

автоматы в ocaml

Для более серьезного использования вы можете попытаться взглянуть на некоторую библиотеку конечных автоматов, такую ​​как fsm библиотека здесь.

Ответ 4

Недавно я создал модуль FSM в OCaml, который вы можете найти здесь

У меня есть некоторые особые требования к моей реализации FSM, которые могли бы не совсем приятно смотреть на некоторые из других, упомянутых здесь, однако, я думаю, что способ, которым вы объявляете сам FSM, является добрым и декларативным. Особое требование состоит в том, что мне нужно иметь возможность генерировать код в HDL (язык описания аппаратных средств) из декларативного описания FSM в дополнение к возможности имитировать операцию FSM в версии OCaml. Из-за этого мне нужно было использовать предикатные выражения вместо функций перехода (в противном случае, как бы преобразовать функцию в строку?) Поэтому в основном вы хотите сосредоточиться на модуле 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)

Некоторая часть используемого здесь кода находится в Logic project на моем github (fsm.ml является частью этого проекта). Выражение предиката оценивается либо T, либо F (true или false). Если true, то переход осуществляется из текущего состояния в следующее состояние. Const T означает всегда переход. Выражение, такое как:

Bop(And, full, soap) 

Означает, что если и full, и soap являются T (true), тогда выражение оценивается как true.