2010-09-14 12 views
6

ho implementato traduzione in un'applicazione mettendo tutte le stringhe in fase di esecuzione in un TStringList con:Come posso cercare più velocemente le coppie nome/valore in una lista TSL di Delphi?

procedure PopulateStringList; 
begin 
    EnglishStringList.Append('CAN_T_FIND_FILE=It is not possible to find the file'); 
    EnglishStringList.Append('DUMMY=Just a dummy record'); 
    // total of 2000 record appended in the same way 
    EnglishStringList.Sorted := True; // Updated comment: this is USELESS! 
end; 

allora ottengo la traduzione utilizzando:

function GetTranslation(ResStr:String):String; 
var 
    iIndex : Integer; 
begin 
    iIndex := -1; 
    iIndex := EnglishStringList.IndexOfName(ResStr); 
    if iIndex >= 0 then 
    Result := EnglishStringList.ValueFromIndex[iIndex] else 
    Result := ResStr + ' (Translation N/A)'; 
end; 

Comunque con questo approccio ci vogliono circa 30 microsecondi per individuare un record, c'è un modo migliore per ottenere lo stesso risultato?

UPDATE: Per riferimento futuro mi scrivere qui la nuova implementazione che utilizza TDictionary come suggerito (funziona con Delphi 2009 e successivi):

procedure PopulateStringList; 
begin 
    EnglishDictionary := TDictionary<String, String>.Create; 
    EnglishDictionary.Add('CAN_T_FIND_FILE','It is not possible to find the file'); 
    EnglishDictionary.Add('DUMMY','Just a dummy record'); 
    // total of 2000 record appended in the same way 
end; 


function GetTranslation(ResStr:String):String; 
var 
    ValueFound: Boolean; 
begin 
    ValueFound:= EnglishDictionary.TryGetValue(ResStr, Result); 
    if not ValueFound then Result := Result + '(Trans N/A)'; 
end; 

La nuova funzione GetTranslation esegue 1000 volte più veloce (sul mio 2000 record di esempio) quindi la prima versione.

+1

Sebbene 'IndexOf' benefici da una TStringList che viene ordinata,' IndexOfName' no. Ciò non significa che * non sia * in alcune classi discendenti, ma TStringList non sovrascrive la ricerca lineare di base fornita da TStrings. –

+0

Sì, ho controllato, è lo stesso (quindi posso risparmiare tempo a non ordinare l'elenco. Dal momento che non lo sto modificando, avrebbe più senso chiamare Sort, invece di impostare True ordinato (comunque non lo farò) Grazie – LaBracca

risposta

15

In Delphi 2009 o versione successiva utilizzare TDictionary < string, string> in Generics.Collections. Si noti inoltre che ci sono strumenti gratuiti come http://dxgettext.po.dk/ per la traduzione di applicazioni.

+0

Grazie, sì, so tdxggettext ma l'implementazione della traduzione che ho descritto era già fatto da uno sviluppatore precedente, quindi sto solo cercando di migliorarlo, e con THasedStringList ho ottenuto un risultato soddisfacente. Comunque darò una prova (per mia conoscenza personale al suggerimento di TDictionary hai dato). – LaBracca

+1

@VilleK: Ci sono alcuni bug in TDictionary e alcuni altri generici in Delphi 2009, quindi è meglio evitarlo a meno che non si utilizzi Delphi 2010 o XE. E ad essere onesti, c'è pochissima documentazione disponibile su come usarlo e sulla sua funzionalità. Mi ha dato un tempo molto più difficile di quello che dovrebbe avere. – lkessler

+0

Sì, ricordo che c'era un bug con TDictionary in Delphi 2009, ma ora lo uso molto a Delphi 2010 e non ho riscontrato alcun problema. Penso che funzioni alla grande. Anche TObjectDictionary nella stessa unità è molto utile perché può opzionalmente assumere la proprietà di entrambe le chiavi e valori. Per me l'unità Generics.Collections è uno dei motivi principali per l'aggiornamento da versioni Delphi precedenti. –

17

THashedStringList dovrebbe essere migliore, penso.

+0

GRAZIE !!!! Ora identificando 45 record passati da 50 ms a 2 ms 25 volte più veloce! – LaBracca

+1

Cool - in qualche modo non ho mai saputo di questa cosa. Dovrò esaminarlo. –

12

Se THashedStringList funziona per te, è grandioso. La sua più grande debolezza è che ogni volta che si modifica il contenuto della lista, la tabella di hash viene ricostruita. Quindi funzionerà per te fintanto che la tua lista rimarrà piccola o non cambierà molto spesso.

Per ulteriori informazioni su questo, vedere: THashedStringList weakness, che offre alcune alternative.

Se si dispone di una grande lista che può essere aggiornata, si consiglia di provare GpStringHash per gabr, che non deve ricalcolare l'intera tabella ad ogni modifica.

+0

Non ho bisogno per aggiornare l'elenco, viene riempito solo una volta, quindi penso di poter stare con THashedStringList poiché ora il collo di bottiglia è finito. – LaBracca

+0

+1 per 'THashedStringList weak' – SOUser

4

Penso che non si usi EnglishStringList (TStringList) correttamente. Questo è un elenco ordinato, si aggiungono elementi (stringhe), lo si ordina, ma quando si esegue una ricerca, lo si fa con una stringa parziale (solo il nome, con IndexOfName).

Se si utilizza IndexOfName in un elenco ordinato, il TStringList non può utilizzare dicotomica ricerca. Usa la ricerca sequenziale.

(questa è l'attuazione di IndexOfName)

for Result := 0 to GetCount - 1 do 
    begin 
    S := Get(Result); 
    P := AnsiPos('=', S); 
    if (P <> 0) and (CompareStrings(Copy(S, 1, P - 1), Name) = 0) then Exit; 
    end; 

Credo che questo sia il motivo di scarso rendimento.
L'alternativa è utilizzare 2 TStringList:
* Il primo (ordinato) contiene solo il "Nome" e un puntatore al secondo elenco che contiene il valore; È possibile implementare questo puntatore al secondo elenco utilizzando il "puntatore" della proprietà Object.
* La seconda lista (non ordinata) contiene i valori.

Durante la ricerca, lo si fa alla prima lista; In questo caso è possibile utilizzare il metodo Trova. quando trovi il nome, il puntatore (implementato con la proprietà Object) ti fornisce la posizione sulla seconda lista con il valore.

In questo caso, il metodo Trova su Elenco ordinamenti è più efficiente di HashList (che deve eseguire una funzione per ottenere la posizione di un valore).

Saluti.

Pd: Mi scusi per gli errori con l'inglese.

2

È inoltre possibile utilizzare una classe di supporto per riprogrammare la funzione "IndexOfName":

TYPE 
    TStringsHelper = CLASS HELPER FOR TStrings 
        FUNCTION IndexOfName(CONST Name : STRING) : INTEGER; 
        END; 

FUNCTION TStringsHelper.IndexOfName(CONST Name : STRING) : INTEGER; 
    VAR 
    SL : TStringList ABSOLUTE Self; 
    S,T : STRING; 
    I : INTEGER; 

    BEGIN 
    IF (Self IS TStringList) AND SL.Sorted THEN BEGIN 
     S:=Name+NameValueSeparator; 
     IF SL.Find(S,I) THEN 
     Result:=I 
     ELSE IF (I<0) OR (I>=Count) THEN 
     Result:=-1 
     ELSE BEGIN 
     T:=SL[I]; 
     IF CompareStrings(COPY(T,1,LENGTH(S)),S)=0 THEN Result:=I ELSE Result:=-1 
     END; 
     EXIT 
    END; 
    Result:=INHERITED IndexOfName(Name) 
    END; 

(o implementare in una classe TStrings discendente se non ti piace aiutanti classe o non li avere nella vostra Versione Delphi).

Questo utilizzerà una ricerca binaria su una TStringList ordinata e una ricerca sequenziale su altre classi TStrings.

Problemi correlati