2013-08-14 13 views
10

Scalaで有限状態機械(または有限状態トランスデューサ)を実装する一般的な方法は何ですか?Scalaの一般的な有限状態機械(トランスデューサ)

多くの場合、自分自身がステートマシンの実装を必要としていることがよくあります。私の典型的な実装では、私は嫌い何

object TypicalFSM { // actually — finite state transducer 
    type State 
    case object State1 extends State 
    case object State2 extends State 
    type Message 
    case object Message1 extends Message 
    type ResultMessage 
    case object ResultMessage1 extends ResultMessage 
} 

import TypicalFSM._ 

class TypicalFSM extends ((Message) =>Seq[ResultMessage]){ 
    var state:State = State1 

    def apply(message:Message):Seq[ResultMessage] = (state, message) match { 
    case (State1, Message1) => 
     state = State2 
     Seq(ResultMessage1, ResultMessage2) 
    } 
} 

のように見えるソリューションのスレッドが危険な状態になるその変更可能varです。また、FSMトポロジーは明確ではありません。

  1. 機能的な方法でFSMを作成するにはどうすればよいですか?

  2. また.dot format

  3. にFSM-グラフを描画するために非常に良いでしょうアッカFSMだけではなく、オブジェクト名を与え、国家といくつかのデータを関連付けすることを可能にする優れた特性を有しています。これも感謝しています。 (ただし、アッカFSMは、それが非同期で、時には少し重いですとして使用するために、常に便利ではありません。)

+0

FSMは、相互に再帰的な関数として表現するときれいになります。本当のテールコールがそこに鍵となるので、Scalaはそれをカットしません。あなたの 'var'を避けるためには、メッセージとともに次の状態を返して、関数をそれ自身に送り続けてください。あなたは効果的に 'State'型を構築しています。 –

+0

Akkaフレームワークは有用なステートマシン実装を持っていますが、アクターシステムの周りにメッセージを送信することに依存しています。もっと読む[ここ](http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

答えて

7

これは、あなたが探しているものはおそらくありませんが、私はそれが面白いコンセプトだと思います。

object TypicalFSM { 

    sealed trait State 
    final class State1 extends State 
    final class State2 extends State 

    sealed trait Message 
    case class Message1(s: String) extends Message 
    case class Message2(s: String) extends Message 

    sealed trait ResultMessage 
    object ResultMessage1 extends ResultMessage 
    object ResultMessage2 extends ResultMessage 
} 

import TypicalFSM._ 

case class Transformation[M <: Message, From <: State, To <: State](
    f:M => Seq[ResultMessage]) { 

    def apply(m:M) = f(m) 
} 

object Transformation { 

    implicit def `message1 in state1` = 
    Transformation[Message1, State1, State2] { m => 
     Seq(ResultMessage1, ResultMessage2) 
    } 

    implicit def `message1 in state2` = 
    Transformation[Message1, State2, State2] { m => 
     Seq(ResultMessage1) 
    } 

    implicit def `message2 in state2` = 
    Transformation[Message2, State2, State1] { m => 
     Seq(ResultMessage2) 
    } 
} 

class TypicalFSM[CurrentState <: State] { 

    def apply[M <: Message, NewState <: State](message: M)(
    implicit transformWith: Transformation[M, CurrentState, NewState]) = { 

    this.asInstanceOf[TypicalFSM[NewState]] -> transformWith(message) 
    } 
} 

使い方は次のようになります:

def test() = { 
    val s1 = new TypicalFSM[State1] 
    // type of s1: TypicalFSM[State1] 

    val (s2, r1) = s1(Message1("m1")) 
    // type of s2: TypicalFSM[State2] 

    val (s3, r2) = s2(Message1("m1")) 
    // type of s2: TypicalFSM[State2] 

    val (s4, r3) = s2(Message2("m2")) 
    // type of s2: TypicalFSM[State1] 

    // val (s5, r4) = s4(Message2("m2")) 
    // Fails with: 
    // 'No transformation available for TypicalFSM.Message2 in TypicalFSM.State1' 
    // type of s5: TypicalFSM[State1] 
} 

ご利用の場合は強く、この概念にコードの構造を決定するであろう。ユースケースは実際に保持したいタイプ情報の量を決定します。

この概念は、型システムを使用して状態が維持され、コンパイル時に不正な遷移が報告されるためです。

+1

興味深いアプローチ。厳密に型指定された状態。しかし、私は変換の署名は次のようにしなければならないと考えています: ケースクラス変換[M <:メッセージ、<:状態、宛先<:状態]( f:M =>(To、ResultMessage)) –

+1

@ArseniyZhizhelev私はちょうどあなたの例に従った。理論的には、出力の種類をメッセージの種類にバインドすることができます。本当にあなたがScalaの型でどれだけ遠くに行きたいのかというユースケースに依存します。 – EECOLOR

+0

非常に面白いです...しかし、いったんs1やその他の状態に戻ったら、どうやってチェックしますか?このような方法を仮定しましょう:def typeOf [T:TypeTag](t:T)= reflect.runtime.universe.typeOf [T]、私は動作を取ることができるようにTypicalFSMによって返される状態/応答を決定しますか? typeOf(s1)match {??? } – j3d