如何在 JavaScript 中构建树模式匹配算法?

问题描述

好的,这是一个有点复杂的问题,但是 tl;dr 基本上是你如何使用“模式树”解析“实际树”?你如何检查特定的树实例是否与特定的模式树匹配?

首先,我们有模式树的结构。模式树通常可以包含以下类型的节点:

  • sequence 节点:匹配项目序列(零个或多个)。
  • optional 节点:匹配一项或零项。
  • class 节点:委托给另一个模式树进行匹配。
  • first 节点:匹配它从集合中找到的第一个子模式。
  • interlace 节点:以任何顺序匹配任何子模式。
  • text 节点:匹配直接文本。

这对于这个问题应该足够了。还有一些节点类型,但这些是主要的。本质上它就像一个正则表达式或语法树。

我们可以从一个简单的模式树开始:

<sequence>
  <text value="foo">
    <text value="bar" />
  </text>
</sequence>

这应该与以下任何文本匹配。

<foo><bar/></foo><foo><bar/></foo><foo><bar/></foo>
<foo><bar/></foo>
<foo><bar/></foo><foo><bar/></foo>

更具体地说,您应该将其想象为 JSON,将模式树也想象为 JSON。

{
  "tag": "foo","children": [
    { "tag": "bar" }
  ]
}

而序列模式树是这样的:

{
  "type": "sequence","children": [
    {
      "type": "text","value": "foo","children": [
        {
          "type": "text","value": "bar"
        }
      ]
    }
  ]
}

对于模式树来说,一个更复杂的例子是这样的:

matchThing = <text name="thing">
  <text />
  <sequence>
    <first>
      <class name="value"/>
      <class name="function"/>
    </first>
  </sequence>
</text>

matchFunction = <text name="function">
  <text />
  <sequence>
    <text name="input">
      <text />
    </text>
  </sequence>
  <sequence>
    <text name="call">
      <text />
    </text>
  </sequence>
</text>

matchValue = <text name="value">
  <text />
</text>

它会匹配这样的文本:

<thing>
  <call-me-anything />
  <function>
    <input><foo/></input>
    <input><bar/></input>
    <call><foo/></call>
    <call><foo/></call>
    <call><bar/></call>
    <call><bar/></call>
  </function>
  <function>
    <call><baz/></call>
  </function>
  <value>
    <hello/>
  </value>
  <function>
    <input><hello/></input>
    <call><world/></input>
  </function>
</thing>

因此也可以将其想象为 JSON。

想知道您如何为此创建算法。我一开始就被困住了,但它似乎需要某种递归下降,但在树上而不是在序列上。

所以你有一个函数:

function checkIfMatch(actualTree,patternTree) {
  for (node in actualTree) {
    if (!checkIfMatchesSubtree(node,patternTree)) {
      return false
    }
  }
  return true
}

我真的不知道如何开始这个,正在 Google 上搜索“tree pattern matching algorithms”或“arbology”。如果我朝着正确的方向前进,我将花费大量时间尝试将这些数学抽象转化为代码。想知道您是否可以帮助为这种情况构建一个简单的算法。很难弄清楚应该如何遍历实际树,同时还要遍历模式树,并跟踪您在每棵树中的位置。

在它上面花费相当多的时间并没有让我走得很远:

function parse(patternTree,textTree) {
  let state = { ti: 0,pi: 0 }
  while (state.ti < textTree.length) {
    let pattern = patternTree[state.pi]
    let result = parsePattern(pattern,textTree[state.ti])
    if (!result) {
      return
    }
  }
}

function parsePattern(patternNode,textNode,state) {
  if (patternNode.type == 'sequence') {
    return parseSequencePattern(patternNode,state)
  }
}

function parseSequencePattern(patternNode,state) {
  while (true) {
    let i = 0
    while (i < patternNode.children.length) {
      let childPattern = patternNode.children[i++]
      let result = parsePattern(childPattern,textNode)
      if (!result) {
        return false
      }

    }
  }
}


while (stack.length) {
  let {
    parents,node,} = stack.shift()

  stack.push({
    parents: [node,...parents]
  })
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)