如何将条件链转换为更快,更简单的代码?

时间:2009-05-21 16:38:49

标签: java performance parsing dictionary antlr

我有9种不同的语法。其中一个将被加载,具体取决于它正在解析的文件的第一行txt。

我正在考虑将词法分析器/解析器派生到sep中。类,然后在我得到匹配时立即实例化它们 - 不确定这是否会减慢我的速度但不会。我想一些基准测试是有序的。

真的,速度绝对是我的目标,但我知道这是丑陋的代码。

现在代码看起来像这样:

sin.mark(0)
site = findsite(txt)
sin.reset()

if ( site == "site1") {
   loadlexer1;
   loadparser1;
} else if (site == "site2") {
   loadlexer2;
   loadparser2;
}
.................
} else if (site == "site8") {
   loadparser8;
   loadparser8;
}

findsite(txt) {
  ...................
  if line.indexOf("site1-identifier") {
    site = site1;
  } else if(line.indexOf("site2-identifier") {
    site = site2;
  } else if(line.indexOf("site3-identifier") {
    site = site3;
  }
  .........................
  } else if(line.indexOf("site8-identifier") {
    site = site8;
  }
}

一些澄清

1)是的,我确实有9个不同的语法,我用antlr构建,所以他们都有自己的词法分析器/解析器objs。

2)是的,截至目前我们正在比较字符串并且显然将用某种整数映射替换。    我也考虑过将网站标识符固定在一个正则表达式中,但是我不认为这会加快任何速度。

3)是的,这是伪代码所以我不会对这里的语义过于挑剔..

4)kdgregory注意到我无法创建lexer / parser对的一个实例是正确的

我喜欢哈希的想法,让代码看起来更好看,但我不认为它会加速我。

11 个答案:

答案 0 :(得分:7)

标准方法是使用Map将关键字符串连接到将处理它们的词法分析器:

Map<String,Lexer> lexerMap = new HashMap<String,Lexer>();
lexerMap.put("source1", new Lexer01());
lexerMap.put("source2", new Lexer02());
// and so on

一旦你检索到标识要使用的词法分析器的字符串,就可以从地图中检索它,如下所示:

String grammarId = // read it from a file, whatever
Lexer myLexer = lexerMap.get(grammarId);

但是,您的示例代码有一些怪癖。首先,indexOf()调用表明您没有独立字符串,并且Map不会查看字符串内部。所以你需要有一些方法从你读过的任何字符串中提取实际的密钥。

其次,词法分析器和解析器通常保持状态,因此您将无法创建单个实例并重用它。这表明您需要创建一个工厂类,并将其存储在地图中(这是抽象工厂模式)。

如果你期望有很多不同的词法分析器/解析器,那么使用地图驱动的方法是有意义的。对于一个小数字,if-else链可能是你最好的选择,正确封装(这是工厂方法模式)。

答案 1 :(得分:2)

使用多态几乎可以保证比字符串操作更快,并且将在编译时检查其是否正确。 site真的是一个字符串吗?如果是这样,FindSite应该被称为GetSiteName。我希望FindSite返回一个知道相应词法分析器和解析器的Site对象。

另一个速度问题是编码速度。在单个类中使用不同的词法分析器和解析器肯定会更好(可能在另一个类中具有共享功能)。这会让你的代码变得更小,并且人们更容易理解。

答案 2 :(得分:1)

  

我正在考虑将词法分析器/解析器派生到sep中。类,然后在我得到匹配后立即实例化

看起来你已经有了答案。这将创建更灵活的代码,但不是更快。

  

我想一些基准测试是按顺序

是的,用两种方法衡量并做出明智的决定。我猜你的方式已经足够了。

也许,如果您遇到困难的是"kilometric"方法,可以使用extract method在不同的函数中重构它。

最重要的是首先要有一个能够完成工作的解决方案,即使它很慢,一旦你有了工作,就可以对其进行分析并检测可以提高性能的点。请记住"Rules of optimization"

答案 3 :(得分:1)

假设您的代码效率低下。

实际解析输入需要花费更多时间(例如)1%的时间吗?

如果没有,你就会有更大的“炸鱼”。

答案 4 :(得分:1)

类似的东西:

Map<String,LexerParserTuple> lptmap = new HashMap<String,LexerParserTuple>();
lpt=lptmap.get(site)
lpt.loadlexer()
lpt.loadparser()

结合一些正则表达式魔术而不是string.indexOf()来抓取网站的名称应该大大清理你的代码。

答案 5 :(得分:1)

Replace Conditional With Polymorphism

对于findite()的半个小节,你可以简单地设置一个HashMap来从站点标识符到站点。另一种清理方法就是返回站点字符串,因此:

String findsite(txt) {
  ...................
  if line.indexOf("site1-identifier") 
    return site1;
  if(line.indexOf("site2-identifier")
    return  site2;
  if(line.indexOf("site3-identifier")
    return  site3;
...
}

以这种方式使用indexOf()并不具有表现力;我会使用equals()或contains()。

答案 6 :(得分:0)

我不知道Java,但是某些语言允许切换到字符串。

switch(site)
{
    case "site1": loadlexer1; loadparser1; break;
    case "site2": loadlexer2; loadparser2; break;
    ...
}

对于秒位,使用正则表达式提取标识符并打开它。您可能最好使用enum

答案 7 :(得分:0)

我会更改find​​site的类型以返回网站类型(超类),然后利用多态... 这应该比字符串操作更快......

你需要单独的词法分析器吗?

答案 8 :(得分:0)

使用Map将站点配置为loadstrategy结构。然后根据“站点”进行简单查找,然后执行适当的策略。对于findSite()也可以这样做。

答案 9 :(得分:0)

可以有一个标识符与网站的地图,然后迭代地图条目。

// define this as a static somewhere ... build from a properties file
Map<String,String> m = new HashMap<String,String>(){{
    put("site1-identifier","site2");
    put("site2-identifier","site2");
}}

// in your method
for(Map.Entry<String,String> entry : m.entries()){
    if( line.contains(entry.getKey())){
        return line.getValue();
    }
}
清洁工:是的 更快:不知道......应该足够快

答案 10 :(得分:0)

你可以使用反射

char site = line.charAt(4);
Method lexerMethod = this.getClass().getMethod( "loadLexer" + site, *parameters types here*)
Method parserMethod = this.getClass().getMethod( "loadparser" + site, *parameters types here*)

lexerMethod.invoke(this, *parameters here*);
parserMethod.invoke(this, *parameters here*);