2010-10-23 15 views
5

Mi piacerebbe avere un dizionario che restituisce un valore predefinito quando la chiave di ricerca non viene trovata. Leggendo dalla documentazione:Delphi Generics> Dizionario con valore predefinito

Generics.Collections.Tdictionary [...] Questa classe fornisce una mappatura [...] e il contenuto iniziale.

1 - Come? C'è un modo per farlo in Python: {1: 'one'; 2: "due"}?

Generics.Collections.TDictionary.TryGetValue [...] TryGetValue restituisce true se la chiave specificata è nel dizionario e fornisce il valore in Valore. In caso contrario, restituisce false e Valore viene impostato sul tipo di valore predefinito di Tvalue.

2 - Come posso impostare questo valore predefinito? Non riesco a trovare un costruttore (potrebbe essere che ho appena cercato nel posto sbagliato. Mi aspetto qualcosa del tipo "costruttore Create (DefaultValue: TValue);)

Quindi sto cercando di implementare il mio (può essere non è necessario Vedi sopra.): (! recensioni e suggerimenti sono ben accetti)

Il codice è:

unit Util; 

interface 

uses 
    Generics.collections; 

type 

    // 
    // Dictionary with default response 
    // 
    TDefaultDictonary<K, V> = class(TObjectDictionary<K, V>) 
    private 
     M_DefaultValue : V; 

    public 
     constructor Create(Defaultvalue : V); 
     destructor Destroy; reintroduce; 
     function GetDefaultValue : V; 
     function TryGetValue(const Key: K; out Value: V): Boolean; 
     function GetValueOf(const Key: K) : V; 
    end; 

implementation 

// 
// Contructor and destructor 
// 
constructor TDefaultDictonary<K, V>.Create(Defaultvalue : V); 
begin 
    inherited Create; 

    M_DefaultValue := Defaultvalue; 
end; 

destructor TDefaultDictonary<K, V>.Destroy; 
begin 
    inherited Destroy; 
end; 

// 
// Get the default Value 
// 
function TDefaultDictonary<K, V>.GetDefaultValue : V; 
begin 
    Result := M_DefaultValue; 
end; 


// 
// Try to get a value from the dictionary for the given key. 
// 
// If the value is found then "Value" holds it and the function returns true. 
// If the value is not found then "Value" holds the default value and the 
// function returns false. 
// 
function TDefaultDictonary<K, V>.TryGetValue(const Key: K; out Value: V): Boolean; 
var 
    IsKeyFound : boolean; 
    DictVal : V; 

begin 
    IsKeyFound := inherited TryGetValue(Key, DictVal); 
    if not IsKeyFound then begin 
     DictVal := M_DefaultValue; 
    end; 

    // Outputs: 
    Value := DictVal; 
    Result := IsKeyFound; 
end; 


// 
// Get a value from the dictionary for the given key. 
// 
// If the value is found then the function returns it. 
// If the value is not found the function returns the default value. 
// 
function TDefaultDictonary<K, V>.GetValueOf(const Key: K) : V; 
var 
    DictVal : V; 

begin 
    TryGetValue(Key, DictVal); 

    Result := DictVal; 
end; 

e le prove sono:

unit Test_Utils; 
{ 
    Test the TDefaultDictionary functionality 
} 

interface 

uses 
    Sysutils, Math, TestFramework, Util; 

type 

    TestUtil = class(TTestCase) 

    public 
     procedure SetUp; override; 
     procedure TearDown; override; 

    published 
     procedure TestDefaultDictionaryGetDefaultResponse; 
     procedure TestDefaultDictionaryExistingKey; 
     procedure TestDefaultDictionaryNotExistingKey; 

    end; 


implementation 


procedure TestUtil.SetUp; 
begin 
end; 

procedure TestUtil.TearDown; 
begin 
end; 


procedure TestUtil.TestDefaultDictionaryGetDefaultResponse; 
var 
    dd : TDefaultDictonary<integer, string>; 

begin 
    dd := TDefaultDictonary<integer, string>.Create('Default response'); 
    checkEquals('Default response', dd.GetDefaultValue); 

    dd.Free; 
end; 

procedure TestUtil.TestDefaultDictionaryExistingKey; 
var 
    dd : TDefaultDictonary<integer, string>; 
    outVal : string; 
    isKeyFound : boolean; 

begin 
    dd := TDefaultDictonary<integer, string>.Create('Default response'); 
    dd.Add(1, 'My one'); 

    checkEquals(1, dd.Count, 
     'One element as count'); 

    isKeyFound := dd.TryGetValue(1, outVal); 
    check(isKeyFound, 
     'Key not found by TryGetValue'); 

    checkEquals('My one', outVal, 
     'Value given by TryGetValue'); 

    checkEquals('My one', dd[1], 
     'Value given by indexing as array'); 

    dd.Free; 
end; 


procedure TestUtil.TestDefaultDictionaryNotExistingKey; 
var 
    dd : TDefaultDictonary<integer, string>; 
    outVal : string; 
    isKeyFound : boolean; 

begin 
    dd := TDefaultDictonary<integer, string>.Create('Default response'); 
    dd.Add(1, 'one'); 

    isKeyFound := dd.TryGetValue(2, outVal); 
    check(not isKeyFound, 
     'Key should not be found by TryGetValue'); 

    checkEquals('Default response', outVal, 
     'Default Value given by TryGetValue'); 

    checkEquals('Default response', dd.GetValueOf(2), 
     'Default Value given by indexing as array'); 

    // 
    // It is possible to oveload the indexer operator? 
    // Please review or delete me ! 
    // 
    //checkEquals('Default response', dd[2], 
    //  'Value given by indexing as array'); 
    // 

    dd.Free; 
end; 


initialization 
    RegisterTest(TestUtil.Suite); 
end. 

Questo è lontano da essere completo. Mi piacerebbe che anche l'operatore indicizzatore funzioni (vedi l'ultimo test). È possibile? Cosa dovrebbe essere anche implementato?

Fa questa implementazione perde M_DefaultValue (Sono nuovo a Delphi). Non posso fare qualcosa M_DefaultValue.Free nel distruttore (non è così flessibile a causa del vincolo del costruttore) Cosa si può fare qui?

Grazie in anticipo,

Francesco

+0

Hai sollevato una domanda molto buona, poiché attualmente, in Delphi, dobbiamo fare due ricerche nel dizionario. Lo stesso vale per PHP. –

risposta

1

Prima di scrivere tutto il codice da soli, si potrebbe desiderare di guardare le classi generiche nella DeHL library.

Supporta questo:

Tipo concetto di supporto che definisce un insieme di default "classi di supporto" per ogni tipi built-in Delphi (usato come predefiniti in collezioni). Le classi personalizzate "tipo " possono essere registrate per i tipi di dati personalizzati .

--jeroen

+0

Mi aspetto che sia qualcosa di diverso: comparatori predefiniti e simili, non un valore predefinito restituito da un dizionario in caso di "chiave non presente". –

+0

La tua risposta non è amichevole. L'idea principale della domanda dell'autore era di evitare due ricerche: la prima ricerca non controlla se il valore esiste e la seconda ricerca restituisce effettivamente il valore. Se avessi fornito una risposta chiara e chiara, con un esempio di codice, sarebbe stato utile, ad esempio, su come utilizzare TryGetValue e che ogni tipo ha un valore predefinito che non può essere modificato, ad esempio Integer ha 0, stringa ha '', ecc. –

+0

@MaximMasiutin come utente francis non ha mai risposto (l'ultima volta è attivo nel 2010) e ha sollevato una manciata di domande in una domanda SO, è difficile capire quale sia il suo inciampo. Quindi - dato lo stato del codice nella domanda - ho pensato che scrivere un buon codice di libreria stabile fosse il problema principale e diretto alla libreria DeHL - allora attuale. Oggi vorrei indirizzare a http://spring4d.org –

0

Il problema principale è GetItem non è virtuale in TDictionary <>. Questo può essere risolto semplicemente aggiungendo

property Items[const Key: K]: V read GetValueOf write SetItem; default; 

alla classe.

Btw, i distruttori devono essere sovrascritti, non reintrodotti quindi quando lo si archivia in una variabile definita come classe gerarchia più alta, chiama il distruttore corretto. Questa è solo una buona pratica, ma in questo caso specifico, gli articoli [] non funzionerebbero come vuoi se lo facessi.

Dio vi benedica.