我有一个形如
L1 -> (A, B, C)
L2 -> (D, E),
L3 -> (F, G, A),
L4 -> (C, A)
......
的规则列表,其中包含大约30K条这样的规则。
我有一个输入 (X, Y, Z)
这会创建一个方法
List <Rule> matchRules(input)
属于RuleMatcher类的内容。
我从一个非常简单明了的天真解决方案开始,为了建立框架,让某些东西运作起来。
public RuleMatcher(Collection<Rule> rules) {
this.rules = rules;
}
public Collection<Rule> matchRules(List<Token> input) {
List<Rule> matchingRules = new ArrayList<>();
for(Rule r: this.rules) {
if(r.matches(input)) {
matchingRules.add(r);
}
}
return matchingRules;
}
matches
函数非常简单,它会检查长度是否相同,然后使用for循环检查每个标记。
这个matchRules
函数被调用了数十亿次。
显然,这是一种非常糟糕的实现。根据我的分析工具,至少有一半的执行时间都花费在了这个matches
函数上。
我考虑了两种可能的解决方案:
A. 一些Trie数据结构来存储可以匹配的规则链。
B. 一些哈希函数。每个符号都被赋予一个唯一的标识符。不幸的是,有大约8千个独特的符号,所以这可能很困难。
C. 基于右侧大小和规则中标记数量的哈希表。不幸的是,大多数规则的大小都差不多,所以这可能不值得。
D. 你们中有人提出的某些神奇的解决方案。
希望有人能够解决这个问题。
编辑:标记只是带有唯一编号的对象。例如,“NN”是一个标记。每个“NN”的实例都完全相同。
matches
代码:
public boolean rhsMatches(List<Token> tokens) {
if(tokens.size()!=rhsSize()) return false;
for(int i = 0;i<rhsSize();i++) {
if(!rightSide.get(i).equals(tokens.get(i)) {
return false;
}
}
return true;
}
它并不十分漂亮,但很简单。
TrieSet
的想法是你最好的第一选择。本质上,你需要构建一个语法。 - OldCurmudgeon