web-dev-qa-db-fra.com

Insertion dans un unordered_set avec fonction de hachage personnalisée

J'ai le code suivant pour faire un unordered_set<Interval>. Cela compile très bien.

struct Interval {
  unsigned int begin;
  unsigned int end;
  bool updated;   //true if concat.  initially false
  int patternIndex;  //pattern index. valid for single pattern
  int proteinIndex;   //protein index.  for retrieving the pattern
};

struct Hash {
  size_t operator()(const Interval &interval);
};

size_t Hash::operator()(const Interval &interval){
  string temp = to_string(interval.begin) + to_string(interval.end) + to_string(interval.proteinIndex);
  return hash<string>()(temp);
}

unordered_set<Interval, string, Hash> test;

Cependant, je ne peux pas compiler lorsque j'essaie d'insérer à l'aide de ce code:

for(list<Interval>::iterator i = concat.begin(); i != concat.end(); ++i){
  test.insert((*i));
}

De plus, je ne peux pas déterminer quel est le problème à partir des messages d'erreur, par exemple:

note: candidate is:
note: size_t Hash::operator()(const Interval&)
note:   candidate expects 1 argument, 2 provided  

Je pensais n'avoir fourni qu'un seul argument ...

Quel est le problème avec mon code d'insertion?


Voici le nouveau code d'instanciation: unordered_set<Interval, Hash> test; Cependant, je reçois toujours une multitude de messages d'erreur, par exemple:

note: candidate is:
note: size_t Hash::operator()(const Interval&) <near match>
note:   no known conversion for implicit ‘this’ parameter from ‘const Hash*’ to ‘Hash*’
23
user2052561

Premier problème:

Vous passez string comme deuxième argument de modèle pour votre instanciation de unordered_set<> modèle de classe. Le deuxième argument doit être le type de votre foncteur de hachage , et std::string n'est pas un objet appelable.

Peut-être destiné à écrire:

unordered_set<Interval, /* string */ Hash> test;
//                      ^^^^^^^^^^^^
//                      Why this?

De plus, je suggère d'utiliser des noms autres que begin et end pour vos variables (membres), car ce sont des noms d'algorithmes de la bibliothèque standard C++.

Deuxième problème:

Vous devez garder à l'esprit, que la fonction de hachage doit être qualifiée de const , donc votre foncteur doit être:

struct Hash {
   size_t operator() (const Interval &interval) const {
   //                                           ^^^^^
   //                                           Don't forget this!
     string temp = to_string(interval.b) + 
                   to_string(interval.e) + 
                   to_string(interval.proteinIndex);
     return (temp.length());
   }
};

Troisième problème:

Enfin, si vous voulez std::unordered_set pour pouvoir travailler avec des objets de type Interval, vous devez définir un opérateur d'égalité cohérent avec votre fonction de hachage. Par défaut, si vous ne spécifiez aucun argument de type comme troisième paramètre du std::unordered_set modèle de classe, operator == sera utilisé.

Vous n'avez actuellement aucune surcharge de operator == pour votre classe Interval, vous devez donc en fournir une. Par exemple:

inline bool operator == (Interval const& lhs, Interval const& rhs)
{
    return (lhs.b == rhs.b) && 
           (lhs.e == rhs.e) && 
           (lhs.proteinIndex == rhs.proteinIndex); 
}

Conclusion:

Après toutes les modifications ci-dessus, vous pouvez voir votre code se compiler dans ce exemple en direct .

37
Andy Prowl

Je pense, Andy Prowl parfaitement correction des problèmes avec votre code. Cependant, j'ajouterais la fonction membre suivante à votre Interval, qui décrit ce qui rend deux intervalles identiques:

std::string getID() const { return std::to_string(b) + " " + std::to_string(e) + " " + std::to_string(proteinIndex); }

Veuillez noter que j'ai également suivi la suggestion d'Andy Prowl et renommé les membres begin en b et end en e. Ensuite, vous pouvez facilement définir les fonctions de hachage et de comparaison en utilisant expressions lambda . Par conséquent, vous pouvez définir votre unordered_set comme suit:

auto hash = [](const Interval& i){ return std::hash<std::string>()(i.getID()); };
auto equal = [](const Interval& i1, const Interval& i2){ return i1.getID() == i2.getID(); };
std::unordered_set<Interval, decltype(hash), decltype(equal)> test(8, hash, equal);

Enfin, pour des raisons de lisibilité, j'ai converti votre boucle for en boucle for basée sur une plage:

std::list<Interval> concat {{1, 2, false, 3, 4}, {2, 3, false, 4, 5}, {1, 2, true, 7, 4}};

for (auto const &i : concat)
    test.insert(i);

for (auto const &i : test)
    std::cout << i.b << ", " << i.e << ", " << i.updated << std::endl;

Sortie (je viens d'imprimer les trois premiers membres de chaque Interval):

2, 3, 0
1, 2, 0

Comme vous pouvez le voir, seuls deux intervalles sont imprimés. Le troisième ({1, 2, true, 7, 4}) n'a pas été inséré dans concat, car ses b, e et proteinIndex sont égaux à ceux du premier intervalle ({1, 2, false, 3, 4}).

Code sur Ideone

0
honk