开发者

Set Of String??!!

You are familiar with this block:

Var
  mySet: Set Of Char;
  C:开发者_运维知识库 Char;
begin
  mySet := ['a', 'b', 'c'];
  If C In mySet Then ShowMessage('Exists');
end;

Is there any way to declare Set Of STRING? or is there similar code that i can use instead? The important part of this block is If C In mySet Then ShowMessage('Exists'); I wanna use something like this about a set of string.

Thanks.


Sets are implemented using bit arrays. So no, you cannot have a 'set of string'. Use a TStringList instead, ie:

var 
  mySet: TStringList;
  S: String;
begin 
  S := ...;
  mySet := TStringList.Create;
  try
    mySet.Add('a');
    mySet.Add('b');
    mySet.Add('c'); 
    if mySet.IndexOf(S) <> -1 Then ShowMessage('Exists');
  finally
    mySet.Free;
  end;
end; 


The RTL System.StrUtils unit provides a very interesting method for this:

function MatchText(const AText: string; const AValues: array of string): Boolean; overload;

Use it like this:

  if MatchText(sLanguages, ['fr-FR', 'en-GB', 'de-DE', 'it-IT', 'fr-CH', 'es-ES']) then
    Writeln('found')


You can make use of this.

type 
  TAnyEnum = (aeVal1, aeVal2, aeVal3);
  TEnuns = set of TAnyEnum;
  TAnyMessages: array [TAnyEnum] of String;

const 
  MyMessages: TAnyMessages = ('Exists', 'Something else', 'WTF!?');

var
  MySet : TEnums;
begin
  MySet = [aeVal1, aeVal2];
  If aeVal1 in MySet then ShowMessage(MyMessages[aeVal1]);
end;


It is shame, but Delphi still don't have even very basic data structures, like sets. Suggestion of "Fabricio Araujo" is very limited in usage - you have to know all possible strings, so it is useless when you read them from somewhere. It is also impossible to have more then 256 items in such "alsmost set of strings". Remy Lebeau suggested to use TStringList for that purpose, but it is absolutely inefficient when you have big sets. In modern versions of Delphi it is possible to use TDictionary to keep unordered set of anything, including strings:

procedure TForm6.FormCreate(Sender: TObject);
type
  TEmpty = record end;
var
  MySet: TDictionary<String, TEmpty>;
  Dummy: TEmpty;
begin
  MySet := TDictionary<String, TEmpty>.Create;
  try
    MySet.Add('Str1', Dummy);
    MySet.Add('Str2', Dummy);
    MySet.Add('Str3', Dummy);
    if MySet.TryGetValue('Str2', Dummy) then
      ShowMessage('Exists');;
  finally
    MySet.Free;
  end;
end;

It is case sensitive. If you need case insensitive implementation, you can make all strings lowercased before put/look then in set (use AnsiLowerCase function for that).


Fabricio Araujo posted the best answer to the original question.
But if you are asking that question you should also ask yourself
"Do I only need to know if Str is in this Set?".

For the question "Is MonthAbbr in the set of allowed abbreviations of month names?"
chances are your next question is "Well then, which month is it?".

  • You code should answer both questions in one function call. (e.g. return Month Number else 0)
  • Should be Readable. Make your intent imediatly clear to the code maintainer.
  • Should store the allowed strings in a single obvious place in code.
  • Should allow easy revision ("Add 'Fall' and 'Autumn' and bring that back to me in 15 min.").
  • Be reasonably efficient for the expected size of the set.
  • Use the standard library (VCL) if possible. Else use an external library.
  • If your code does this sort of thing often, separate the strings from the implementation, and put the implementation in one place so it can be changed cleanly.

So your code might read
case GetMonthNumber(Str) of 0: //Error: Str is not a Month Name or Abbreviation 1: //etc.

For the implementation, the simplest general answer is to use a sorted TStringList in Delphi 7 (as the question is tagged).


Another option is to declare a new "String" type and use a RECORD HELPER to implement the "IN" operator on it:

USES System.SysUtils, System.StrUtils;

TYPE
  TextString    = RECORD
                  PRIVATE
                    STR                 : STRING;
                  PUBLIC
                    CLASS OPERATOR      Implicit(CONST S : STRING) : TextString; INLINE;
                    CLASS OPERATOR      Implicit(CONST S : TextString) : STRING; INLINE;
                  END;
{ TextString }

CLASS OPERATOR TextString.Implicit(CONST S : STRING) : TextString;
  BEGIN
    Result.STR:=S
  END;

CLASS OPERATOR TextString.Implicit(CONST S : TextString) : STRING;
  BEGIN
    Result:=S.STR
  END;

TYPE
  TStringHelper = RECORD HELPER FOR TextString
                    CLASS OPERATOR In(CONST T : TextString ; CONST ARR : ARRAY OF STRING) : BOOLEAN;
                    FUNCTION       IsEmpty : BOOLEAN;
                  END;

{ TStringHelper }

CLASS OPERATOR TStringHelper.In(CONST T : TextString ; CONST ARR : ARRAY OF STRING) : BOOLEAN;
  BEGIN
    Result:=MatchText(T.STR,ARR)
  END;

FUNCTION TStringHelper.IsEmpty : BOOLEAN;
  BEGIN
    Result:=STR.IsEmpty
  END;

VAR
  S     : TextString;
  T     : STRING;

begin
  try
    REPEAT
      WRITE('Enter an animal name: '); READLN(T); S:=T;
      IF S.IsEmpty THEN EXIT;
      IF S IN ['Bird','Lion','Monkey'] THEN WRITELN('You got it!') ELSE WRITELN('Nope - Try Again...')
    UNTIL FALSE
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

It is implicitly compatible with STRING type in assignments and comparisons. It is implicitly case insensitive in the "IN" operator, and by extending the record helper (with Equal/NotEqual/GreaterThan/LessThan/GreaterThanOrEqual/LessThanOrEqual operators to STRING and TextString types) you can make a fully-case-insensitive string type.

Disadvantage: You cannot use this new type in VAR/OUT STRING parameters, and it's a little bit slower than a standard STRING type due to its conversion back and forth and UPPER-casing in comparisons.


function MatchStr(const AText: string; const AValues: array of string): Boolean; overload;

MatchStr determines if any of the strings in the array AValues match the string specified by AText using a case sensitive comparison. It returns true if at least one of the strings in the array match, or false if none of the strings match.

For a case insensitive match, use the MatchText routine.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜