Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

有限オートマトン?

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした