LoginSignup
6

More than 5 years have passed since last update.

posted at

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

有限オートマトン?

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をおぼえてでも使う価値はあるかも知れない。

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
What you can do with signing up
6