2013-08-14 1 views
10

스칼라에서 유한 상태 기계 (또는 유한 상태 변환기)를 구현하는 일반적인 방법은 무엇입니까?스칼라의 일반적인 유한 상태 기계 (변환기)

종종 나는 상태 머신 구현을 필요로합니다. 내가 싫어하는 것은 솔루션 스레드가 안전하지 않은하게 변경할 수 var입니다

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

처럼 내 전형적인 구현 보인다. 또한 FSM 토폴로지가 명확하지 않습니다.

  1. FSM을 기능적으로 만드는 방법은 무엇입니까?

  2. 또한 아주 좋은 것은 .dot format

  3. Akka FSM뿐만 아니라 객체 이름을주는 국가로 일부 데이터를 연결할 수의 좋은 특성을 가지고에 FSM-그래프를 그립니다. 이것도 인정합니다. 이 당신을 위해 무엇을 찾고있는 아마하지

+0

FSM은 상호 재귀 적 기능으로 표현할 때 아름답습니다. 진짜 꼬리 호출은 거기서 중요하다. 그래서 Scala는 그것을 잘라 내지 않을 것이다. 당신의'var'을 피하기 위해서, 메세지들과 함께 다음 상태를 리턴하고, 그 함수를 계속 먹이십시오. 당신은 효과적으로'State' 타입을 만들고 있습니다. –

+0

Akka 프레임 워크는 유용한 상태 시스템 구현을 가지고 있지만 액터 시스템을 중심으로 메시지를 보내는 것에 의존합니다. 자세한 내용 [여기] (http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

답변

7

(가끔 조금 무거운 무게 비동기 및 그러나, Akka FSM은. 항상 사용하기 편리하지 않습니다),하지만 난 그게 흥미로운 개념 생각합니다.

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 방금 당신의 모범을 따랐습니다. 이론적으로 출력 유형을 메시지 유형에 바인딩 할 수 있습니다. 실제로 스칼라에서 타입과 함께 가고 싶은 유스 케이스에 따라 다르다. – EECOLOR

+0

매우 흥미 롭습니다.하지만 일단 s1이나 다른 상태로 돌아 가면 어떻게 확인합니까? 다음과 같은 메소드를 가정 해 봅시다 : def typeOf [T : TypeTag] (t : T) = reflect.runtime.universe.typeOf [T], 누가 행동을 취할 수 있도록 TypicalFSM에 의해 반환 된 상태/응답을 결정합니까? typeOf (s1) match {??? } – j3d

관련 문제