【问题标题】:Expressing a boolean expression as a finite state machine将布尔表达式表示为有限状态机
【发布时间】:2016-10-08 08:14:06
【问题描述】:

是否可以将布尔表达式表示为有限状态机,以便 FSM 接受与布尔表达式匹配的输入?

是否可以合并多个这样的 FSM,并在接受后知道哪个原始 FSM 匹配?

背景:我正在尝试一次计算很多布尔表达式。我在想,通过正确的表示,也许 DFA 可以非常有效地做到这一点(即比 n = 表达式数的 O(n) 更好)。

【问题讨论】:

  • 视情况而定。当您说“布尔表达式”时,您说的是哪种谓词?您允许的布尔运算和“输入值”/“原子”是什么?
  • 带有 AND/OR/NOT 的布尔表达式。输入是无序布尔变量的列表。
  • 所以你使用命题逻辑。我不知道如何比 O(n) 做得更好,你仍然需要评估每个命题。 (如果 n 是布尔列表的长度,我误解了,当命题是 x1 AND x2 AND ... AND xn 时,我看不出如何比 O(n) 表现更好)你能做的是通过转换为合取范式来改善平均情况,然后重用您已经做过的计算。但是,我看不出这会比简单的布尔算术更快。你能给出“很多”的数量级吗?每个表达式有多少个表达式和输入列表?
  • bar|baz 这样的正则表达式可以很容易地表示为 NFA 并转换为 DFA。 DFA 将对两个交替进行一次评估,这比单独评估两者要快。也许布尔表达式也可以。数量级是 1000 个表达式,每个表达式 20 个术语。
  • 哦,我明白你的意思了。您使用析取范式,拆分合取子表达式,并希望构建一个接受所有这些子表达式的自动机。这是个好主意。你可以完全摆脱决策树。不过,请确保值得构建它。

标签: boolean fsm


【解决方案1】:

我知道这是一个非常古老的问题,但我确实发现自己处于类似情况,并且我认为在某些情况下,FSM 可能会在复杂的布尔逻辑中提供一些帮助。

我在blog post of mine 中概述了一个示例,其中我描述了在遍历(循环)简单字符串的标记时使用状态机。

在这种情况下的优点是,解析过程中的每个标记都没有一个专用的布尔标志,语法树中的其他路径需要防范,我可以将每个标记作为事件提供给 FSM 并让机器陷入不同的状态。我在处理过程中使用 Statechart 操作来构建小操作码,然后最终根据最终状态中止或继续编译生成的操作码。

您必须阅读这篇文章才能了解我上面所说的内容。但它的要点是,在某些情况下,一系列布尔值可以转换为事件名称并传递给 FSM 进行处理。在另一个示例中,我必须根据一组压倒性的布尔逻辑来选择需要呈现的 UI 状态。

由于某些标志优先于其他标志,因此生成的逻辑树如下所示:

图 1 — UML 活动图示例

这导致代码看起来有点像这样:

图 2 — 复杂的布尔逻辑代码
let presenterFlags = {
  showSpecialTypeTab: model.type === 'special',
  showDefaultWarning: (model.type === 'special' && model.isDefault && !settings.enforced),
  showDefaultInBrokenStateWarning: (model.type === 'special' && model.isDefault && settings.enforce),
  disableSaveButton: (model.type === 'special' && model.isDefault && !settings.enforce) || !model.canWrite,
  disableCancelButton: (model.type === 'special' && model.isDefault && !settings.enforce) || !model.canWrite,
  disableDeleteButton: (model.type === 'special' && model.isDefault) || !model.canWrite,
  disableEnforcementToggle: (model.type === 'special' && model.isDefault && !settings.enforced) || !model.canWrite,
  disableAnotherToggle: (model.type === 'special' && model.isDefault) || !model.canWrite,
};

这对我来说太过分了,我的大脑无法承受。所以我倾向于使用 FSM,它产生了如下状态图:

图 3 — UML 状态图示例

使用XState,代码可能看起来像这样:

图 4 — XState 机器示例
let booleanMachine = Machine({
  id: 'ExamplePresentationFlags',
  strict: true,
  initial: 'conditional',
  context: {},
  states: {
    conditional: {
      on: {
        'EVALUATE': [
          { target: 'SpecialExample', cond: 'isSpecialExample' },
          { target: 'GenaricExample' },
        ],
      },
    },
    GenaricExample: {
      initial: 'conditional',
      states: {
        conditional: {
          on: {
            '': [
              { target: 'ReadOnly', cond: 'canNotWrite' },
              { target: 'Default', cond: 'isDefault' },
              { target: 'Writable' },
            ],
          },
        },
        Writable: {},
        Default: {},
        ReadOnly: {
          meta: {
            disableSaveButton: true,
            disableCancelButton: true,
            disableDeleteButton: true,
          },
        },
      },
    },
    SpecialExample: {
      initial: 'conditional',
      meta: { showSpecialTypeTab: true },
      states: {
        conditional: {
          on: {
            '': [
              { target: 'ReadOnly', cond: 'canNotWrite' },
              { target: 'Default', cond: 'isDefault' },
              { target: 'Writable' },
            ],
          },
        },
        Writable: {},
        ReadOnly: {
          meta: {
            disableSaveButton: true,
            disableCancelButton: true,
            disableDeleteButton: true,
            disableAnotherToggle: true,
          },
        },
        Default: {
          initial: 'conditional',
          states: {
            conditional: {
              on: {
                '': [
                  { target: 'Enforced', cond: 'isEnforced' },
                  { target: 'Unenforced' },
                ],
              },
            },
            Unenforced: {
              meta: {
                exampleWarning: 'default-read-only',
                disableSaveButton: true,
                disableCancelButton: true,
                disableDeleteButton: true,
                disableAnotherToggle: true,
              },
            },
            Enforced: {
              meta: {
                exampleWarning: 'special-default-broken-enforce-state',
                disableSaveButton: false,
                disableCancelButton: false,
                disableDeleteButton: true,
                disableAnotherToggle: true,
              },
            },
          },
        },
      },
    },
  },
}, {
  guards: {
    isSpecialExample: (ctx) => ctx.exampleType === 'special',
    canNotWrite: (ctx) => !ctx.canWrite,
    isEnforced: (ctx) => ctx.isEnforced,
    isDefault: (ctx) => ctx.isDefault,
    isNotDefault: (ctx) => !ctx.isDefault,
  },
});

使用类似于 reducer 的功能:

图 5 — XState reducer 函数示例
function presentorFlags({ canWrite, model, settings }) {
  let machine = booleanMachine.withContext({
    canWrite,
    exampleType: model.type,
    isEnforced: settings.enforced,
    isDefault: model.isDefault,
  });
  let { meta } = machine.transition(machine.initialState, 'EVALUATE');
  return Object.keys(meta)
    .reduce((acc, key) => ({ ...acc, ...meta[key] }), {});
}

理解我同意这个例子是非常规的,而且更大。它确实使我能够理解逻辑,尤其是使用手头的可视化工具(即图 3)。当时,它让我能够概念化所有边缘案例状态,而不必担心 UI 的每个状态就视图代码而言意味着什么。相反,我可以专注于状态本身以及使机器进入该状态的逻辑。然后我给减速器实际的布尔值,让机器完成工作。我得到的只是一组易于放置在我的模板中的 UI 标志。

再一次,也许这不是更好,也可能是这样。关键是可以使用状态机来表达布尔逻辑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-19
    • 1970-01-01
    • 2012-03-09
    • 2017-04-23
    • 2017-04-26
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    相关资源
    最近更新 更多