LoginSignup
5
6

More than 5 years have passed since last update.

Actorで作る有限オートマトン

Posted at

有限オートマトン?

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で、gotostayを返せば良い。
今回のサンプルでは破棄しているが、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をおぼえてでも使う価値はあるかも知れない。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6