Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Wortliste erstellen: Zählroutine zu langsam (https://www.delphipraxis.net/151273-wortliste-erstellen-zaehlroutine-zu-langsam.html)

friedemann2009 10. Mai 2010 17:56


Wortliste erstellen: Zählroutine zu langsam
 
Liebe Leute,

ich hätte noch eine weitere Frage, die v.a. die Performance angeht. Ich habe rund 5000 Textdateien, die zeilenweise aus Wörtern bestehen (genauer: jede Zeile hat das Format ZuZählenderAusdruck TAB EinUninteressantesWort TAB NochEinUninteressantesWort). Ich möchte die einzelnen Wörter jeder Datei zählen und in eine Liste addiert zusammenfassen. Jede Textdatei hat im Schnitt 4000 Zeilen, macht insg. ~ 20000000 zu verarbeitende Zeilen. Den Code, den ich bisher dazu habe, braucht aber viel zu lang (~Stunde..). Hat jemand eine Idee, wie ich das optimieren könnte?

Danke und viele Grüße,
frieder

Delphi-Quellcode:
Procedure Clickblabla();
var
 TokenListeKorpus, tokenlist: tstringlist
begin
  TokenListeKorpus:= tstringlist.create;
  tokenlist:= tstringlist.create;

  {Lade Textdatei in tokenliste..}

  ZaehleAlleWoerterDerListe(tokenlist {Quelldatei}, TokenListeKorpus {Gesamtliste});

  {Gib die TokenListeKorpus aus, gib die Listen frei usw.}
end;

Procedure ZaehleAlleWoerterDerListe(const tokenliste: tstringlist; var TokenListeKorpus: tstringlist);
var
 i: integer;
 token: string;
begin
 for i:= 0 to tokenliste.count-1 do
   begin
     token:= gibmirtoken(tokenliste.strings[i], #9);
     if TokenListeKorpus.Values[token] = '' then
       TokenListeKorpus.Values[token] := '1'
     else
       TokenListeKorpus.Values[token]:= inttostr(strtoint(TokenListeKorpus.Values[token]) + 1);
   end;
end;

function gibmirtoken(s:string; sep:char) :string;
begin
  Result := Copy(s, 1, Pos(sep, s) - 1);
  { Funktion ermittelt aus tokenliste.zeile den zu zählenden "Ausdruck" der Zeile, die das
    folgendes Format hat: Ausdruck TAB EinUninteressantesWort TAB NochEinUninteressantesWort }
end;

himitsu 10. Mai 2010 18:41

Re: Wortliste erstellen: Zählroutine zu langsam
 
a) In einer sortierten Liste wird ein schnellerer Algorithmus zum Suchen verwendet.
b) Das Object-Property kann man schön für einen Integer verwenden, anstatt hier ständig die Strings/Zeilen für den Value-Zugriff zerlegen zu müssen. (Value und Name wird in nur einem String verwaltet und bei solchen Zugriffen jedesmal erneut zerlegt und zusammengesetzt)
c) Und wenn die Anzahl nicht mehr als String gehalten wird, kann man sich auch noch die ganzen Integer<>String-Umwandlungen sparen.
d) Var und Const ist bei Objeten nicht nötig ... es sei denn man will wirklich den Instantzeiger verändern und nicht nur den Inhalt

Delphi-Quellcode:
Procedure ZaehleAlleWoerterDerListe(TokenListe, TokenListeKorpus: TStringList);
var
  i, i2: Integer;
  token: String;
begin
  for i := 0 to TokenListe.Count - 1 do
  begin
    token := GibMirToken(TokenListe[i], #9);
    i2 := TokenListeKorpus.IndexOf(Token);
    if i2 <= 0 then
      TokenListeKorpus.AddObject(token, TObject(1))
    else
      TokenListeKorpus.Objects[i2] := TObject(Integer(TokenListeKorpus.Objects[i2]) + 1);
  end;
end;
und in neueren Delphiversionen ginge auch sowas:
Delphi-Quellcode:
Procedure ZaehleAlleWoerterDerListe(TokenListe, TokenListeKorpus: TStringList);
var
  i: Integer;
  S, Token: String;
begin
  for S in TokenListe do
  begin
    token := GibMirToken(S, #9);
    i := TokenListeKorpus.IndexOf(Token);
    if i <= 0 then
      TokenListeKorpus.AddObject(token, TObject(1))
    else
      TokenListeKorpus.Objects[i] := TObject(Integer(TokenListeKorpus.Objects[i]) + 1);
  end;
end;
Die Tockenliste sollte natürlich entsprechend erstellt werden, damit sie auch optimal genutzt werden kann
Delphi-Quellcode:
TokenListeKorpus := TStringList.Create;
TokenListeKorpus.Sorted := True;
TokenListeKorpus.Duplicates := dupIgnore;
Integer(TokenListeKorpus.Objects[i]) enthält dann also die Anzahl zu dem Token mit dem Namen TokenListeKorpus[i] .

friedemann2009 10. Mai 2010 18:52

Re: Wortliste erstellen: Zählroutine zu langsam
 
Vielen Dank! Das versuche ich mal umzusetzen.

Schönen Abend,
frieder

EDIT Ich bin baff, das ist ja ultraschneller! Ha, wieder was dazugelernt! Danke nochmals herzlich!!

himitsu 10. Mai 2010 19:30

Re: Wortliste erstellen: Zählroutine zu langsam
 
Zitat:

Zitat von friedemann2009
EDIT Ich bin baff, das ist ja ultraschneller! Ha, wieder was dazugelernt! Danke nochmals herzlich!!

Ist schon von "Vorteil", wenn man einige 100.000.000 "langsame" Stringoperationen einfach mal wegläßt.

z.B. hier mal das, welches sich hinter TokenListeKorpus.Values versteckt:
Delphi-Quellcode:
procedure TStrings.SetValue(const Name, Value: string);
var
  I: Integer;
begin
  I := IndexOfName(Name);
  if Value <> '' then
  begin
    if I < 0 then I := Add('');
    Put(I, Name + NameValueSeparator + Value);
  end else
  begin
    if I >= 0 then Delete(I);
  end;
end;

function TStrings.GetValue(const Name: string): string;
var
  I: Integer;
begin
  I := IndexOfName(Name);
  if I >= 0 then
    Result := Copy(Get(I), Length(Name) + 2, MaxInt) else
    Result := '';
end;
Da passiert also so Einiges.

Und nochmal bezüglich dem "sortiert" ... die Funktion Find ist im Verhältnis zur Listengröße expotentiell schneller als der "normale" IndexOf:
Delphi-Quellcode:
function TStringList.IndexOf(const S: string): Integer;
begin
  if not Sorted then Result := inherited IndexOf(S) else
    if not Find(S, Result) then Result := -1;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:44 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz