Actorで作る有限オートマトン
2016-08-24
QiitaScalaAkkaactor有限オートマトン?
Finite State Machine(FSM) ざっくり言うとイベントを受け取って状態遷移するステートマシン。 詳しくはWikipedia参照
FSM を Actor で実装する
まさにそのためのakka.actor.FSM
があるので、これを使って実装する。
FSM に必要な型を定義する
必要となるのは以下の 3 つの型
State
- FSM の状態
Data
- FSM が内部的に持つ情報
Event
- FSM の状態遷移をキックするもの
今回は信号を FSM で実装してみる。
state.scala
sealed trait SignalState
case object Red extends SignalState
case object Green extends SignalState
case object Yellow extends SignalState
data.scala
sealed trait SignalData
object SignalData {
sealed case class SignalColor(value: String) extends SignalData
val RedData = SignalColor("red")
val YellowData = SignalColor("yellow")
val GreenData = SignalColor("green")
}
event.scala
sealed trait SignalEvent
case object ChangeSignal extends SignalEvent
case object RetainSignal extends SignalEvent
FSM を実装する
先ほど用意した State/Data/Event を利用して FSM を実装する。 FSM 自体の実装は DSL だけで完結するため非常に楽。
signalactor.scala
class SignalChangeFSMActor extends Actor with FSM[SignalState, SignalData] {
// 初期状態
startWith(Red, RedData)
// Eventを受け取った時の状態遷移
when(Green) {
case Event(ChangeSignal, _) => goto(Yellow) using YellowData
case Event(RetainSignal, _) => stay
}
when(Yellow) {
case Event(ChangeSignal, _) => goto(Red) using RedData
case Event(RetainSignal, _) => stay
}
when(Red) {
case Event(ChangeSignal, _) => goto(Green) using GreenData
case Event(RetainSignal, _) => stay
}
// 状態遷移中の処理
onTransition {
case Green -> Yellow =>
println(s"WARN! green -> yellow: $stateData")
case Yellow -> Red =>
println(s"CAUTION! yellow -> red: $stateData")
case Red -> Green =>
println(s"OK! red -> green: $stateData")
}
// Actorが終了する際の処理
onTermination {
case StopEvent(_, _, _) =>
println("Shutting down FSM...")
}
// 初期化
initialize()
}
FSM の根幹となる状態遷移は、when
の引数に対するPartialFunction
で、goto
かstay
を返せば良い。
今回のサンプルでは破棄しているが、Event
の第二引数は現在の状態を表すstateData
と同じものが入ってくるらしい。
FSM な Actor を使う
通常の Actor と同様に生成してメッセージを送信すれば良い。
object ApplicationMain extends App {
val system = ActorSystem("MyActorSystem")
val signalActor = system.actorOf(Props[SignalChangeFSMActor], "fsm-signal")
signalActor ! ChangeSignal
signalActor ! RetainSignal
signalActor ! ChangeSignal
signalActor ! RetainSignal
signalActor ! ChangeSignal
signalActor ! RetainSignal
signalActor ! ChangeSignal
signalActor ! RetainSignal
Thread.sleep(3000)
system.terminate()
}
実行すると以下のように出力される。
OK! red -> green: SignalColor(red)
WARN! green -> yellow: SignalColor(green)
CAUTION! yellow -> red: SignalColor(yellow)
OK! red -> green: SignalColor(red)
Shutting down FSM...
所感
Actor の場合、context.become
とかcontext.unbecome
で状態を切り替えることが可能なため、わざわざakka.actor.FSM
を使わなくても同じようなことは実現できる。
akka.actor.FSM
なら DSL で状態遷移とイベントハンドラを宣言的に記述できるので、やや状態遷移が複雑なものなどは DSL をおぼえてでも使う価値はあるかも知れない。
from: https://qiita.com/petitviolet/items/9a6899a14a5219b43c5a