2013-08-14 11 views
10

Was ist der allgemeine Weg, um eine endliche Zustandsmaschine (oder endlicher Zustandswandler) in Scala zu implementieren?Allgemeine endliche Maschine (Transducer) in Scala

Ich bin oft auf die Implementierung von Zustandsautomaten angewiesen. Meine typische Implementierung sieht aus wie

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) 
    } 
} 

Was ich nicht mag die wandelbar var ist, die die Lösung Faden unsicher macht. Auch die FSM-Topologie ist nicht klar.

  1. Wie können Sie FSMs funktional erstellen?

  2. Es wäre auch sehr gut sein FSM-Grafik zu zeichnen in .dot format

  3. Akka FSM hat eine gute Eigenschaft, so dass einige Daten mit einem Staat zu verbinden, nicht nur einen Objektnamen zu geben. Dies wird auch geschätzt. (Allerdings ist Akka FSM nicht immer bequem zu verwenden, wie es asynchron ist und manchmal etwas schwergewichtige.)

+0

FSMs können schön sein, wenn sie als gegenseitig rekursive Funktionen ausgedrückt werden. Reale Tail-Calls sind jedoch der Schlüssel, also wird Scala es nicht schneiden. Um Ihre 'var' zu vermeiden, geben Sie einfach den nächsten Zustand zusammen mit den Nachrichten zurück und füttern Sie die Funktion in sich hinein. Sie erstellen effektiv den Typ "State". –

+0

Das Akka-Framework hat eine nützliche State-Machine-Implementierung, ist aber eher abhängig vom Senden von Nachrichten um ein Aktorsystem. Sie können mehr lesen [hier] (http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

Antwort

7

Dies ist wahrscheinlich nicht das, was Sie suchen, aber ich denke, es ist ein interessantes Konzept.

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) 
    } 
} 

Verbrauch würde so aussehen:

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] 
} 

Ihr Anwendungsfall stark die Struktur des Codes in diesem Konzept bestimmen würde. Der Anwendungsfall bestimmt wirklich, wie viele Typinformationen Sie behalten möchten.

Ich dieses Konzept, weil der Zustand mit dem Typsystem beibehalten wird und dass illegale Übergänge zur Kompilierzeit gemeldet werden.

+1

Interessanter Ansatz. Streng typisierte Staaten. Ich denke jedoch, dass Signatur der Transformation sein sollte: Fallklasse Transformation [M <: Nachricht, von <: Zustand, bis <: Status] ( f: M => (An, ResultMessage)) –

+1

@ArseniyZhizhelev Ich stimme zu, Ich bin einfach deinem Beispiel gefolgt. Theoretisch könnten Sie die Art der Ausgabe an den Nachrichtentyp binden. Kommt wirklich auf den Anwendungsfall an, wie weit man mit Scala-Typen gehen will, haha. – EECOLOR

+0

Sehr interessant ... aber sobald ich zurück s1 oder was auch immer sonst Zustand, wie überprüfe ich es? Nehmen wir eine Methode wie folgt an: def typeOf [T: TypTag] (t: T) = reflect.runtime.universe.typeOf [T], wer bestimmt den Status/die Antwort, die von TypicalFSM zurückgegeben wird, damit ich eine Aktion ausführen kann? typeOf (s1) entspricht {??? } – j3d

Verwandte Themen