Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - case of mit strings


MaxiTB - Fr 19.03.04 19:10
Titel: case of mit strings
Aus aktuellen Anlaß ein kleine Performance-/Erfahrungs-Frage:

Wie wir alle wissen, unterstützt Delphi kein 'case of' mit dem Typen string.

Nun sieht in meinen Programmen der work around bis jetzt so aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
if Value=String1 then
begin
 //Bla1
end else if Value=String2 then
begin
 //Bla2
end else if Value=String3 then
begin
 //Bla3
end else
begin
 //BlaElse
end;


Das finde ich aber nicht wirklich hübsch - oder in meiner Landessprache: Des is schiach. Was ja von der Bedeutung aufs selbe rausläuft.

Aber wie kann man jetzt am besten eine Liste von Konstanten flott und fesch checken ?

Ich hab mir foldendes überlegt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
//--- Global irgendwo ---
gCaseList:=THashedStringList.Create(); // Oder TStringList für alle <D6
gCaseList.Add(String1);
gCaseList.Add(String2);
gCaseList.Add(String3);

//--- Dann bei der Methode ---
case gCaseList.IndexOf(Value) of
 0//BlaElse
 1//BlaElse
 2//BlaElse
 else //BlaElse
end;


Der Aufwand ist zwar ein bisserl höher, aber das ganze ist wesentlich übersichtlicher als die andere Methode - und vielleicht ja auch schneller (vor allem durchs hashing bei vielen Fällen).

Was haltet ihr von diesem Ansatz - oder wißt ihr vielleicht noch einen besseren ?

In meinem Fall muß ein Parser eine Unzahl Commands auswerten (okay, sind nur zwei Dutzend *g*) - da ist mir dieser Gedanke gekommen, besonders, da man diese Liste ja in initialization/finalization unterbringen kann.


patrick - Fr 19.03.04 19:14

es geht auch noch so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:

  Funktioniert unabhängig von der Klein-/Großscheibung in dem Input-String: 
  Versuchen Sie es z.B. mit "Mother" oder "mOTHER"... 

  Damit kann man seine eigene Makrosprache entwickeln oder bestimmte 
  Teile eines Strings aus einem SQL statement, aus einer wohlgeformten Datei 
  wie im XML-Format...interpretieren. 
}
 


  It works fine and it's not case sensitive: 
  Try to give "Mother" or "mOTHER" in the Input-String... 

  For exemple, you may use this to programm your own macro command system. 
  You can also parse a well formed text file (XML...) 
  Or to interpret a Database Field name as a selector, or whatelse you want. 
}
 


uses 
  TypInfo; 

type 
  TNumericChoiceParent = (ncp_Mother, ncp_Father, ncp_Child); 

procedure TForm1.btChooseClick(Sender: TObject); 
var 
  S: string
begin 
  S := InputEdit.Text; 
  case TNumericChoiceParent(GetEnumValue(TypeInfo(TNumericChoiceParent), 'ncp_' + S)) of 
    ncp_Mother: ShowMessage('Hello Mom :o)'); 
    ncp_Father: ShowMessage('Hi, Dad -]'); 
    ncp_Child: ShowMessage('Shut up and eat your soup !-('); 
  else 
    ShowMessage('Who do you think that you are?'); 
  end
end;


quelle: http://www.swissdelphicenter.ch/de/showcode.php?id=1028

ist aber nicht wirklich eine verbesserung


MaxiTB - Fr 19.03.04 19:20

::patrick

Das ist eine ziemlich fuchsige Idee - muß ich schon sagen *g*.

:arrow: Allerdings - wird die TypeInfo standandmäßig mitgelinkt ?
:arrow: Wie schauts mit der Geschwindigkeit aus - ich will den Parser ja boosten - nicht bremsen ? Schon Erfahrung damit gemacht ?

Ansonsten gefällt mir der Ansatz ziemlich *g* Schade das Delphi keine Macros unterstützt, sonst würd ich gleich eines dafür basteln !

Zitat:
'Shut up and eat your soup !-('

:lol:


matze - Fr 19.03.04 19:22

haben wir hier in den FAQs oder den Tuts nicht sogar ein workaround zu dem thema ?


MaxiTB - Fr 19.03.04 19:26

::matze

Jep - aber wenn du dir das ansiehst, dann merkst du schnell, daß da eine Schleife über die Anzahl der Strings durchlaufen wird.
Ich wollte ja ne Lösung, die unter Umständen schneller ist - aber schon mal mindestens in der selben Klasse spielt.

Deshalb hab ich hier das Thema nochmals angepostet und mir gleich den Link darauf gespart ;-) .

Der Vollständigkeit halber, hier der angesprochene Lösungsvorschlag: http://www.delphi-forum.de/viewtopic.php?t=11117


MartinPb - Sa 20.03.04 01:25

::MaxiTB

Willst du alle deine Befehle in einer Prozedur auswerten? Ist doch sehr unübersichtlich. Wenn du das auf Prozeduren verteilst, dann sieht das optisch bereits schöner aus:


Delphi-Quelltext
1:
2:
3:
4:
  if Value = String1 then Procedure1 else
  if Value = String2 then Procedure2 else
  if Value = String3 then Procedure3 else
                          Procedure4;


Aber mal zu deinem zweiten Vorschlag. Den kann man auf jeden Fall verfeinern:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
  //--- Global irgendwo ---
  gCaseList:=THashedStringList.Create();
  gCaseList.CommaText:=AnsiUpperCase('String1,String2,String3');

  //--- Dann bei der Methode ---
  case gCaseList.IndexOf(AnsiUpperCase(Value)) of
    0//BlaElse
    1//BlaElse
    2//BlaElse
  else //BlaElse
  end;


Noch einfacher:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
  //--- Global irgendwo ---
  with THashedStringList.Create do
  begin
    CommaText:=AnsiUpperCase('String1,String2,String3');

    //--- Dann bei der Methode ---
    case IndexOf(AnsiUpperCase(Value)) of
      0//BlaElse
      1//BlaElse
      2//BlaElse
    else //BlaElse
    end;
  end;


Anonymous - Sa 20.03.04 01:48

MaxiTB hat folgendes geschrieben:
Jep - aber wenn du dir das ansiehst, dann merkst du schnell, daß da eine Schleife über die Anzahl der Strings durchlaufen wird.
Ich wollte ja ne Lösung, die unter Umständen schneller ist - aber schon mal mindestens in der selben Klasse spielt.


Und IndexOf errät die Position des Strings?
Das schnellste ist bei strings immer if, da case aus technischer sicht nicht möglich ist (bzw. es würde intern auf if ... else hinauslaufen)

// Edit

Wenn du einen 32-Bit Hash berechnest wirds zwar warscheinlich schneller, aber nicht unbedingt lesbar.


Phantom1 - Sa 20.03.04 09:40

Also wenn's dir um geschwindigkeit geht, ich habe eben mal alle 4 bisherigen lösungvorschläge getestet. Dabei hab ich die Case-anweisung jeweils in einer forschleife 50000 mal durchlaufen, um etwas genauere ergebnisse zu bekommen. gemessen wurde natürlich priority: realtime bzw timecritical.

- "normale IF abfrage" von MaxiTB dauerte etwa 3 millisekunden
- "THashedStringList" von MaxiTB dauerte etwa 24 millisekunden
- "Type" möglichkeit von patrick dauerte etwa 18 millisekunden
- "CaseStr" möglichkeit aus der FAQ von mir (Phantom1) dauerte etwa 10 millisekunden

Das schnellste ist also immernoch die normale IF abfrage, auch wenn's unübersichtlich ist.