PDA

Visualizza la versione completa : [C++] Utilizzare STL::Map


beppe66
09-12-2009, 18:38
Ciao a tutti,
sono alle prese con l'uso del container Map, e se ho capito bene posso utilizzarlo per creare dei semplice hash map..(std::map )

Non sono fortissimo in programmazione, ma allo stesso tempo penso di capirne qualcosa.
correggetemi se sbaglio, ma posso definire la mia struttura come una coppia dove il primo elemento è una chiave e il secondo elemento è un dato.

std::map<std::string, DataMsg> pair;

dove DataMsg è il tipo di un'oggetto da me definito...
Ora quello che vorrei chiedervi è: "Posso mettere come chiave (ossia come primo elemento della definizione di map) un'ulteriore oggetto?"

es: std::map<Current, DataMsg> pair;

dove Current fondamentalmente è un'oggetto o meglio un'array di tre elementi.. Questa cosa mi crea piccoli errori in fase di compilazione, come posso gestirla?

Grazie dell'aiuto.
Giuseppe

shodan
09-12-2009, 18:51
Originariamente inviato da beppe66
Non sono fortissimo in programmazione, ma allo stesso tempo penso di capirne qualcosa.
correggetemi se sbaglio, ma posso definire la mia struttura come una coppia dove il primo elemento è una chiave e il secondo elemento è un dato.

In genere si, ma dipende dal dato, non dalla mappa.


Posso mettere come chiave (ossia come primo elemento della definizione di map) un'ulteriore oggetto?"

Si, a patto che rispetti alcuni vincoli.
Dev'essere confrontabile con < altrimenti la map non sa come manipolare la chiave.
In alternativa serve una funzione
bool comparazione(oggetto a, oggetto b)
tale che a < b dia true (o false per inserire gli elementi in ordine inverso)

beppe66
09-12-2009, 18:56
Il mio oggetto che devo usare come chiave è un'array di tre elementi...ciò cosa comporta?
Non riesco ben a capire quello che mi hai detto..

Grazie Giuseppe.

shodan
09-12-2009, 20:23
Che non puoi usare un array grezzo come chiave della map se non crei una funzione per il confronto. Inoltre non puoi usare direttamente un array, ma solo il puntatore.
Per capirsi:


int val1[3] = {0,1,2};
int val2[3] = {1,2,3};
map<int[3],string> mp;

mp.insert(make_pair(val1,string("1")));
mp.insert(make_pair(val2,string("2")));

Non compila.


int val1[3] = {0,1,2};
int val2[3] = {1,2,3};
map<int*,string> mp;

mp.insert(make_pair(val1,string("1")));
mp.insert(make_pair(val2,string("2")));


invece si.
Però così non vengono confrontati gli array, ma solo il valore del puntatore al primo elemento, in questo modo:


key1 < key2

non


(*key1) < (*key2)
o
key1[0] < key2[0]


Per un simile confronto, devi fare una funzione ( o meglio ancora un functor) apposta.

Il mio consiglio è di fare qualcosa del genere:


template <typename T, int MassimaDimensioneDelloArray>
struct MyArrayCompare {
bool operator()(T a, T b) {
// implementazione del contronto tra array
// ad esempio.
return (a[0] < b[0]);
}
};

int main() {

int val1[3] = {0,1,2};
int val2[3] = {1,2,3};
map<int*,string,MyArrayCompare <int*,3> > mp;

mp.insert(make_pair(val1,string("1")));
mp.insert(make_pair(val2,string("2")));


};

alka
10-12-2009, 10:20
Il linguaggio di programmazione va indicato anche nel titolo, come da Regolamento (http://forum.html.it/forum/showthread.php?s=&threadid=973887).

Qui l'ho aggiunto io. Tienilo a mente per il futuro.

Ciao! :ciauz:

beppe66
10-12-2009, 11:15
Ciao di nuovo,
voglio in primis ringraziarti per le tue risposte complete, sei stato preziosissimo alla mia causa..ihihihih... ti spiego cosa ho fatto perchè c'è ancora qualcosa che non va:

ho definito come appunto tu mi hai detto una funzione apposta per il confronto:
template <typename T, int MassimaDimensioneDelloArray>
struct MyArrayCompare {
bool operator()(T a, T b) {
return (a[0] < b[0]);
}
};

dopo di che ho definito come variabile globale(quindi accessibile ad ogni procedura):
map<int*,DataMsg,MyArrayCompare <int*,3> > mp;

dove (int *) è la mia chiave formata appunto da quel famoso array di tre elementi e DataMsg è l'oggetto che devo memorizzare associato a quella chiave quindi come secondo elemento non ho più una stringa ma un'oggetto.

A questo punto ho definito una procedura che mi salva i valori nella struttura e che prende in input il valore di DataMsg che mi deve salvare, quindi "packdata" che vedi come parametro rappresenta proprio DataMsg

void saveMessage(*packdata){

int current[3] = {packdata->getCur(0),packdata->getCur(1),packdata->getCur(2)}

mp.insert(make_pair(current,packdata->getData()));

for( map<int*,SmallNetDataMsg,MyArrayCompare <int*,3>>::iterator ii=mp.begin(); ii!=mp.end(); ++ii){
cerr <<"Node datas into Hash : node "<<current[0] << "timestmp :" << (*ii).first[2] << " data :" << (*ii).second.getSd() << endl;
}
}

Effettivamente funziona nel senso che li salva all'interno della struttura, ma il problema ora è che l'ultimo elemento inserito in map viene inserito correttamente, quelli invece precedentemente salvati li vedo visualizzati con valori di chiave casuali.... ossia per esser più chiaro current[0],current[1],current[2] assumono deivalori strani tipo:"123452" ecc.. come se current una volta memorizzato cambiasse valore...Ma che significa?

shodan
10-12-2009, 13:03
Se la mappa è globale, anche gli array devono esserlo, altrimenti spariscono quando vanno fuori scope e i puntatori diventano non più validi.
Per evitare situazioni del genere devi usare un vector (semplificando anche il functor) :


template <typename T>
struct MyArrayCompare {
bool operator()(const T& a, const T& b) {
// massimo indice confrontabile, se serve
std::size_t maxComparingSize = std::min(a.size(),b.size());
// implementazione del contronto tra array
// ad esempio.
return (a[0] < b[0]);
}
};

map< vector<int>,string,MyArrayCompare <vector<int> > > mp;

int main() {

int val1[3] = {0,1,2};
int val2[3] = {1,2,3};

vector<int> v1(&val1[0],&val1[3]);
vector<int> v2(&val2[0],&val2[3]);

mp.insert(make_pair(v1,string("1")));
mp.insert(make_pair(v2,string("2")));
};


in alternativa, se il tuo compilatore supporta il nuovo std::tr1::array<>, puoi usare quello al posto del vector.



template <typename T>
struct MyArrayCompare {
bool operator()(const T& a, const T& b) {
// massimo indice confrontabile, se serve
std::size_t maxComparingSize = std::min(a.size(),b.size());
// implementazione del contronto tra array
// ad esempio.
return (a[0] < b[0]);
}
};

map< std::tr1::array<int,3>,string,MyArrayCompare <std::tr1::array<int,3> > > mp;

int main() {

std::tr1::array<int,3> val1 = {0,1,2};
std::tr1::array<int,3> val2 = {1,2,3};

mp.insert(make_pair(val1,string("1")));
mp.insert(make_pair(val2,string("2")));
};

beppe66
10-12-2009, 15:21
Ciao di nuovo,
allora ho messo come dicevi tu la dichiarazione del metodo:

template <typename T>
struct MyArrayCompare {
bool operator()(const T& a, const T& b) {
// massimo indice confrontabile, se serve
std::size_t maxComparingSize = std::min(a.size(),b.size());
// implementazione del contronto tra array
// ad esempio.
return (a[0] < b[0]);
}
};

ho dichiarato globale la struttura map e un'array di tre elementi:

map< vector<int>,DataMsg,MyArrayCompare <vector<int> > > mp;
int current[3];

all'interno della procedura SaveMessage ora ho scritto:

void saveMessage(*packdata){
//inserisco gli elementi all'interno dell'array
current[0]=packdata->getCurrent(0);
current[1]=packdata->getCurrent(1);
current[2]=packdata->getCurrent(2);

//creo il vector come appunto mi avevi detto
vector<int> v1(&current[0],&current[3]);
//stampo gli elementi
for( map<vector<int>,DataMsg,MyArrayCompare <vector<int>>>::iterator ii=mp.begin(); ii!=mp.end(); ++ii){
cerr <<"Node datas into Hash : node "<<(*ii).first[0]<< "timestmp :" << (*ii).first[2] << " data :" << (*ii).second.getSd() << endl;
}
}

Quello che accade è che la stampa fa vedere ogni volta solo un elemento all'interno della struttura...o meglio i problemi rispetto a prima non sono per niente risolti, ho voluto fare una prova allora e ho tolto la parte relativa al confronto all'interno della struttura map e le cose funzionano o meglio inserisce tutti gli elementi che gli dico...la stampa infondo alla procedura saveMessage mi fa vedere tutti gli elementi che intendevo memorizzare

praticamente ho dichiarato il map in questo modo:
map< vector<int>,DataMsg> mp;

Secondo te ho sbagliato in pieno?
In teoria la parte relativa al confronto di chiave in un'hash map non ci sarebbe più?
Detto proprio sinceramente non riesco a capire quel terzo campo che prende la definizione di map..
Grazie Giuseppe

shodan
10-12-2009, 19:21
Per cortesia, inserisci il codice tra i tag indicati al punto 6 del regolamento che trovi qui:
http://forum.html.it/forum/showthread.php?threadid=973887, altrimenti
si fa davvero molta fatica a leggere il codice se non si usano.

Poi ti invito a vedere il codice che ho postato.
Nel mio codice gli array sono locali alla funzione, per quello ho usato i vector. Se rendi anche gli array globali, il vector non serve più.

Se intendi continuare a usare i vector (cosa che consiglio caldamente), queste riche puoi modificarle così:


//inserisco gli elementi all'interno dell'array
Vector v1(3);
v1[0]=packdata->getCurrent(0);
v2[1]=packdata->getCurrent(1);
v2[2]=packdata->getCurrent(2);

Eliminando un passaggio.

Per quanto riguarda la mappa, la definizione completa è:


template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;

di cui in genere si usano Key e T. Compare invece, volendo in caso di necessità, la possiamo passare noi ed è quello che ho fatto.
Questo per due motivi:
1) un puntatore non ha un operatore di confronto.
2) essendo la chiave un puntatore ad un array, potrei fare alcune operazioni su questo array per stabilire quale sia il minore.
Ad esempio:


template <typename T>
struct MyArrayCompare {
bool operator()(const T& a, const T& b) {
int x = a[0] + b[1] / a[2]
int y = b[0]*a[1] -b[2] / a[2];
return (x < y);
}
};

invece di limitarmi a un confronto tra due elementi e basta come l'altra versione.
Qui l'algoritmo di confronto lo devi stabilire tu.
Se basta l'operatore < implementato nel vector, non passare nulla. Se vuoi qualcosa di più complesso, ci devi pensare tu.

beppe66
11-12-2009, 09:35
Veramente non so come ringraziarti sei stato veramente forte...
Voglo scusarmi per non aver taggato il codice, ma sono da poco iscritto a questo forum e diciamo che sono alle prime armi.. Non mancherò comunque in seguito.

Tornando a noi, penso di aver risolto il problema, sinceramente non ho capito perchè prima il salvataggio non funzionase, ma ho apportato qualche modifica:



template <typename T>
struct MyArrayCompare {
bool operator()(const T& a, const T& b) {
// massimo indice confrontabile, se serve
std::size_t maxComparingSize = std::min(a.size(),b.size());
// implementazione del contronto tra array
// ad esempio.
return (a[2] < b[2]);
}
};


ho usato la definizione di map che mi avevi dato


map< vector<int>,DataMsg,MyArrayCompare <vector<int> >> mp;


e infine ho modificato la funzione che salvava i dati, eliminando l'array, e facendo solo uso dei vector, in questo modo:



void saveMessage(*packdata){

vector <int>v1(3);
v1[0]=packdata->getCurrent(0);
v1[1]=packdata->getCurrent(1);
v1[2]=packdata->getCurrent(2);

mp.insert(make_pair(v1,packdata->getData()));



faccio notare che non c'è nè localmente alla funzione nè globalmente la definizione dell'array una definzione dell'array int current[3] che avevo messo nel precedente codice e che a questo punto, come mi hai fatto notare, posso mettere tutto direttamente nel vector.
Infine vorrei spendere due parole per quel terzo campo della definizione completa di map, ho letto un pò sul web e anche con qualche esempio, ho capito che quello che mi permette di effettuare è un'inserimento ordinato dei miei elementi all'interno della struttura, nel mio caso posso imporre un'ordinamento secondo il terzo elemento che compare nell'array che forma la chiave (a[2] < b[2]).
Grazie Giuseppe.

Loading