ズンドコキヨシをErlangのgen_fsmを使って書いた
- FSMが保持するStateDataを使わないバージョンを追加
そういえばズンドコキヨシは gen_fsm のイイサンプルになるとおもうので、誰か書きませんか。
— V (@voluntas) March 18, 2016
ということで書いてみた。graceful exitがなかなか思うようにいかなかったし、コールバック関数も全部実装していないのでコンパイル時にWarningが出たまんま。Unicodeのformatは~ts
、get_stateは文字列の比較で頑張ってたけど、ちゃんとatomが返ってきてた*1。そもそもget_state使うべきでは無い気がするけど、graceful exitがうまく行かなかったのでこのやり方としている。
-module(zundokokiyoshi). -behaviour(gen_fsm). -compile(export_all). start_link(COMP) -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []). init(COMP) -> {ok, zundoko, {[], COMP}}. finish_song() -> gen_fsm:send_all_state_event(?MODULE, stop). sing_phrase(Phrase) -> gen_fsm:send_event(?MODULE, {sing, Phrase}). zundoko({sing, Phrase}, {SoFar, COMP}) -> case lists:sublist([Phrase|SoFar],5) of COMP -> io:format("ドコ~nキヨシ!~n"), {next_state, finish, {[], COMP}}; ["ドコ"|_Tail] -> io:format("ドコ~n"), {next_state, zundoko, {[], COMP}}; Else -> io:format("~ts~n",[Phrase]), {next_state, zundoko, {Else, COMP}} end. handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok. take_phrase() -> Zd = ["ズン","ドコ"], Index = random:uniform(length(Zd)), lists:nth(Index,Zd). start_song() -> start_link(["ズン","ズン","ズン","ズン","ドコ"]), random:seed( os:timestamp() ), sing_song(). sing_song() -> sing_phrase( take_phrase() ), timer:sleep(200), case sys:get_state(?MODULE) of {finish,_} -> finish_song(); _ -> sing_song() end.
- SoFarを使わずに全て状態遷移で実現(こちらの方がFSMを使う良い例ぽい)
-module(zundokokiyoshi). -behaviour(gen_fsm). -compile(export_all). start_link(COMP) -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, lists:reverse(COMP), []). init(COMP) -> {ok, zun1, {[], COMP}}. finish_song() -> gen_fsm:send_all_state_event(?MODULE, stop). sing_phrase(Phrase) -> io:format("~ts~n",[Phrase]), gen_fsm:send_event(?MODULE, {sing, Phrase}). zun1({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun2, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun2({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun3, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun3({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, zun4, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. zun4({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, doko, {[], COMP}}; "ドコ" -> {next_state, zun1, {[], COMP}} end. doko({sing, Phrase}, {_, COMP}) -> case Phrase of "ズン" -> {next_state, doko, {[], COMP}}; "ドコ" -> io:format("キヨシ!~n"), {next_state, finish, {[], COMP}} end. handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok. take_phrase() -> Zd = ["ズン","ドコ"], Index = random:uniform(length(Zd)), lists:nth(Index,Zd). start_song() -> start_link(["ズン","ズン","ズン","ズン","ドコ"]), random:seed( os:timestamp() ), sing_song(). sing_song() -> sing_phrase( take_phrase() ), timer:sleep(200), case sys:get_state(?MODULE) of {finish,_} -> finish_song(); _ -> sing_song() end.
$ erl Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Eshell V7.1 (abort with ^G) 1> c(zundokokiyoshi). zundokokiyoshi.erl:2: Warning: undefined callback function code_change/4 (behaviour 'gen_fsm') zundokokiyoshi.erl:2: Warning: undefined callback function handle_info/3 (behaviour 'gen_fsm') zundokokiyoshi.erl:2: Warning: undefined callback function handle_sync_event/4 (behaviour 'gen_fsm') {ok,zundokokiyoshi} 2> zundokokiyoshi:start_song(). ドコ ズン ドコ ドコ ズン ズン ドコ ズン ズン ズン ドコ ドコ ズン ズン ドコ ズン ドコ ドコ ズン ズン ズン ドコ ズン ズン ズン ズン ドコ キヨシ! ok 3>
*1:これ見る人が見るとStateはatomだと分かるのだろうか?センスの問題かもしれないけれど。 http://erlang.org/doc/man/sys.html#get_state-1