Как превратить условную цепочку в более быстрый и менее уродливый код? - PullRequest
0 голосов
/ 21 мая 2009

У меня 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, поэтому они ВСЕ будут иметь свои собственные объекты лексера / парсера.

2) да, на данный момент мы сравниваем строки и, разумеется, это будет заменено некоторой целочисленной картой. Я также подумал о том, чтобы вставить идентификаторы сайта в одно регулярное выражение, однако я не верю, что это что-то ускорит.

3) да, это псевдокод, поэтому я бы не стал слишком требователен к семантике здесь ..

4) kdgregory правильно отмечает, что я не могу создать один экземпляр пары лексер / парсер

Мне нравится идея хэша, чтобы код выглядел немного лучше, однако я не думаю, что это увеличит скорость до некоторой степени.

Ответы [ 11 ]

7 голосов
/ 21 мая 2009

Стандартный подход заключается в использовании 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 - это, вероятно, ваша лучшая ставка, правильно инкапсулированная (это шаблон фабричного метода).

2 голосов
/ 21 мая 2009

Использование полиморфизма почти гарантированно быстрее, чем манипуляции со строками, и будет проверено на правильность во время компиляции. site действительно строка? Если это так, FindSite должен называться GetSiteName. Я ожидаю, что FindSite вернет объект Site, который знает соответствующий лексер и анализатор.

Еще одна проблема скорости - это скорость кодирования. Определенно было бы лучше иметь разные лексеры и парсеры в отдельных классах (возможно, с общей функциональностью в другом). Это сделает ваш код немного меньше, и это будет значительно легче для понимания.

1 голос
/ 21 мая 2009

Предположим, ваш код неэффективен.

Потребуется ли больше времени, чем (скажем) 1% времени, чтобы фактически проанализировать ввод?

Если нет, то у вас есть больше "рыба, чтобы жарить".

1 голос
/ 21 мая 2009

Я думал о выводе нереста лексера / парсера в sep. классы, а затем их создание, как только я получу совпадение

Похоже, у вас уже есть ответ. Это создаст код, который будет более гибким, но не обязательно быстрее.

Я полагаю, что некоторые тесты в порядке

Да, оцените оба подхода и примите обоснованное решение. Я думаю, так, как у вас уже было бы достаточно.

Возможно, если вас беспокоит наличие «километрического» метода , вы можете изменить его в различных функциях с помощью извлечения метода .

Самое главное - сначала найти решение, которое выполняет работу, даже если оно медленное, и, как только оно заработало, профилируйте его и определите точки, в которых производительность может быть улучшена. Запомните «Правила оптимизации»

1 голос
/ 21 мая 2009

Заменить условное на полиморфизм

Для полумера, для 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 () или has ().

1 голос
/ 21 мая 2009

Что-то вроде:

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

в сочетании с некоторой магией регулярных выражений, а не string.indexOf (), чтобы захватить имена сайтов, должны значительно очистить ваш код.

0 голосов
/ 22 мая 2009

Я не знаю о Java, но некоторые языки позволяют переключателю принимать строки.

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

Что касается секундного бита, используйте регулярное выражение, чтобы извлечь идентификатор и включить его. Вам может быть лучше использовать enum.

0 голосов
/ 21 мая 2009

Вы можете использовать отражение возможно

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*);
0 голосов
/ 21 мая 2009

Может иметь карту идентификаторов и сайтов, а затем просто перебрать записи на карте.

// 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();
    }
}

очиститель: да быстрее: не знаю ... должно быть достаточно быстро

0 голосов
/ 21 мая 2009

Используйте карту для настройки сайта для загрузки стратегии. Затем требуется простой поиск на основе «сайта», и вы выполняете соответствующую стратегию. То же самое можно сделать для findSite ().

...