Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/.htaccess/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在JavaScript中构造树模式匹配算法?_Javascript_Algorithm_Parsing_Tree_Recursive Descent - Fatal编程技术网

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

如何在JavaScript中构造树模式匹配算法?,javascript,algorithm,parsing,tree,recursive-descent,Javascript,Algorithm,Parsing,Tree,Recursive Descent,好吧,这是一个有点复杂的问题,但是tl;dr基本上就是如何使用“模式树”解析“实际树”?如何检查特定的树实例是否与特定的模式树匹配 首先,我们有模式树的结构。模式树通常可以包含以下类型的节点: sequence节点:匹配项目序列(零或更多) 可选节点:匹配一项或零项 class节点:委托到另一个模式树进行匹配 first节点:匹配从集合中找到的第一个子模式 交错节点:以任意顺序匹配任何子模式 text节点:匹配直接文本 对于这个问题,这应该足够好了。还有一些节点类型,但这些是主要的。本质上,

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

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

  • sequence
    节点:匹配项目序列(零或更多)
  • 可选
    节点:匹配一项或零项
  • class
    节点:委托到另一个模式树进行匹配
  • first
    节点:匹配从集合中找到的第一个子模式
  • 交错
    节点:以任意顺序匹配任何子模式
  • text
    节点:匹配直接文本
对于这个问题,这应该足够好了。还有一些节点类型,但这些是主要的。本质上,它类似于正则表达式或语法树

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

<sequence>
  <text value="foo">
    <text value="bar" />
  </text>
</sequence>
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>
序列模式树如下所示:

{
  "type": "sequence",
  "children": [
    {
      "type": "text",
      "value": "foo",
      "children": [
        {
          "type": "text",
          "value": "bar"
        }
      ]
    }
  ]
}
<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>
对于模式树,更复杂的示例如下:

<sequence>
  <text value="foo">
    <text value="bar" />
  </text>
</sequence>
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>
我真的不知道如何开始这个,我正在谷歌搜索“”或“”。如果我的方向正确的话,我会花很多时间把这些数学抽象翻译成代码。想知道您是否可以为这种情况构造一个简单的算法。很难弄清楚您应该如何遍历实际的树,同时还要遍历模式树,并跟踪您在每个树中的位置

花相当多的时间在这上面并不能让我走得很远:

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, textNode, state)
  }
}

function parseSequencePattern(patternNode, textNode, 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]
  })
}
函数解析(模式树、文本树){
让状态={ti:0,pi:0}
while(state.ti
最简单的方法就是将“模式树”转换为regexp,然后用该regexp检查“实际树”的文本表示形式

关于递归下降。递归下降本身足以执行语法检查,但不是非常有效,因为有时需要从一开始就多次检查模式。要制作单遍语法检查器,还需要状态机,这就是Regexp的功能

因此,无需重新发明轮子,只需将“模式”指定为regexp(或者将模式的表示形式转换为regexp)

你的


将转换为

/<foo><bar\/><\/foo>/gm
//gm
这完全符合这个

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


我假设转换模式->regexp的算法很简单

当尝试将模式
p
匹配到树
t
时,必须首先将
p
的根及其子项匹配到
t
的根和
t
的子项。如果
p
在根上不匹配,则必须遍历
t
的子级,并且每个子级值都与
p
匹配

根据发布的输入和节点类型,递归模式匹配函数必须处理三种主要输入场景:

  • p(对象)
    t(对象)
    。在这里,模式是一个具有类型键和子键的对象。输入树也是一个对象,至少有
    标记
    子项
    键。模式匹配函数委托给一个助手函数,该函数为
    p
    对象执行匹配。键入
  • p(对象)
    t(数组)
    。在这种情况下,树是
    对象
    数组
    ,并且基于模式的对象,代码必须决定如何处理树的
    数组
    序列
    首先
    ,等等)
  • p(数组)
    t(数组)
    。这里,
    p
    t
    都是序列的形式,匹配操作是为
    p
    Array
    中的每个
    a
    查找
    t
    Array
    中相应的
    a1
    ,从而
    模式匹配(a,a1)
    产生
    true
  • 首先,下面的代码实现了一个
    find_match
    函数,该函数支持基本节点
    text
    序列

    //object that stores node match handlers
    node_types = {'sequence':sequence_match, 'text':text_match}
    //main find_match function
    function find_match(pattern, tree_root, tree_children=null){
        var tree = tree_children === null? tree_root.children : tree_children;
        if (!Array.isArray(pattern)){ 
            //pattern is an object - delegate to handler for node type
            if (node_types[pattern.type](pattern, tree_root, tree)){
                return true
            }
            //no match at the root - check the children
            return tree.some(x => find_match(pattern, x, null));
        }
        //pattern is an array - delegate to sequence
        return sequence_match({'children':pattern}, tree_root, tree)
    }
    //text match handler
    function text_match(pattern, tree_root, tree_children){
        if (!('value' in pattern) && !('name' in pattern)){
            //empty text node found
            return true
        }
        if (tree_root.tag === pattern['value' in pattern ? 'value' : 'name']){
            //match found at the root - continue match with pattern and tree children
            return find_match(pattern.children, null, tree_root.children)
        } 
        //no match - check the tree's children
        return (tree_children === null ? tree_root.children : tree_children).some(x => find_match(pattern, x))
    
    }
    //sequence match handler
    function sequence_match(pattern, tree_root, tree_children){
        var tree = tree_children === null ? tree_root.children : tree_children;
        //copy the pattern and tree objects, as "p" is mutated as part of the match process
        var p = JSON.parse(JSON.stringify(pattern))
        var t = JSON.parse(JSON.stringify(tree))
        while (p.children.length){
            if (!t.length){
                return false
            }
            var f = false;
            var f_seq = p.children.shift();
            for (var _n_t of t){
                //compare sub-pattern and sub-tree
                if (find_match(f_seq, _n_t, t)){
                    f = true
                    break;
                }
            }
            //no match found for sub-pattern in sequence, return false
            if (!f){
                return false
            }
        }
        //matches found for all sub-patterns in the sequence, return true
        return true
    }
    


    我已经有一段时间没有处理它了,但我相当肯定XMLSchema支持您描述的几乎所有内容。您所需要的只是一个支持JSON的XML-to-JSON transpiler和一个用于检查模式树的XSD验证器,现在就可以开始了。当然还有将模式树定义的任何格式编译为XML模式的代码。无需重新发明轮子我想了解算法是如何实现的,我在这里使用XML作为示例,但我真正的项目使用的是自定义数据语言,而不是XML。哦,很有趣。基本上,这就是对象扫描在内部的工作方式。如果你想要灵感,请看一下代码。特别是
    find.js
    文件很有意思,我在这个问题上花了很多时间,在解析树和树重构中进行模式匹配。我学会了那份令状
    <foo><bar/></foo><foo><bar/></foo><foo><bar/></foo>
    <foo><bar/></foo>
    <foo><bar/></foo><foo><bar/></foo>
    
    //object that stores node match handlers
    node_types = {'sequence':sequence_match, 'text':text_match}
    //main find_match function
    function find_match(pattern, tree_root, tree_children=null){
        var tree = tree_children === null? tree_root.children : tree_children;
        if (!Array.isArray(pattern)){ 
            //pattern is an object - delegate to handler for node type
            if (node_types[pattern.type](pattern, tree_root, tree)){
                return true
            }
            //no match at the root - check the children
            return tree.some(x => find_match(pattern, x, null));
        }
        //pattern is an array - delegate to sequence
        return sequence_match({'children':pattern}, tree_root, tree)
    }
    //text match handler
    function text_match(pattern, tree_root, tree_children){
        if (!('value' in pattern) && !('name' in pattern)){
            //empty text node found
            return true
        }
        if (tree_root.tag === pattern['value' in pattern ? 'value' : 'name']){
            //match found at the root - continue match with pattern and tree children
            return find_match(pattern.children, null, tree_root.children)
        } 
        //no match - check the tree's children
        return (tree_children === null ? tree_root.children : tree_children).some(x => find_match(pattern, x))
    
    }
    //sequence match handler
    function sequence_match(pattern, tree_root, tree_children){
        var tree = tree_children === null ? tree_root.children : tree_children;
        //copy the pattern and tree objects, as "p" is mutated as part of the match process
        var p = JSON.parse(JSON.stringify(pattern))
        var t = JSON.parse(JSON.stringify(tree))
        while (p.children.length){
            if (!t.length){
                return false
            }
            var f = false;
            var f_seq = p.children.shift();
            for (var _n_t of t){
                //compare sub-pattern and sub-tree
                if (find_match(f_seq, _n_t, t)){
                    f = true
                    break;
                }
            }
            //no match found for sub-pattern in sequence, return false
            if (!f){
                return false
            }
        }
        //matches found for all sub-patterns in the sequence, return true
        return true
    }
    
    tree = [{'tag': 'foo', 'children': [{'tag': 'bar', 'children': []}]}, {'tag': 'foo', 'children': [{'tag': 'bar', 'children': []}]}, {'tag': 'foo', 'children': [{'tag': 'bar', 'children': []}]}]
    pattern = {'type': 'text', 'name': 'thing', 'children': [{'type': 'text', 'children': []}, {'type': 'sequence', 'children': [{'type': 'first', 'children': [{'type': 'class', 'name': 'value', 'children': []}, {'type': 'class', 'name': 'function', 'children': []}]}]}]}
    console.log(find_match(pattern, {'tag':null, 'children':tree}))
    //prints "true"