2010-08-30 11 views
5

Voglio cambiare qualche codice Perl in C++. Devo sapere come implementare gli hash Perl annidati in C++. Ho pensato a STL come a una buona scelta e ho usato le mappe. Con l'aiuto di mappe posso solo creare un semplice hash, ma non so come creare una struttura hash nidificato.Come faccio a implementare l'equivalente di nidificato Perl hash in C++?

mio Perl hash è come questo:

%foo = (
    "bar1" => { 
     Default => 0, 
     Value => 0 
    }, 
    "bar2" => { 
     Default => 2, 
     value => 5, 
     other => 4 
    } 
) 

posso modificare così: $foo{"bar1"}->{"Default"} = 15.

come faccio a fare questo in C++ utilizzando STL? Forse questa è una domanda semplice ma non riesco a capirlo.

+2

Nota che risponde riferimento std :: mappa non si tradurrà in tabelle hash. Questi sono contenitori associativi basati su BTree. Devi passare a Boost Unordered o al nuovo STL, che include le tabelle hash in modo nativo. –

+0

Questa è stata una domanda sorprendentemente divertente. Buon lavoro @saran –

risposta

2

Si possono avere nidificato hash con std::map<key_t,std::map<key_t,value_t> >.

+0

Ho modificato il tuo post per aggiungere uno spazio tra il >>, che purtroppo non verrà compilato (si pensa che sia operatore >>). Se non sbaglio, 0x risolverà il problema ma fino ad allora ... –

+0

Ci ho pensato, è "risolto" in 0x ma in questo caso è meglio essere chiari; grazie. –

4

Potrebbe essere necessario il tipo:

std::map< std::string, std::map<std::string, int> > 

Potrebbe essere necessario utilizzare struct (o class), invece.

struct Element { 
    int default; 
    int value; 
    int other; 
    Element(): default(0), value(0), other(0) 
    { } 
    Element(int default_, int value_, int other_) 
    : default(default_) 
    , value(value_) 
    , other(other_) 
    { } 
}; 

int main() { 
    std::map<std::string, Element> elements; 
    elements["bar1"]; // Creates element with default constructor 
    elements["bar2"] = Element(2,5,4); 
    elements["bar3"].default = 5; // Same as "bar1", then sets default to 5 
    return 0; 
} 
2

Come osservato Steve Townsend, std::map è simile nel concetto, ma ha una diversa implementazione.

Creare il contenitore nidificato in C++ è un po 'più dettagliato:

#include <tr1/unordered_map> 
#include <iostream> 

typedef std::tr1::unordered_map< std::string, int > Inner; 
typedef std::tr1::unordered_map< std::string, Inner > Outer; 

int main() 
{ 
    Outer foo; 

    Inner::value_type bar1_data[] = { 
    Inner::value_type("Default", 0), 
    Inner::value_type("Value", 0), 
    }; 
    const size_t n_bar1_data = sizeof(bar1_data)/sizeof(*bar1_data); 

    foo["bar1"] = Inner(bar1_data, bar1_data + n_bar1_data); 

    Inner::value_type bar2_data[] = { 
    Inner::value_type("Default", 2), 
    Inner::value_type("value", 5), 
    Inner::value_type("other", 4), 
    }; 
    const size_t n_bar2_data = sizeof(bar2_data)/sizeof(*bar2_data); 

    foo["bar2"] = Inner(bar2_data, bar2_data + n_bar2_data); 

Come documentato in perlref, frecce tra parentesi subscripting sono opzionali, così si potrebbe avere scritto (ha commentato per mantenere il flusso del programma C++)

// $foo{"bar1"}{"Default"} = 15; 

che è abbastanza vicino a C++:

foo["bar1"]["Default"] = 15; 

Per buona misura, abbiamo stampare la struttura risultante e tornare 0 da main:

for (Outer::const_iterator o = foo.begin(); o != foo.end(); ++o) { 
    std::cout << o->first << ":\n"; 
    for (Inner::const_iterator i = o->second.begin(); i != o->second.end(); ++i) 
     std::cout << " - " << i->first << " => " << i->second << '\n'; 
    } 

    return 0; 
} 

uscita:

bar1: 
    - Value => 0 
    - Default => 15 
bar2: 
    - Default => 2 
    - value => 5 
    - other => 4

NOTA: ingresso e di uscita hanno lo stesso ordine in questo programma giocattolo, ma non dipendono da questo comportamento!

Se invece si preferisce utilizzare boost::unordered_map, cambiare alcune linee nella parte superiore del vostro programma:

#include <boost/unordered_map.hpp> 

typedef boost::unordered_map< std::string, int > Inner; 
typedef boost::unordered_map< std::string, Inner > Outer; 
+1

Non essendo un grande utente C++, tr1 è l'anteprima del nuovo STL, mentre boost è una sostituzione di terze parti per gran parte delle funzionalità? –

+2

@Evan [TR1] (http://en.wikipedia.org/wiki/C%2B%2B_Technical_Report_1) è un insieme di aggiunte proposte alla libreria standard C++, molte delle quali sono basate su [Boost] (http://www.boost.org/) librerie. Puoi pensare a Boost come a un terreno di prova per future aggiunte alla libreria standard C++. –

0

permettetemi di aggiungere un po 'di meta-informazioni alla domanda. Altri hanno fornito esempi di codice di lavoro effettivo.

Oltre ad essere un diverso tipo di contenitore (O (accesso log (n) per mappe C++ vs. O (1) per gli hash Perl), la differenza principale tra le mappe C++ e gli hash Perl si trova nello statico vs. distinzione di tipo dinamico delle lingue stesse Gli argomenti di una mappa indicano chiaramente il tipo di chiave e valore, mentre per gli hash Perl, solo la chiave ha un tipo noto (una stringa) .Il valore è uno scalare, ma che potrebbe e di solito contenere qualsiasi tipo di struttura dei dati.Così, è molto ingombrante per utilizzare le mappe C++ per una struttura di dati piuttosto ordinaria Perl come questa:

{ 
    foo => [qw(bar baz)], 
    quibble => { 
    lion => 'animal', 
    pine => 'plant', 
    }, 
    count => 5, 
} 

Questo è più di una distinzione pratica di uno concettuale, perché si può sempre definire una struct/categoria/tipo che conterrà cose arbitrarie (vedi altre risposte). È semplicemente molto più lavoro e finisce per essere piuttosto ingombrante da usare.

Utilizzando un semplice tipo su modelli come map<string, map<string, Stuff> > è davvero possibile solo se la struttura dei dati è molto regolare.

0

Come faccio questo in C++ usando STL? Forse questa è una domanda semplice ma non riesco a capirlo.

Il modo in cui si utilizza l'hash di Perl è il solito modo di imitare lo struct di C/C++.

Così penso che il C++ traduzione più vicina del codice è:

struct foobar { 
    int Default, value, other; 

    foobar(int Default_ = 0, int value_ = 0, int other_ = 0) 
    : Default(Default_), value(value_), other(other_) 
    {} 
}; 

std::map< std::string, foobar > foo; 

foo["bar1"] = foobar(0, 0); 
foo["bar2"] = foobar(2, 5, 4); 
Problemi correlati