【问题标题】:General Finite State Machine (Transducer) in ScalaScala 中的通用有限状态机(转换器)
【发布时间】:2013-08-16 02:50:09
【问题描述】:

在 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中画FSM-graph也很好

  3. Akka FSM 具有允许将某些数据与状态相关联的良好属性,而不仅仅是提供对象名称。这也值得赞赏。 (但是,Akka FSM 并不总是很方便使用,因为它是异步的,有时有点重。)

【问题讨论】:

  • FSM 在表示为相互递归函数时可以很漂亮。但是,真正的尾调用是关键,因此 Scala 不会削减它。为避免您的var,只需将下一个状态与消息一起返回,并继续将函数提供给自身。您正在有效地构造 State 类型。
  • Akka 框架有一个有用的状态机实现,但它相当依赖于在参与者系统周围发送消息。你可以阅读更多here

标签: scala state-machine fsm transducer


【解决方案1】:

这可能不是您想要的,但我认为这是一个有趣的概念。

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

您的用例将在很大程度上决定此概念中的代码结构。用例确实决定了您要保留多少类型信息。

我这个概念是因为状态是使用类型系统保持的,并且在编译时会报告非法转换。

【讨论】:

  • 有趣的方法。严格类型的状态。但是,我认为 Transformation 的签名应该是:case class Transformation[M <: message from state to f:m=""> (To, ResultMessage))
  • @ArseniyZhizhelev 我同意,我只是按照你的例子。从理论上讲,您可以将输出类型绑定到消息类型。真的取决于用例你想在 Scala 中使用类型多远,哈哈。
  • 非常有趣...但是一旦我回到 s1 或其他任何状态,我该如何检查它?让我们假设一个这样的方法:def typeOf[T: TypeTag](t: T) = reflect.runtime.universe.typeOf[T],我该由谁来确定典型FSM返回的状态/响应以便我可以采取行动? typeOf(s1) 匹配 { ??? }
猜你喜欢
  • 1970-01-01
  • 2019-04-01
  • 2015-07-28
  • 2016-07-21
  • 1970-01-01
  • 2011-03-06
  • 2023-03-27
  • 1970-01-01
  • 2016-08-19
相关资源
最近更新 更多