У меня есть маленький список ключевых слов. То, что я действительно хотел бы сделать, сродни:
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 + ''), ОЧЕНЬ дороги мудрый временем и должен избежаться, когда это возможно. Почти любое другое решение лучше, чем выполнение этого.
Для такого рода вопрос я могу настоятельно рекомендовать читать руководство по разработчикам Mobifrorge . Многие полезные советы там, в том числе как выбрать компромиссы, которые неизбежно являются результатом мобильной связи.
-121--3181283-В основном я использую функцию Indextext из Strutils для этой цели. Это похоже на ваш пост, но возвращаемое значение не зависит от индивидуальной длины строк. Как уловка, это также нечувствительно к регистру (используйте IndexStr, если вы этого не хотите).
function IndexText(const AText: string; const AValues: array of string): Integer; overload;
Комментарий к этим функциям на самом деле упоминает корпус Construct:
{AnsimatchText & Ansiindextext Обеспечить в этом случае функцию для решения С струнами}
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
Ваша серия Если
, если утверждения, чтобы проверить, является ли вход какой-либо из данных ключевых слов, может быть сокращена, проверяя единичные символы, выручите как можно скорее. Ваш пример
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
для расчета следующего состояния, который бы вел в футляре иначе
, как только первый Соответствующий символ найден. Быстрее было бы быстрее, чем любое решение с участием хэшей.
Вы также можете перейти к более объектно-ориентированному подходу и иметь что-то вроде
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;
таким образом, что у вас есть Локализованные и инкапсулированные все строки ключевых слов в простом заводском классе, которые впустя могут очень простые изменения на языке ввода. Использование этого командного подхода имеет другие преимущества, такие как легкая расширяемость.
Я знаю, что это может интерпретировать как не ответ на ваш вопрос, потому что это не о том, как быстро вы можете сделать это. Но это еще один подход, который может стоить думать о.
Отказ от ответственности: Ответ основан на обновленном операторе задачи, то есть просто проверяя ли строковые спички или нет.
Если вы действительно хотите пойти на этот последний бит производительности, может помочь некоторую дополнительную информацию о ваших наборах данных.
Например,
Большинство из них являются немного экстремальными для общих задач настройки производительности. Я бы, наверное, профиль стандартных реализаций «Hashed Set» (Delphi Generics, JCL и т. Д.) Сначала посмотреть, какой из них лучше всего работает в вашем задаче.
Для чистого кода лучше использовать случаю с перечислениями, или если..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 ... блоки.
Вы можете использовать таблицу 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;
Я думаю, что
if x then
else
, безусловно, лучшее решение. Ваше собственное решение очень несуществует, и для повышения квалификации в эффективности его не стоит. Eff Тогда еще конструкция идеально подходит для того, что вы хотите.