gen_fsmがやっと分かった

あの説明じゃ、状態遷移をどうやって実装するのかが全然イメージ湧かないので、そこが結構苦労した。有限状態機械ってのは、状態と操作がいくつかあって、

f(元の状態, 操作) -> ( 次の状態, イベント出力 )

という状態遷移(写像)が、すべての状態について定義できる機械のこと。なので、この写像がgen_fsmの中でどういう風に実現されているかがまずは出発点。で、gen_fsmでは

状態A(操作x、状態変数)->
  イベント出力,
  {next_state, 状態B, 次の状態変数}; 
状態A(操作y、状態変数)->
  イベント出力,
  {next_state, 状態C, 次の状態変数}.
状態B(操作z、状態変数)->
  イベント出力,
  {next_state, 状態A, 次の状態変数};
....

てな感じである。操作zには、関数として定義されたものだけでなくてtimeoutなんていうものも使える。スレッドセーフで当然だし。すげーよOTP。


関係ないけど、最初はこっちのFSMのことかとおモタ。

A behaviour module for implementing a finite state machine. A generic finite state machine process (gen_fsm) implemented using this module will have a standard set of interface functions and include functionality for tracing and error reporting. It will also fit into an OTP supervision tree.

  • 参考:
  • コード:
    • 子供サンプル。最初は泣いていて、褒めると笑う。ほっとくとまたぐずって、褒めると笑う。
    • 笑ってるときにおどかすと泣く。
    • ぐずっているときにもっとほっとくと、やっぱり泣き出す。
-module(test_fsm).
-author('kuenishi@gmail.com').
-behaviour(gen_fsm).

%%finite state machine sample
%% see also: $ erl -man gen_fsm

%%  STATE DIAGRAM OF AN INFANT: any other state transition event is ignored. name that has ! is an event.
%%    
%%   +--------> crying <-------+
%%   |            |            |
%%   |            | praise!    | scare!
%%   |timeout     |            |
%%   | (10sec)    +-> happy ---+
%%   |                  | <----+
%%   |           timeout|      |
%%   |            (5sec)|      | praise!
%%   |                  |      | 
%%   +--------- sad <---+      |
%%               |             |
%%               +-------------+

%%> test_fsm:start().
%%> test_fsm:praise().
%%> test_fsm:scare().
%%> test_fsm:stop().

-compile(export_all).

%-module(code_lock).

start() ->
    gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []).

stop() -> gen_fsm:send_all_state_event(?MODULE, stop).

scare() ->
    gen_fsm:send_event(?MODULE, scare).
praise() ->
    gen_fsm:send_event(?MODULE, praise).

init(_) ->
    io:format("~p.~n",  [started]),
    {ok, crying, []}.

crying(praise, History) ->
    NewHistory = [crying|History],
    io:format("~p , ~p.~n", [crying, History]),
    {next_state, happy, NewHistory, 5000};
crying(_, History)->
    {next_state, sad, History}.

happy(scare, History)->
    NewHistory = [happy|History],
    io:format("~p , ~p.~n", [happy, History]),
    {next_state, crying, NewHistory};

happy(praise, History)->
    NewHistory = [happy|History],
    io:format("~p , ~p.~n", [happy, History]),
    {next_state, happy, History, 5000};

happy(timeout, History)->
    NewHistory = [happy|History],
    io:format("~p , ~p.~n", [happy, History]),
    {next_state, sad, NewHistory, 10000};
happy(_, History)->
    {next_state, happy, History, 5000}.

sad(praise, History)->
    NewHistory = [sad|History],
    io:format("~p , ~p.~n", [sad, History]),
    {next_state, happy, NewHistory, 5000};

sad(timeout, History) ->
    NewHistory = [sad|History],
    io:format("~p , ~p.~n", [sad, History]),
    {next_state, crying, NewHistory, 10000};
sad(_, History)->
    {next_state, sad, History, 10000}.

handle_event(stop, _StateName, StateData) ->
    {stop, normal, StateData}.
terminate(normal, _StateName, _StateData) ->
    ok.