EcmaScript용 smplie 유한 상태 머신
Mach: ECMAScript의 유연한 FSM
안녕하세요,
방금 JS에서 간단한 Finite State Machine을 개발했습니다. 초기 목표는 SvelteKit 구성 요소 내에 통합하여 UI 상태 관리를 제공하는 것이었습니다.
디자인 및 사용법
이것은 단일 ECMAScript 파일입니다. 간단하게 코드로 가져오고 FSM() 생성자를 해당 매개변수와 함께 호출합니다.
import { FSM } from './FSM.js'
const fsm = FSM ({
debug: true,
jumps: ({ state }) => console.log ({ state }),
success: ({ state }) => console.log ('success'),
failure: ({ state }) => console.log ('failure')
})
그런 다음 상태 및 전환 화살표를 간단히 정의할 수 있습니다.
fsm.state ({ name: 'INIT', init: true })
fsm.state ({ name: 'TERM', term: true })
fsm.state ({ name: 'OFF' })
fsm.state ({ name: 'ON' })
fsm.arrow ({ from: 'INIT', accept: 'activate', to: 'OFF'})
fsm.arrow ({ from: 'OFF', accept: 'switch', to: 'ON'})
fsm.arrow ({ from: 'ON', accept: 'switch', to: 'OFF'})
fsm.arrow ({ from: 'ON', accept: "kick", to: 'TERM'})
물론 이러한 방식으로 데이터베이스와 같은 외부 데이터 저장소에서 상태 및 전환 화살표를 자유롭게 수화할 수 있습니다. 또는 논리 테스트에 따라 FSM 설계를 모듈화하십시오!
결국, 그것을 실행하기 위해 기계를 포장하고 몇 가지 항목을 읽으십시오.
fsm.pack ({ state: 'INIT '})
fsm.accept ({ word: 'activate'})
fsm.accept ({ word: 'on'})
fsm.accept ({ word: 'kick'})
// console.log ('success')
구현 세부정보
모든 항목(STATES 및 reanition 화살표)은 오리 블록과 같은 동일한 배열 구조에 저장됩니다. 항목 목록은 머신 내부의 모든 이름에 대한 쌍(이름) => [인덱스 배열]을 포함하는 조회 맵에 의해 두 배가 됩니다(STATES의 경우 대문자, 화살표의 경우 소문자). 이렇게 하면 상태/단어 블록을 찾는 데 사용되는 시간이 단축됩니다.
추가 단계
비동기 변환 작업에 대한 지원을 추가합니다.
/// Here's the complete code for the Mach FSM ...
export const FSM = ({ jumping, success, failure, debug } = {}) => {
// holds STATE and ARROW items
const entries = [{ uid: 0, def: 'ROOT' }]
// aliases lookups table
const lookups = new Map ()
const runtime = {
GUID: 0,
state: null,
trace: [],
data: null
}
// state definition
const state = ({ name, init, term, payload }) => {
const def = 'STATE'
const alias = ('' + name).toUpperCase ()
const uid = ++runtime.GUID
const uids = lookups.get(alias) || []
const len = uids.length
const item = {
uid, def, alias, init, term, payload
}
if (len === 0) {
entries.push (item)
lookups.set (alias, [uid])
} else {
throw new Error ('duplicate entry ' + uis + ' ' + alias)
}
if (debug) {
if (len === 0) {
console.log ('creating new state', { alias, uid })
} else {
console.log ('replacing item', { alias, uid })
}
}
}
// arrow definition
const arrow = ({ from, accept, to, ops }) => {
const def = 'ARROW'
const alias = ('' + accept).toLowerCase ()
const uid = ++runtime.GUID
const uids = lookups.get(alias) || []
const len = uids.length
const item = {
uid, def, alias, from, accept, to, ops
}
entries.push (item)
uids.push (uid)
lookups.set (alias, uids)
// console.log ('craating arrow', { item })
}
// ready to run !!!
const pack = ({ state, data }) => {
const alias = ('' + state).toUpperCase ()
runtime.state = alias
runtime.data = data
if (debug) {
console.log('Finite State Machine packing', alias)
}
}
// read entry word
const read = ({ word, data }) => {
const alias = ('' + word).toLowerCase ()
const uids = lookups.get (alias) || []
let accepted = false
console.log ('read', { [alias]: uids.join(', ') })
uids.map ((uid) => entries [uid])
.map ((item) => {
console.log ('MAP', { item })
return item
})
.filter ((item) => accepted === false)
.filter ((item) => item.def === 'ARROW')
.filter ((item) => item.from === runtime.state)
.filter ((item) => item.accept === alias)
.map ((item) => {
const suids = lookups.get (item.to) || []
const final = entries [suids[0]]
runtime.state = item.to
runtime.trace.push (alias)
accepted = true
jumping && jumping.call && jumping.call (null, {
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
item.ops && item.ios.call && item.ops.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
if (final.term) {
success && success.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
}
return true
})
if (accepted === false) {
failure && failure.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
}
}
// return debug table as string
const table = () => {
const texts = []
texts.push ('~~~ Finistamach: lightweight Finite State Machine ~~~')
texts.push ('')
texts.push ('uid\tdef\talias\t\tmore info...')
texts.push ('--------'.repeat(5))
entries.map ((item) => {
if (item.def === 'STATE') {
texts.push ([
item.uid, 'STATE', item.alias + '\t',
(item.init ? '*init*' : '') + (item.term ? "*term*" : "")
].join('\t'))
}
if (item.def === 'ARROW') {
texts.push ([
item.uid, 'ARROW', item.alias + '\t',
item.from + ' --{' + item.accept + '}-> ' + item.to
].join('\t')) }
})
return texts.join('\n')
}
return {
state, arrow, pack, read, table
}
}
Reference
이 문제에 관하여(EcmaScript용 smplie 유한 상태 머신), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/hefeust/smplie-finite-state-machine-for-ecmascript-2a5d
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
import { FSM } from './FSM.js'
const fsm = FSM ({
debug: true,
jumps: ({ state }) => console.log ({ state }),
success: ({ state }) => console.log ('success'),
failure: ({ state }) => console.log ('failure')
})
fsm.state ({ name: 'INIT', init: true })
fsm.state ({ name: 'TERM', term: true })
fsm.state ({ name: 'OFF' })
fsm.state ({ name: 'ON' })
fsm.arrow ({ from: 'INIT', accept: 'activate', to: 'OFF'})
fsm.arrow ({ from: 'OFF', accept: 'switch', to: 'ON'})
fsm.arrow ({ from: 'ON', accept: 'switch', to: 'OFF'})
fsm.arrow ({ from: 'ON', accept: "kick", to: 'TERM'})
fsm.pack ({ state: 'INIT '})
fsm.accept ({ word: 'activate'})
fsm.accept ({ word: 'on'})
fsm.accept ({ word: 'kick'})
// console.log ('success')
/// Here's the complete code for the Mach FSM ...
export const FSM = ({ jumping, success, failure, debug } = {}) => {
// holds STATE and ARROW items
const entries = [{ uid: 0, def: 'ROOT' }]
// aliases lookups table
const lookups = new Map ()
const runtime = {
GUID: 0,
state: null,
trace: [],
data: null
}
// state definition
const state = ({ name, init, term, payload }) => {
const def = 'STATE'
const alias = ('' + name).toUpperCase ()
const uid = ++runtime.GUID
const uids = lookups.get(alias) || []
const len = uids.length
const item = {
uid, def, alias, init, term, payload
}
if (len === 0) {
entries.push (item)
lookups.set (alias, [uid])
} else {
throw new Error ('duplicate entry ' + uis + ' ' + alias)
}
if (debug) {
if (len === 0) {
console.log ('creating new state', { alias, uid })
} else {
console.log ('replacing item', { alias, uid })
}
}
}
// arrow definition
const arrow = ({ from, accept, to, ops }) => {
const def = 'ARROW'
const alias = ('' + accept).toLowerCase ()
const uid = ++runtime.GUID
const uids = lookups.get(alias) || []
const len = uids.length
const item = {
uid, def, alias, from, accept, to, ops
}
entries.push (item)
uids.push (uid)
lookups.set (alias, uids)
// console.log ('craating arrow', { item })
}
// ready to run !!!
const pack = ({ state, data }) => {
const alias = ('' + state).toUpperCase ()
runtime.state = alias
runtime.data = data
if (debug) {
console.log('Finite State Machine packing', alias)
}
}
// read entry word
const read = ({ word, data }) => {
const alias = ('' + word).toLowerCase ()
const uids = lookups.get (alias) || []
let accepted = false
console.log ('read', { [alias]: uids.join(', ') })
uids.map ((uid) => entries [uid])
.map ((item) => {
console.log ('MAP', { item })
return item
})
.filter ((item) => accepted === false)
.filter ((item) => item.def === 'ARROW')
.filter ((item) => item.from === runtime.state)
.filter ((item) => item.accept === alias)
.map ((item) => {
const suids = lookups.get (item.to) || []
const final = entries [suids[0]]
runtime.state = item.to
runtime.trace.push (alias)
accepted = true
jumping && jumping.call && jumping.call (null, {
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
item.ops && item.ios.call && item.ops.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
if (final.term) {
success && success.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
}
return true
})
if (accepted === false) {
failure && failure.call (null, {
data,
trace: runtime.trace,
result: runtime.data,
state: runtime.state,
word: alias
})
}
}
// return debug table as string
const table = () => {
const texts = []
texts.push ('~~~ Finistamach: lightweight Finite State Machine ~~~')
texts.push ('')
texts.push ('uid\tdef\talias\t\tmore info...')
texts.push ('--------'.repeat(5))
entries.map ((item) => {
if (item.def === 'STATE') {
texts.push ([
item.uid, 'STATE', item.alias + '\t',
(item.init ? '*init*' : '') + (item.term ? "*term*" : "")
].join('\t'))
}
if (item.def === 'ARROW') {
texts.push ([
item.uid, 'ARROW', item.alias + '\t',
item.from + ' --{' + item.accept + '}-> ' + item.to
].join('\t')) }
})
return texts.join('\n')
}
return {
state, arrow, pack, read, table
}
}
Reference
이 문제에 관하여(EcmaScript용 smplie 유한 상태 머신), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hefeust/smplie-finite-state-machine-for-ecmascript-2a5d텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)