Что Самый Быстрый Путь состоит в том, чтобы Проверить на Ключевое слово в Списке Ключевых слов в Delphi?

У меня есть маленький список ключевых слов. То, что я действительно хотел бы сделать, сродни:

case MyKeyword of
  'CHIL': (code for CHIL);
  'HUSB': (code for HUSB);
  'WIFE': (code for WIFE);
  'SEX': (code for SEX);
  else (code for everything else);
end;

К сожалению, Оператор выбора не может использоваться как этот для строк.

Я ЗАТЕМ ЕЩЕ мог использовать прямое ЕСЛИ ЕСЛИ конструкция, например:

if MyKeyword = 'CHIL' then (code for CHIL)
else if MyKeyword = 'HUSB' then (code for HUSB)
else if MyKeyword = 'WIFE' then (code for WIFE)
else if MyKeyword = 'SEX' then (code for SEX)
else (code for everything else);

но я услышал, что это относительно неэффективно.

Что я делал, вместо этого:

P := pos(' ' + MyKeyword + ' ', ' CHIL HUSB WIFE SEX ');
case P of
  1: (code for CHIL);
  6: (code for HUSB);
  11: (code for WIFE);
  17: (code for SEX);
  else (code for everything else);
end;

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

Таким образом, что лучший способ состоит в том, чтобы переписать это в Delphi так, чтобы это было оба просто, понятно, но также и быстро?

(Для ссылки я использую Delphi 2009 со строками Unicode.)


Продолжение:

Toby рекомендовал, чтобы я просто использовал, Если Затем Еще создают. Оглядываясь назад на мои примеры, которые использовали Оператор выбора, я вижу, как это - жизнеспособный ответ. К сожалению, мое включение СЛУЧАЯ непреднамеренно скрыло мой реальный вопрос.

Я на самом деле не забочусь, какое ключевое слово это. Это - просто премия, если конкретный метод может определить, что как метод POS может. То, в чем я нуждаюсь, должно знать, является ли ключевое слово в наборе ключевых слов.

Таким образом, действительно я хочу знать, существует ли что-нибудь лучше, чем:

if pos(' ' + MyKeyword + ' ', ' CHIL HUSB WIFE SEX ') > 0 then

Если Затем Еще эквивалентный не кажется лучше в этом случае том, чтобы быть:

if (MyKeyword = 'CHIL') or (MyKeyword = 'HUSB') or (MyKeyword = 'WIFE') 
      or (MyKeyword = 'SEX') then

В комментарии Barry к вопросу Kornel он упоминает Дженерик TDictionary. Я еще не взял на новых Универсальных наборах, и похоже, что я должен копаться в них. Мой вопрос здесь состоял бы в том, создаются ли они для эффективности и как был бы с помощью TDictionary выдержать сравнение во взглядах и в скорости к вышеупомянутым двум строкам?


В более позднем профилировании я нашел что конкатенация строк как в: (' '+ MyKeyword + ''), ОЧЕНЬ дороги мудрый временем и должен избежаться, когда это возможно. Почти любое другое решение лучше, чем выполнение этого.

7
задан lkessler 22 April 2010 в 15:38
поделиться

8 ответов

Для такого рода вопрос я могу настоятельно рекомендовать читать руководство по разработчикам Mobifrorge . Многие полезные советы там, в том числе как выбрать компромиссы, которые неизбежно являются результатом мобильной связи.

-121--3181283-

В основном я использую функцию Indextext из Strutils для этой цели. Это похоже на ваш пост, но возвращаемое значение не зависит от индивидуальной длины строк. Как уловка, это также нечувствительно к регистру (используйте IndexStr, если вы этого не хотите).

function IndexText(const AText: string; const AValues: array of string): Integer; overload;

Комментарий к этим функциям на самом деле упоминает корпус Construct:

{AnsimatchText & Ansiindextext Обеспечить в этом случае функцию для решения С струнами}

3
ответ дан 6 December 2019 в 07:50
поделиться
type TKeyword = ( ECHIL, EHUSB, EWIFE, ESEX )
const TKeyNames : array[TKeyword] of string = ( 'CHIL', 'HUSB', 'WIFE', 'SEX' );

Key : TKeyword

case Key of 
  ECHIL : (code for CHIL);
  EHUSB : (code for HUSB);
  EWIFE : (code for WIFE);
  ESEX  : (code for SEX);
  else (code for everything else);
end;

В целом не используйте строки как «ключи», используют перечисления - они безопаснее, и вы получаете большую часть скорости.

К сожалению, Delphi (насколько я знаю) не имеет стандартной реализации Hashtable, которая была бы простым в использовании, вы всегда можете откатывать свои собственные.

BTW, код для секса звучит гораздо веселее, чем «код для пива»: P

7
ответ дан 6 December 2019 в 07:50
поделиться

Ваша серия Если , если утверждения, чтобы проверить, является ли вход какой-либо из данных ключевых слов, может быть сокращена, проверяя единичные символы, выручите как можно скорее. Ваш пример

if MyKeyword = 'CHIL' then (code for CHIL)
else if MyKeyword = 'HUSB' then (code for HUSB)
else if MyKeyword = 'WIFE' then (code for WIFE)
else if MyKeyword = 'SEX' then (code for SEX)
else (code for everything else);

может быть заменен

KW := kwOther;
if MyKeyword <> '' then begin
  case MyKeyword[1] of
    'C': if MyKeyword = 'CHIL' then KW := kwCHIL;
    'H': if MyKeyword = 'HUSB' then KW := kwHUSB;
    'S': if MyKeyword = 'SEX' then KW := kwSEX;
    'W': if MyKeyword = 'WIFE' then KW := kwWIFE;
  end;
end;
case KW of
  kwCHIL: {code for CHIL};
  kwHUSB: {code for HUSB};
  kwSEX: {code for SEX};
  kwWIFE: {code for WIFE};
else
  {code for everything else};
end;

для нечувствительных к регистру ключевым словам. Чехол будет проверять бы верхний и нижний регистр, а сравнение будет использоваться AnsiComparetext () . Если у вас есть несколько ключевых слов с той же первой буквой, вы могли бы гнездиться этими характеристиками .

Принимая это к максимуму, вы можете реализовать государственный станок, в котором используется PCHAR для расчета следующего состояния, который бы вел в футляре иначе , как только первый Соответствующий символ найден. Быстрее было бы быстрее, чем любое решение с участием хэшей.

3
ответ дан 6 December 2019 в 07:50
поделиться

Вы также можете перейти к более объектно-ориентированному подходу и иметь что-то вроде

TCommand = class
public
  procedure Execute; virtual; abstract;
end;
TCommandClass = class of TCommand;

и иметь завод создавать команды для вас

TCommandFactory = class
public
  procedure RegisterKeyWord (const Keyword : String; CmdClass : TCommandClass);
  function  CreateCommand (const Keyword : String) : TCommand;
end;

, то вызова код будет выглядеть так просто, как:

CommandFactory.CreateCommand (Keyword).Execute;

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

Я знаю, что это может интерпретировать как не ответ на ваш вопрос, потому что это не о том, как быстро вы можете сделать это. Но это еще один подход, который может стоить думать о.

0
ответ дан 6 December 2019 в 07:50
поделиться

Отказ от ответственности: Ответ основан на обновленном операторе задачи, то есть просто проверяя ли строковые спички или нет.

Если вы действительно хотите пойти на этот последний бит производительности, может помочь некоторую дополнительную информацию о ваших наборах данных.

  • Сколько ключевых слов мы говорим о? (Какой порядок)
  • Набор ключевых слов фиксирован?
  • Есть ли много повторения в наборе ввода? (то есть одинаковые ключевые слова часто повторяются)
  • Каково ожидаемое отношение попадания / мисс? Вы ожидаете, что соответствуют одному ключевому слову на каждые тысячи входных слов или вы ожидаете, что почти каждое слово найден?

Например,

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

Большинство из них являются немного экстремальными для общих задач настройки производительности. Я бы, наверное, профиль стандартных реализаций «Hashed Set» (Delphi Generics, JCL и т. Д.) Сначала посмотреть, какой из них лучше всего работает в вашем задаче.

1
ответ дан 6 December 2019 в 07:50
поделиться

Для чистого кода лучше использовать случаю с перечислениями, или если..else с строками, как и другие. Однако есть несколько решений от проторенной дорожки, хотя, если вы действительно хотите пойти туда.

Один состоит в том, чтобы использовать карту хэш строки, которая похожа на список «индексированных» строками. Значения в списке будут указатели процедуры к коду, которое вы хотите выполнить для каждой строки. Все процедуры должны были бы иметь одинаковые точные параметры - и вам придется самостоятельно написать хэш-карту, или найти один, который вы можете использовать E.G. в JVCL.

// prepare the hash map
myHashMap[ 'CHIL' ] := @procToCallForChil;
myHashMap[ 'HUSB' ] := @procToCallForHusb;

// use the hash map
type
  TPrototypeProc = procedure( your-parameters-here );
var
  procToCall : TPrototypeProc;
begin
  @procToCall := myHashMap[ 'CHIL' ];
  procToCall( your-parameters-here );
end;

Два, и это что-то странное, которое я попробовал один раз: если и только если ваши идентификаторы строки вписываются в 4 символа (как во всех ваших примерах), и это строки ANSI (не строки Unicode), вы можете на карте строки до целых чисел напрямую. 32-битное целое число эквивалентно по размеру с 4-байтовой строкой, поэтому вы можете сделать:

function FourCharStringToInteger( const s : ansistring ) : integer;
begin
  assert(( length( s )  = 4 ), 'String to convert must be exactly 4 characters long!');
  move( s[1], result, 4 );
end;

любой идентификатор строки, который короче 4 символов должен быть дополнены пробелами, и строки чувствительны к регистру ('Chil «И« Чил »доходятся разные значения). Чтобы использовать это с помощью оператора CASE, вам нужно предварительно предложить значения, которые могут или не могут быть подходящими для вашей цели:

id_CHIL := FourCharStringToInteger( 'CHIL' );

и, наконец, вы можете иметь свое заявление о случаях:

case id of
  id_CHIL : ...
  id_HUSB : ...
end;

Это код «особый уход» И может иметь значение только в том случае, если у вас есть сотни или больше ваших строк идентификатора - это действительно не должно быть сделано вообще :) Это, безусловно, легко сломать. Я согласен, хотя этот случай утверждает, что реплики, чем бесконечные процессии ... else ... блоки.

2
ответ дан 6 December 2019 в 07:50
поделиться

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

Вот функция использования:

function IsKeyWord(const KeyWords: array of string; const aToken: String): Boolean;
// aToken must be already uppercase
var First, Last, I, Compare: Integer;
begin
  First := Low(Keywords);
  Last := High(Keywords);
  Result := False;
  while First <= Last do
  begin
    I := (First + Last) shr 1;
    Compare := CompareStr(Keywords[i],aToken);
    if Compare = 0 then
      begin
        Result := True;
        break;
      end
    else
    if Compare < 0  then
      First := I + 1 else
      Last := I - 1;
  end;
end;

и здесь, некоторые примеры ключевых слов:

const
  PASCALKEYWORDS: array[0..100] of string =
  ('ABSOLUTE', 'ABSTRACT', 'AND', 'ARRAY', 'AS', 'ASM', 'ASSEMBLER',
   'AUTOMATED', 'BEGIN', 'CASE', 'CDECL', 'CLASS', 'CONST', 'CONSTRUCTOR',
   'DEFAULT', 'DESTRUCTOR', 'DISPID', 'DISPINTERFACE', 'DIV', 'DO',
   'DOWNTO', 'DYNAMIC', 'ELSE', 'END', 'EXCEPT', 'EXPORT', 'EXPORTS',
   'EXTERNAL', 'FAR', 'FILE', 'FINALIZATION', 'FINALLY', 'FOR', 'FORWARD',
   'FUNCTION', 'GOTO', 'IF', 'IMPLEMENTATION', 'IN', 'INDEX', 'INHERITED',
   'INITIALIZATION', 'INLINE', 'INTERFACE', 'IS', 'LABEL', 'LIBRARY',
   'MESSAGE', 'MOD', 'NAME', 'NEAR', 'NIL', 'NODEFAULT', 'NOT', 'OBJECT',
   'OF', 'OR', 'OUT', 'OVERRIDE', 'PACKED', 'PASCAL', 'PRIVATE', 'PROCEDURE',
   'PROGRAM', 'PROPERTY', 'PROTECTED', 'PUBLIC', 'PUBLISHED', 'RAISE',
   'READ', 'READONLY', 'RECORD', 'REGISTER', 'REINTRODUCE', 'REPEAT', 'RESIDENT',
   'RESOURCESTRING', 'SAFECALL', 'SET', 'SHL', 'SHR', 'STDCALL', 'STORED',
   'STRING', 'STRINGRESOURCE', 'THEN', 'THREADVAR', 'TO', 'TRY', 'TYPE',
   'UNIT', 'UNTIL', 'USES', 'VAR', 'VARIANT', 'VIRTUAL', 'WHILE', 'WITH', 'WRITE',
   'WRITEONLY', 'XOR');

  DFMKEYWORDS: array[0..4] of string = (
    'END', 'FALSE', 'ITEM', 'OBJECT', 'TRUE');

  CKEYWORDS: array[0..47] of string = (
  'ASM', 'AUTO', 'BREAK', 'CASE', 'CATCH', 'CHAR', 'CLASS', 'CONST', 'CONTINUE',
  'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', 'FLOAT', 'FOR',
  'FRIEND', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', 'NEW', 'OPERATOR', 'PRIVATE',
  'PROTECTED', 'PUBLIC', 'REGISTER', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF',
  'STATIC', 'STRUCT', 'SWITCH', 'TEMPLATE', 'THIS', 'THROW', 'TRY', 'TYPEDEF',
  'UNION', 'UNSIGNED', 'VIRTUAL', 'VOID', 'VOLATILE', 'WHILE');

  CSHARPKEYWORDS : array[0..86] of string = (
  'ABSTRACT', 'AS', 'BASE', 'BOOL', 'BREAK', 'BY3', 'BYTE', 'CASE', 'CATCH', 'CHAR',
  'CHECKED', 'CLASS', 'CONST', 'CONTINUE', 'DECIMAL', 'DEFAULT', 'DELEGATE', 'DESCENDING',
  'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EVENT', 'EXPLICIT', 'EXTERN', 'FALSE', 'FINALLY',
  'FIXED', 'FLOAT', 'FOR', 'FOREACH', 'FROM', 'GOTO', 'GROUP', 'IF', 'IMPLICIT',
  'IN', 'INT', 'INTERFACE', 'INTERNAL', 'INTO', 'IS', 'LOCK', 'LONG', 'NAMESPACE',
  'NEW', 'NULL', 'OBJECT', 'OPERATOR', 'ORDERBY', 'OUT', 'OVERRIDE', 'PARAMS',
  'PRIVATE', 'PROTECTED', 'PUBLIC', 'READONLY', 'REF', 'RETURN', 'SBYTE',
  'SEALED', 'SELECT', 'SHORT', 'SIZEOF', 'STACKALLOC', 'STATIC', 'STRING',
  'STRUCT', 'SWITCH', 'THIS', 'THROW', 'TRUE', 'TRY', 'TYPEOF', 'UINT', 'ULONG',
  'UNCHECKED', 'UNSAFE', 'USHORT', 'USING', 'VAR', 'VIRTUAL', 'VOID', 'VOLATILE',
  'WHERE', 'WHILE', 'YIELD');

и очень просты в использовании:

  if IsKeyWord(PASCALKEYWORDS,UpperCase('BEGIN')) then
   ....

Вы можете легко изменить функцию ISKeyWord () для возврата индекса токена Если вам это нужно.

function KeyWordIndex(const KeyWords: array of string; const aToken: String): integer;
// aToken must be already uppercase
var First, Last, Compare: Integer;
begin
  First := Low(Keywords);
  Last := High(Keywords);
  while First <= Last do
  begin
    result := (First + Last) shr 1;
    Compare := CompareStr(Keywords[result],aToken);
    if Compare = 0 then
      exit else
    if Compare < 0  then
      First := result + 1 else
      Last := result - 1;
  end;
  result := -1; // not found
end;
5
ответ дан 6 December 2019 в 07:50
поделиться

Я думаю, что

if x then

else

, безусловно, лучшее решение. Ваше собственное решение очень несуществует, и для повышения квалификации в эффективности его не стоит. Eff Тогда еще конструкция идеально подходит для того, что вы хотите.

2
ответ дан 6 December 2019 в 07:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: