유한 상태 머신의 예 [닫힘]


25

유한 상태 머신의 좋은 예를 찾고 있습니다. 언어는 특별히 중요하지 않고 좋은 예일뿐입니다.

코드 구현은 유용하지만 (일반화 된 의사 코드) FSM의 다양한 용도를 수집하는 것도 매우 유용합니다.

예를 들어 컴퓨터 기반 일 필요는 없습니다. 예를 들어 Mike Dunlavey의 Railroad 네트워크 예제는 매우 유용합니다.


12
정규식은 유한 상태 머신입니다.
chrisaycock

5
왜이 질문이 '건설적이지 않다'로 표시되는지 이해가되지 않습니다. 처음 닫은 것에 대한 답변을 발표 한 지 거의 2 년이 지났다는 점을 고려하면 실제로 매우 건설적이고 주제가 많았다 고 주장합니다.
아쿠아

2
@ 아쿠아-스택과 관련된 문제가 더 많습니다. 프로그래머는 하나의 답변이있는 매우 구체적인 질문에 대답해야합니다. 그러나 이와 같은 귀중한 질문은 일반적으로 스택 사이트의 정의를 기반으로하는 "구성 적"(IMNSHO에서 용어의 정의가 매우 불량한) 것으로 간주되지 않습니다. 솔직히 프로그래머가 진정으로 유용하기 위해서는이 특정 규칙을 덜 열성적으로 준수해야하지만, 나는 멍청한 사람을 고치려고 노력하는 데 필요한 노력을 기울이기 위해 나이가 많고 피곤하고 다른 방식으로 참여해야합니다.
ocodo

1
실제로 실제 문제는 스택 사이트가 솔직히 알려진 고품질의 리소스 중 하나이며 잘 알려지고 협력 적이며 읽기 쉬운 형식으로되어 있다는 것입니다. 스택에서의 이러한 축소는 실제로 교육적 "질문"(아마도 E 단어를 사용하지 않음)을위한 사이트 형식의 필요성을 가리키는 것으로
보입니다

3
더 많은 예가 좋을 것이므로 사람들 에게이 질문을 다시 열도록 간청합니다. 슬픈 사실은, 새로운 답변이 추가되지 않았다면, 그 질문은 계속 열려있을 것입니다.
ocodo December

답변:


28

안전 (이벤트 발생)

  • States : 다중 "잠금"상태, 하나의 "잠금 해제"상태
  • 전환 : 올바른 조합 / 키를 사용하면 최종 잠금 해제 상태가 될 때까지 초기 잠금 상태에서 잠금 해제 상태에 가까운 잠금 상태로 이동합니다. 잘못된 조합 / 키는 초기 잠금 상태로 돌아갑니다 (때로는 유휴 상태라고도 함) .

신호등 (시간 트리거 | 센서 [이벤트] 트리거)

  • 상태 : RED, YELLOW, GREEN (가장 간단한 예)
  • 전환 : 타이머가 RED에서 GREEN으로, GREEN에서 YELLOW로, YELLOW에서 RED로 변경된 후. 다양한 (더 복잡한) 상태의 자동차를 감지 할 때 트리거 될 수도 있습니다.

자동 판매기 (이벤트 발생, 금고 의 변형 )

  • : 유휴, 5_CENTS, 10_CENTS, 15_CENTS, 20_CENTS, 25_CENTS 등 VEND, CHANGE
  • 전환 : 코인, 청구서 삽입, 올바른 구매 금액 (또는 그 이상)에 따라 VEND로 전환 한 후 상태가 변경된 후 CHANGE 또는 IDLE로 전환 (자판기가 얼마나 윤리적인지에 따라 다름)

가능하면 +1 이상 마지막 것을 제외한 모든 것이 좋아 보인다. 유휴, VEND 및 변경이어야합니다. 값은 조건부이며 IDLE과 자체 간의 전환으로 표시되어야합니다. 항목이 선택되었음을 나타내는 상태를 원할 수도 있습니다.
Evan Plaice

@EvanPlaice : 항목을 선택하는 것이 단순히 유휴에서 VEND로 변경을 유발하는 이벤트가 아닙니까? 판매 전에 선택을 확인하는 방법을 구상하지 않는 한.
Misko

이 두 가지에 대한 다이어그램이 있습니까?
ocodo December

이들은 주어진 실시 예와 동일하다 FSM 위키 페이지 에도 엘리베이터를 포함한다 : "단순 예는 자판기 동전의 적절한 조합이 증착 분배 제품, 엘리베이터 , 정지의 시퀀스가 요청 층으로 판단 운전자 가 대기 중일 때 순서가 바뀌는 교통 신호등 , 조합 번호를 올바른 순서로 입력해야하는 콤비네이션 잠금 장치 "
icc97

1
@ icc97 FSM 예제는 일상 생활에서 풍부하고 일반적입니다. 덧붙여서, 스택 교환 게시물은 Wikipedia 페이지에 예제 정보를 포함시키기 이전입니다 :)
aqua

14

경계 게이트웨이 프로토콜 예

BGP는 인터넷의 핵심 라우팅 결정을 뒷받침하는 프로토콜입니다. 주어진 노드에서 호스트의 도달 가능성을 결정하기 위해 테이블을 유지 관리하고 인터넷을 실제로 분산시킵니다.

네트워크에서 각 BGP 노드는 피어이며 유휴 상태 , 연결 상태 , 활성 상태 중 하나 인 유한 상태 머신을 사용합니다. , OpenSent 상태 , OpenConfirm 상태설정 . 네트워크의 각 피어 연결은 이러한 상태 중 하나를 유지합니다.

BGP 프로토콜은 상태를 변경하기 위해 피어로 전송되는 메시지를 결정합니다.

BPG 상태 차트.

BGP 상태 차트

게으른

첫 번째 상태 유휴 상태 입니다. 이 상태에서 BGP는 리소스를 초기화하고 인바운드 연결 시도를 거부하고 피어에 대한 연결을 시작합니다.

잇다

두 번째 상태는 Connect 입니다. 이 상태에서 라우터는 연결이 완료 될 때까지 대기하고 성공 하면 OpenSent 상태로 전환합니다 . 실패하면 ConnectRetry 타이머를 재설정하고 만료시 활성 상태로 .

유효한

에서 활성 상태, 라우터는에 ConnectRetry의 제로 타이머 반환 재설정 연결을 상태를.

보낸 편지함

에서 OpenSent의 상태, 라우터는 개방 메시지 창 하나 기다린다 보낸다. Keepalive 메시지가 교환되고 성공적으로 수신되면 라우터는 설정 됨 상태가됩니다.

설립

에서 설립 상태, 라우터는 송 / 수신 할 수 있습니다 : 연결 유지를; 최신 정보; 및 피어와의 알림 메시지.

BGP에 대한 자세한 정보는 Wikipedia에 있습니다.


@tcrosley-위키 백과에서 왔으므로 크레딧을받을 가치가 없습니다.
ocodo

1
좋아, 다이어그램 포함에 +1 . :)
tcrosley

:) 충분하지 문자 - 나는 BGP에 차트의 레이블을 수정했는데, 그것은 나를 못하게 것
마이크 Dunlavey에게

이것을 그리는 더 좋은 방법이 있어야합니다.
직업

1
@Job-약간의 늦게 반응하지만, 죄송하지만, 지금은 이것이 너무 난해한 예라고 생각합니다. Safe, Vending Machine 등은 내가 생각하는 방식보다 훨씬 유용합니다.
ocodo

7

모든 종류의 것을 모델링하는 데 유용합니다. 예를 들어, 선거주기는 (정상 정부)-선거 부름-> (초기 선거 운동)-의회 해산-> (무거운 선거 운동)-선거-> (투표 계수)의 선을 따라 상태로 모델링 할 수 있습니다. ). 그런 다음 (투표 수)-다수결 없음-> (연합 협상)-계약에 도달-> (일반 정부) 또는 (투표 수)-다수-> (일반 정부). 정치적 하위 게임이있는 게임에서이 체계에 대한 변형을 구현했습니다.

AI는 게임의 다른 측면에서도 사용됩니다. AI는 종종 상태 기반입니다. 메뉴와 레벨 사이의 전환 및 사망 또는 레벨 완료시의 전환은 종종 FSM에 의해 잘 모델링됩니다.


++ 좋은 예입니다.
마이크 Dunlavey

1
+1 경로 때문에 DFM (결정적 유한 상태 머신)의 좋은 예.
Evan Plaice

4

jquery-csv 플러그인 에서 사용되는 CSV 파서

기본적인 Chomsky Type III 문법 파서입니다.

정규식 토크 나이 저는 문자별로 데이터를 평가하는 데 사용됩니다. 제어 문자가 발생하면 시작 상태를 기반으로 추가 평가를 위해 코드가 switch 문으로 전달됩니다. 제어되지 않는 문자는 그룹화되어 대량으로 복사되어 필요한 문자열 복사 작업 수를 줄입니다.

토크 나이저 :

var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;

첫 번째 일치 항목은 제어 문자입니다. 값 구분 기호 ( ") 값 구분 기호 (,) 및 항목 구분 기호 (모든 줄 바꿈) 마지막 제어는 비 제어 문자 그룹화를 처리합니다.

파서가 충족해야하는 10 가지 규칙이 있습니다.

  • 규칙 # 1-한 줄에 한 항목 씩, 각 줄은 개행으로 끝납니다.
  • 규칙 # 2-파일 끝의 후행 줄 바꿈 생략
  • 규칙 # 3-첫 번째 행에 헤더 데이터가 포함됨
  • 규칙 # 4-공백은 데이터로 간주되며 항목에는 후행 쉼표가 포함되지 않아야합니다.
  • 규칙 # 5-줄은 큰 따옴표로 구분되거나 구분되지 않을 수 있습니다
  • 규칙 # 6-줄 바꿈, 큰 따옴표 및 쉼표가 포함 된 필드는 큰 따옴표로 묶어야합니다.
  • 규칙 # 7-큰 따옴표를 사용하여 필드를 묶는 경우 다른 큰 따옴표를 앞에 두어 필드 안에 나타나는 큰 따옴표를 이스케이프해야합니다.
  • 수정안 # 1-따옴표가없는 필드는
  • 수정안 # 2-따옴표로 묶인 필드는 그렇지 않을 수도 있습니다.
  • 수정안 # 3-항목의 마지막 필드는 null 값을 포함하거나 포함하지 않을 수 있습니다

참고 : 상위 7 개 규칙은 IETF RFC 4180 에서 직접 파생됩니다 . 마지막 3 개는 기본적으로 모든 값을 구분하지 않는 최신 스프레드 시트 앱 (예 : Excel, Google Spreadsheet)에서 도입 한 엣지 사례를 다루기 위해 추가되었습니다. RFC 변경 사항을 다시 제공하려고했지만 아직 문의에 대한 답변을 듣지 못했습니다.

와인딩과 함께 충분한 다이어그램이 있습니다.

CSV 파서 유한 상태 머신

상태 :

  1. 항목 및 / 또는 값의 초기 상태
  2. 시세가 발생했습니다
  3. 두 번째 인용문이 발견되었습니다
  4. 인용되지 않은 값이 발생했습니다

전환 :

  • 에이. 따옴표로 묶은 값 (1), 따옴표없는 값 (3), null 값 (0), null 항목 (0) 및 새 항목 (0)을 모두 확인합니다.
  • 비. 두 번째 인용 문자 확인 (2)
  • 기음. 이스케이프 된 따옴표 (1), 값 끝 (0) 및 항목 끝 (0)을 확인합니다.
  • 디. 값 끝 (0) 및 항목 끝 (0)을 확인합니다.

참고 : 실제로 상태가 없습니다. 이스케이프 된 두 번째 구분 기호는 첫 번째 구분 기호가 여전히 열려 있음을 의미하므로 'c'-> 'b'에서 상태가 '1'로 표시된 줄이 있어야합니다. 실제로 다른 전환으로 표시하는 것이 좋습니다. 이것들을 만드는 것은 예술이며, 올바른 방법은 없습니다.

참고 : 종료 상태도 없지만 유효한 데이터에서 파서는 항상 전환 'a'에서 종료되며 구문 분석 할 항목이 없으므로 상태가 없습니다.

상태와 전환의 차이점 :

상태는 유한하기 때문에 한 가지 의미 만 유추 할 수 있습니다.

전이는 상태 간의 흐름을 나타내므로 많은 것을 의미 할 수 있습니다.

기본적으로, 상태-> 전이 관계는 1-> * (즉, 일대 다)입니다. 상태는 '무엇인가'를 정의하고 전환은 '어떻게 처리되는지'를 정의합니다.

참고 : 상태 / 전환 적용이 직관적이지 않다고 걱정하지 마십시오. 직관적이지 않습니다. 나는 마침내 개념을 고수하기 전에 나보다 훨씬 똑똑한 사람과 광범위하게 대응했습니다.

의사 코드 :

csv = // csv input string

// init all state & data
state = 0
value = ""
entry = []
output = []

endOfValue() {
  entry.push(value)
  value = ""
}

endOfEntry() {
  endOfValue()
  output.push(entry)
  entry = []
}

tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm

// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
  switch(state) {
    case 0:
      if(opening delimiter)
        state = 1
        break
      if(new-line)
        endOfEntry()
        state = 0
        break
      if(un-delimited data)
        value += match
        state = 3
        break
    case 1:
      if(second delimiter encountered)
        state = 2
        break
      if(non-control char data)
        value += match
        state = 1
        break
    case 2:
      if(escaped delimiter)
        state = 1
        break
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
    case 3:
      if(separator)
        endOfValue()
        state = 0
        break
      if(newline)
        endOfEntry()
        state = 0
        break
  }
}

참고 : 이것은 요점입니다. 실제로 고려해야 할 것이 더 많습니다. 예를 들어, 오류 검사, 널 (NULL) 값, 후행 공백 행 (예 : 유효한) 등

이 경우 상태는 정규식 일치 블록이 반복을 완료 할 때의 상태입니다. 전이는 사례 진술로 표현됩니다.

인간으로, 우리는 높은 수준의 초록하지만 FSM 작업이로 낮은 수준의 작업을 단순화하는 경향이 있다 낮은 수준의 연산 작업을. 상태와 전환은 개별적으로 작업하기가 매우 쉽지만 전체를 한 번에 시각화하는 것은 본질적으로 어렵습니다. 전환이 어떻게 진행되는지 직감 할 때까지 반복되는 개별 실행 경로를 따르는 것이 가장 쉽다는 것을 알았습니다. 기본 수학을 배우는 것과 마찬가지로 저수준 세부 사항이 자동으로 시작될 때까지 높은 수준에서 코드를 평가할 수 없습니다.

따로 : 실제 구현을 보면 많은 세부 정보가 빠져 있습니다. 첫째, 모든 불가능한 경로는 특정 예외를 발생시킵니다. 타격을 가하는 것은 불가능하지만 어떤 것이 고장 나면 테스트 러너에서 예외가 발생합니다. 둘째, '법적'CSV 데이터 문자열에서 허용되는 것에 대한 파서 규칙이 매우 느슨하여 코드가 많은 특수한 경우를 처리하는 데 필요했습니다. 사실과 상관없이, 이것은 모든 버그 수정, 확장 및 미세 조정 전에 FSM을 조롱하는 데 사용 된 프로세스였습니다.

대부분의 디자인과 마찬가지로 구현의 정확한 표현은 아니지만 중요한 부분을 간략하게 설명합니다. 실제로이 디자인에서 파생 된 csv 특정 라인 스플리터, 단일 라인 파서 및 완전한 다중 라인 파서의 세 가지 파서 함수가 실제로 있습니다. 그들은 모두 비슷한 방식으로 작동하며 개행 문자를 처리하는 방식이 다릅니다.


1
우와! 아주 좋은 기여.
ocodo

@Slomojo 감사합니다, 공유하게되어 기쁩니다. 나는 CS를 위해 학교에 가지 않았기 때문에이 물건을 스스로 배워야했다. 온라인과 같은 높은 수준의 CS 주제를 다루는 실제 응용 프로그램을 찾기 란 정말 어렵습니다. 결국 파서 알고리즘을 자세하게 문서화하여 앞으로 나와 같은 사람들에게 유용하게 사용할 계획입니다. 이것은 좋은 시작입니다.
Evan Plaice

나는 또한 스스로 가르쳤지만 30 년 전에 시작했기 때문에 지금까지 CS 커리큘럼을 다뤘습니다. 인터넷은 없었지만, 우리는 모두 동굴에 살았지만 산만 함이 적었고 금속 근처에서 일할 수있는 더 많은 기회가 있었기 때문에 당시에는 매우 낮은 수준의 이론을 배우는 것이 훨씬 쉽다고 생각합니다.
ocodo

3

Java의 간단한 FSM

int i=0;

while (i<5) {
 switch(i) {
   case 0:
     System.out.println("State 0");
     i=1;
     break;
   case 1:
     System.out.println("State 1");
     i=6;
     break;
   default:
     System.out.println("Error - should not get here");
     break;      
  }

} 

당신은 간다. 좋습니다, 훌륭하지는 않지만 아이디어를 보여줍니다.

통신 제품에서 FSM은 종종 복잡한 상황에 대한 간단한 솔루션을 제공하기 때문에 찾을 수 있습니다.


3
또한 어휘 분석에서 컴파일러 구성의 중요한 부분입니다.
jmq

@jmquigley, 답변을 추가 할 수 있습니까?
ocodo

1
나는 당신을 위해 두 개의 링크가있는 별도의 답변을 추가했습니다.
jmq

3

나는 리프트 (엘리베이터)에 대한 생각 / 모델링이 유한 상태 기계의 좋은 예라는 것을 알았습니다. 도입 방법은 거의 필요하지 않지만 구현하기에는 사소한 상황과는 거리가 멀다.

상태는 예를 들어 1 층, 1 층 등에서, 1 층으로 이동하거나 3 층에서 1 층으로 이동하지만 현재 3 층과 2 층 사이에 있습니다.

리프트 케이지 및 바닥에있는 버튼의 효과는 입력을 제공하며,이 효과는 현재 상태와 함께 눌려진 버튼에 따라 달라집니다.

상단과 하단을 제외한 각 층에는 두 개의 버튼이 있습니다. 하나는 리프트 상승을 요청하고 다른 하나는 내려갑니다.


2

예, 여기 예가 있습니다. 정수를 구문 분석한다고 가정하십시오. 정수 자릿수 와 같은 dd*곳으로 갈 것 d입니다.

state0:
    if (!isdigit(*p)) goto error;
    p++;
    goto state1;
state1:
    if (!isdigit(*p)) goto success;
    p++;
    goto state1;

물론 @Gary가 말했듯 goto이 switch 문과 state 변수를 사용하여 이들을 위장 할 수 있습니다. 이 코드로 구성 할 수 있습니다.이 코드는 원래 정규식과 동형입니다.

if (isdigit(*p)){
    p++;
    while(isdigit(*p)){
        p++;
    }
    // success
}
else {
    // error
}

물론 조회 테이블을 사용하여 수행 할 수도 있습니다.

유한 상태 머신은 여러 가지 방법으로 만들 수 있으며, 많은 것을 유한 상태 머신의 인스턴스로 설명 할 수 있습니다. 그것은 사물에 대해 생각하기위한 개념만큼이나 "사물"이 아닙니다.

철도 네트워크 예

FSM의 한 예는 철도 네트워크입니다.

기차가 두 개의 트랙 중 하나를 갈 수있는 유한 한 수의 스위치가 있습니다.

이 스위치들을 연결하는 트랙은 유한합니다.

열차는 언제든지 한 트랙에 있으며 단일 비트의 입력 정보를 기반으로 스위치를 건너 다른 트랙으로 전송할 수 있습니다.


(귀하의 답변을 수정했습니다. 승인하길 바랍니다.)
ocodo

@ 슬로 모조 : 괜찮습니다. 좋아 보인다
Mike Dunlavey

2

루비의 유한 상태 머신 :

module Dec_Acts
 def do_next
    @now = @next
    case @now
    when :invite
      choose_round_partner
      @next = :wait
    when :listen
      @next = :respond
    when :respond
      evaluate_invites
      @next = :update_in
    when :wait
      @next = :update_out
    when :update_in, :update_out
      update_edges
      clear_invites
      @next = :exchange
    when :exchange
      update_colors
      clear_invites
      @next = :choose
    when :choose
      reset_variables
      choose_role
    when :done
      @next = :done
    end
  end
end

이것이 분산 시스템에서 단일 컴퓨팅 노드의 동작으로 링크 기반 통신 체계를 설정합니다. 다소간. 그래픽 형식에서는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


흥미로운 +1. DGMM은 무엇을 의미합니까?
Evan Plaice

@EvanPlaice 그것은 분산 약자 일치 기반 최소 가중치 버텍스 커버 알고리즘 (DGMM)입니다 ... 약어 약어입니다 .G가 어디에서 왔는지 묻지 마십시오.
ocodo

@slomojo "G"는 "Generalized"를위한 것으로,이 알고리즘에서 파생 된 순차적 알고리즘은 generalized maximal matching이라는 기술을 사용합니다.
철학자

@ philosodad 나는 많이 가정했지만 게시 가정을 싫어합니다.
ocodo


0

실제로 State Machine은 종종 다음 용도로 사용됩니다.

  • 설계 목적 (프로그램의 다양한 동작 모델링)
  • 자연어 (문법) 파서
  • 문자열 파싱

한 가지 예는 문자열을 스캔하여 올바른 구문이 있는지 확인하는 상태 머신입니다. 예를 들어 네덜란드 우편 번호는 "1234 AB"형식입니다. 첫 번째 부분은 숫자 만 포함하고 두 번째 부분은 문자 만 포함 할 수 있습니다. NUMBER 상태인지 LETTER 상태인지 여부를 추적하고 잘못된 입력이 발생하면이를 거부하는 상태 머신을 작성할 수 있습니다.

이 억 셉터 상태 머신에는 숫자와 알파의 두 가지 상태가 있습니다. 상태 머신은 숫자 상태에서 시작하여 확인할 문자열의 문자를 읽기 시작합니다. 어떤 상태에서도 유효하지 않은 문자가 발견되면 함수는 False 값으로 리턴하여 입력을 유효하지 않은 것으로 거부합니다.

파이썬 코드 :

import string

STATE_NUMERIC = 1
STATE_ALPHA = 2

CHAR_SPACE = " "

def validate_zipcode(s):
cur_state = STATE_NUMERIC

for char in s:
    if cur_state == STATE_NUMERIC:
        if char == CHAR_SPACE:
            cur_state = STATE_ALPHA
        elif char not in string.digits:
            return False
    elif cur_state == STATE_ALPHA:
        if char not in string.letters:
            return False
return True

zipcodes = [
    "3900 AB",
    "45D6 9A",
]

for zipcode in zipcodes:
    print zipcode, validate_zipcode(zipcode)

출처 : 실제 유한 기계

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.