web-dev-qa-db-ja.com

カスタムハッシュ関数を使用してunordered_setに挿入する

unordered_set<Interval>を作成する次のコードがあります。これは正常にコンパイルされます。

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;

ただし、次のコードを使用して挿入しようとすると、コンパイルできません。

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

さらに、エラーメッセージから問題の原因を特定できません。たとえば、次のようになります。

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

引数を1つだけ指定したと思いました...

挿入コードの問題は何ですか?


新しいインスタンス化コードは次のとおりです:unordered_set<Interval, Hash> test;ただし、次のようなエラーメッセージは引き続き表示されます。

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

最初の問題:

stringを、unordered_set<>クラステンプレートのインスタンス化の2番目のテンプレート引数として渡します。 2番目の引数は、ハッシュ関数のタイプである必要があります 、およびstd::stringは呼び出し可能なオブジェクトではありません。

おそらく書くつもりです:

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

また、(メンバー)変数にはbeginおよびend以外の名前を使用することをお勧めします。これらはC++標準ライブラリのアルゴリズムの名前です。

2番目の問題:

hasher関数はconst として修飾する必要があることを覚えておく必要があるため、ファンクターは次のようになります。

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());
   }
};

3番目の問題:

最後に、std::unordered_setIntervalタイプのオブジェクトを処理できるようにするには、ハッシュ関数と一致する等価演算子を定義する必要があります。デフォルトでは、std::unordered_setクラステンプレートの3番目のパラメーターとして型引数を指定しない場合、operator ==が使用されます。

現在、クラスIntervaloperator ==のオーバーロードがないため、オーバーロードを提供する必要があります。例えば:

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

結論:

上記のすべての変更後、この ライブ例 でコードがコンパイルされるのを確認できます。

37
Andy Prowl

Andy Prowlは完全に 問題を修正 とコードで考えています。ただし、次のメンバー関数をIntervalに追加すると、2つの間隔が同じになる理由が記述されます。

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

Andy Prowlの提案にも従い、メンバーの名前をbeginからbに、endeに変更したことに注意してください。次に、ハッシュ関数と比較関数を ラムダ式 を使用して簡単に定義できます。その結果、unordered_set 次のように:

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);

最後に、読みやすさの理由から、forループを範囲ベースのforループに変換しました。

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;

出力(各Intervalの最初の3つのメンバーを出力しました):

2、3、0
1、2、0

ご覧のとおり、印刷される間隔は2つだけです。 3番目({1, 2, true, 7, 4})はconcatに挿入されませんでした。そのbe、およびproteinIndexは最初の間隔({1, 2, false, 3, 4})。

Ideonのコード

0
honk