在Delphi中检查关键字列表中关键字的最快方法是什么?

lke*_*ler 7 delphi lookup optimization case-statement

我有一小部分关键字.我真正想做的是类似于:

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;
Run Code Online (Sandbox Code Playgroud)

不幸的是,CASE语句不能像字符串一样使用.

我可以使用直接的IF THEN ELSE IF结构,例如:

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);
Run Code Online (Sandbox Code Playgroud)

但我听说这是相对低效的.

我一直在做的是:

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;
Run Code Online (Sandbox Code Playgroud)

这当然不是最好的编程风格,但它对我来说工作正常,到目前为止没有任何区别.

那么在Delphi中重写它的最佳方法是什么呢?它既简单又易懂,而且速度快?

(供参考,我使用带有Unicode字符串的Delphi 2009.)


跟进:

托比建议我只使用If Then Else结构.回顾我使用CASE语句的示例,我可以看到这是一个可行的答案.不幸的是,我无意中收录了CASE隐藏了我真实的问题.

我实际上并不关心它是哪个关键字.如果特定方法可以像POS方法那样识别它,那么这只是一个奖励.我需要知道关键字是否在关键字集中.

所以我真的想知道是否有更好的东西:

if pos(' ' + MyKeyword + ' ', ' CHIL HUSB WIFE SEX ') > 0 then
Run Code Online (Sandbox Code Playgroud)

在这种情况下,If Then Else等效似乎不是更好:

if (MyKeyword = 'CHIL') or (MyKeyword = 'HUSB') or (MyKeyword = 'WIFE') 
      or (MyKeyword = 'SEX') then
Run Code Online (Sandbox Code Playgroud)

在Barry对Kornel的问题的评论中,他提到了TDictionary Generic.我还没有接受新的Generic系列,看起来我应该深入研究它们.我的问题是它们是否是为了效率而建立的,以及如何使用TDictionary在外观和速度上与上述两行进行比较?


在后来的分析中,我发现字符串的连接如:(''+ MyKeyword +'')在时间上非常昂贵,应该尽可能避免.几乎任何其他解决方案都比这样做更好.

Kor*_*icz 7

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;
Run Code Online (Sandbox Code Playgroud)

一般情况下,不要使用字符串作为"键",使用枚举 - 它们更安全,并且您可以大大提高速度.

不幸的是Delphi(据我所知)没有标准的哈希表实现,它易于使用,但是你总是可以自己卷起来.

顺便说一句,code for SEX听起来比"啤酒代码"更有趣:P

  • Generics.Collections包含`TDictionary <TKey,TValue>`. (7认同)

Arn*_*hez 5

您可以使用const表(必须进行alpha排序)和快速二进制排序.它非常高效,不需要任何散列.

这是使用的功能:

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;
Run Code Online (Sandbox Code Playgroud)

在这里,一些关键字的例子:

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');
Run Code Online (Sandbox Code Playgroud)

而且它非常易于使用:

  if IsKeyWord(PASCALKEYWORDS,UpperCase('BEGIN')) then
   ....
Run Code Online (Sandbox Code Playgroud)

您可以轻松修改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;
Run Code Online (Sandbox Code Playgroud)


Uwe*_*abe 3

我主要使用 StrUtils 中的 IndexText 函数来实现此目的。它与 pos 方法类似,但返回值与字符串的单个长度无关。作为一个噱头,它也不区分大小写(如果您不希望这样,请使用 IndexStr)。

function IndexText(const AText: string; const AValues: array of string): Integer; overload;
Run Code Online (Sandbox Code Playgroud)

对这些函数的注释实际上提到了 case 结构:

{ AnsiMatchText 和 AnsiIndexText 提供类似大小写的函数来处理字符串 }