纯粹炫技

2022-07-28 15:56:35 浏览数 (1)

最近在搞原子状态机,后续会发文章介绍。

代码语言:javascript复制
export const enum FSM_EVENT {
  START = 'start',
  START_SUCCESS = 'startSuccess',
  START_FAILED = 'startFailed',
  STOP = 'stop',
  STOP_SUCCESS = 'stopSuccess',
  STOP_FAILED = 'stopFailed',
};
export const enum FSM_STATE {
  IDLE,
  STARTING,
  RUNNING,
  STOPING,
}
const Transitions = [
  {
    [FSM_EVENT.START]: FSM_STATE.STARTING,
  },
  {
    [FSM_EVENT.START_SUCCESS]: FSM_STATE.RUNNING,
    [FSM_EVENT.START_FAILED]: FSM_STATE.IDLE,
  },
  {
    [FSM_EVENT.STOP]: FSM_STATE.STOPING,
  },
  {
    [FSM_EVENT.STOP_SUCCESS]: FSM_STATE.IDLE,
    [FSM_EVENT.STOP_FAILED]: FSM_STATE.RUNNING
  }
] as const;

这个状态机是写死的四种状态

下面是正常的改变状态的过程

代码语言:javascript复制
private transition(event: FSM_EVENT, ...args: any[]) {
    const to = Transitions[this.state]?.[event];
    if (typeof to == 'number') {
      this.state = to;
      return true;
    }
    return false;
  }
}

到这里没什么问题,但是我盯着最上面的代码看,总觉得里面有重复的东西需要优化。

开始和结束有相同的状态变化过程,而且看着这张图总有种对称性,似乎我们的代码太过肤浅了,没有那个玄而又玄的味道。

直觉上来说四种状态对应着两个二进制位,00、 01、 10 、11,也可以作为数组的下标,0,1,2,3。

这里面一定可以用数学来计算。

现在有六种事件,但是抽出重复的单词,实际上还是四个单词的组合:start、stop、Success、Failed,所以这四个单词也可以用00、01、10、11表示。

代码语言:javascript复制
export enum FSM_EVENT {
  start,
  stop,
  Success,
  Failed
}
export enum FSM_STATE {
  IDLE = 0b11,
  STARTING = 0b00,
  RUNNING = 0b10,
  STOPPING = 0b01
}

整理一下状态机:得到下面

代码语言:javascript复制
const Transitions = [];
Transitions[0b00] = {
  [FSMEvent[0b00]   FSMEvent[0b10]]: 0b10,
  [FSMEvent[0b00]   FSMEvent[0b11]]: 0b11,
};
Transitions[0b01] = {
  [FSMEvent[0b01]   FSMEvent[0b10]]: 0b11,
  [FSMEvent[0b01]   FSMEvent[0b11]]: 0b10,
};
Transitions[0b10] = {
  [FSMEvent[0b01]]: 0b01
};
Transitions[0b11] = {
  [FSMEvent[0b00]]: 0b00
};

现在的问题是,如果通过输入:当前状态和事件,得到输出:下一个状态(或者不变)

我们已经把startSuccess事件拆成了,start Success了,所以相当于有三个输入量,当前状态、X、Y。

反复观察上面的规律,可以发现 下一个状态值 ,就是 X^Y (异或)。

现在剩下如何判断是否跳转到下一个状态了。

00 状态下只接受 X =00,01状态下只接受X = 01 ,那这个好办就是state ^ X == 00

10 状态下只接受 X = 01 ,11 状态下只接受X = 00 ,这个也好办就是state ^ X == 11

再考虑一下其他情况,Y只能是10 和 11,但是下面两个状态变化中没有Y,所以给Y 为0,这样也能保证下一个状态是X^Y。

最后我们整理到一个判断里面即可:

代码语言:javascript复制
 private transition(x: FSM_EVENT.start | FSM_EVENT.stop, y: 0 | FSM_EVENT.Success | FSM_EVENT.Failed, ...args: any[]) {
    const from = this.state.value;
    const event = FSM_EVENT[x]   (y ? FSM_EVENT[y] : "")
    if ((from ^ x) == (y ? 0 : 3)) {
      this.state.value = x ^ y;
      return true;
    }
    return false;
  }
}

至此我们消灭了一坨看似无法消灭的代码。

0 人点赞