使用占位符快速查找树?

时间:2017-08-11 05:03:43

标签: indexing data-structures tree s-expression

对于我正在考虑的应用程序,会有一个大的(100,000+)'数据库'树(想想编程语言中的表达式,或S表达式),我需要查询该数据库中的表达式匹配特定的给定表达式。

在详细说明我想要的内容之前,请注意我感谢任何信息,这些信息与索引大量树木以优化子树查找有关。

在我的特定情况下(对于Metamath证明助手使用的后端),表达式具有以下结构(以类似Haskell的表示法):

<attr name="drawableLeftCompat" format="reference" />
    <attr name="drawableRightCompat" format="reference" />
    <attr name="drawableTopCompat" format="reference" />
    <attr name="drawableBottomCompat" format="reference" />

    <declare-styleable name="CustomTextView">
        <attr name="font" format="string" />
        <attr name="drawableLeftCompat" />
        <attr name="drawableRightCompat" />
        <attr name="drawableTopCompat" />
        <attr name="drawableBottomCompat" />
    </declare-styleable>

或作为S表达形式的BNF:

data Expression = Placeholder Id | VarName Id | ConstName Id [Expression]

其中Expression = '?' Id | Id | '(' Id Expression* ')' 是某种标识符。

例如,我可以拥有一个包含

等表达式的数据库
Id

在此上下文中,两个表达式匹配如果通过替换占位符的表达式可以使它们相等。因此,在上面的迷你数据库中查找(equiv ?ph ?ps) (not (in (appl (sqrt) (2)) (Q))) (equiv (eq ?A ?B) (forall ?x (equiv (in ?x ?A) (in ?x ?B)))) 将导致第一个和最后一个表达式。

再次说明:如何在一大堆带有占位符的(表达式)树中实现快速查找?我可以使用什么样的索引数据结构?

1 个答案:

答案 0 :(得分:0)

我将使用trie实现查找。每个密钥将包含以下内容之一:

  • ConstName标识符
  • 带有上下文信息的变量
  • ConstValue
  • 占位符

这些应该以某种方式排序-可能是占位符,然后是所有ConstNames(字母顺序),然​​后是变量(作用域顺序,然后是参数顺序),然​​后是ConstValues(数字顺序)。只要在Trie中有具体的使用顺序,就可以了。

遍历表达式的树,在遇到它们时将适当的键注入到trie中。对要插入到数据结构中的所有表达式执行此操作。当需要查询它时,您可以以类似的方式遍历trie,但有一些新规则。

  • 所有内容都与一个占位符节点匹配。如果它也与其他键匹配,那么您将需要探索两个分支(通过类似于DFS的递归方法轻松完成)。
  • 占位符匹配所有内容。这不等于前面的要点-我们在这里在查询中谈论占位符,前面的项目符号将占位符作为trie键。

现在,这确实意味着当您遇到占位符时,搜索空间可能会在某种程度上“爆炸”,但是您可以做一件事来在实践中尝试减轻这种情况。以广度优先的方式遍历表达式的树(在构造trie和查询时)。这意味着,如果其中一个参数是占位符,则无需完全深入搜索迄今为止与该表达式 匹配的每个子树-您可以跳至下一个参数-可能不会是一个占位符,因此将大大缩小搜索空间(与匹配“一切”相比)。

出于完整性考虑,让我们举一个例子

(not (in (appl (sqrt) (2)) (Q)))

并从中输入一个特里文字-

not -> in -> apply -> "Q" -> sqrt -> 2

为此添加(not (in ?ph E))会导致-

not -> in -> apply -> "Q" -> sqrt -> 2
         \-> ?ph   -> "E"

以这种方式继续,将表达式注入到trie中。还要以这种方式遍历查询,直到到达搜索结尾为止,然后返回匹配的内容。

注意-这些条目的唯一性是基于您不必支持可变参数功能的假设。如果这样做,请在每个键上附加一些上下文信息(有关如何执行此操作的信息,请阅读下几段),以区分将哪些参数传递给哪个函数

我掩盖了一个过度变量的细节。如果您只希望它们是完全相同的变量名来匹配,则无需任何工作。但这可能不是您想要的。您可能希望它与通用变量匹配,只要它们彼此“一致”即可。执行此操作的方法是为每个变量分配一个标识符,该标识符表示最初定义该变量的范围。

最简单的方法就是根据其祖先的参数顺序串联一个标识符。也就是说,如果首先将变量定义为函数的 second 参数,而该参数是根函数的 fifth 参数,则我们可以将其标记为{{1} }或(5, 2),从直觉上讲更合理。无论哪种方式,这都将确保变量被赋予一致的标识符,而不管其他地方的其他变量/函数如何。然后像往常一样使用这个新变量名。