使用自定义哈希函数插入到一个无序集合中
Inserting into an unordered_set with custom hash function
我有以下代码来制作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
我以为我只提供了一个论点。。。
我的插入代码有什么问题?
这是新的实例化代码: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*’
第一个问题:
您将传递string
作为unordered_set<>
类模板实例化的第二个模板参数。第二个参数应该是hasher函子的类型,并且std::string
不是可调用对象。
也许是想写:
unordered_set<Interval, /* string */ Hash> test;
// ^^^^^^^^^^^^
// Why this?
此外,我建议您的(成员(变量使用begin
和end
以外的名称,因为这些名称是C++标准库的算法名称。
第二个问题:
您应该记住,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());
}
};
第三个问题:
最后,如果希望std::unordered_set
能够处理Interval
类型的对象,则需要定义一个与哈希函数一致的相等运算符。默认情况下,如果未将任何类型参数指定为std::unordered_set
类模板的第三个参数,则将使用operator ==
。
您的类Interval
当前没有任何operator ==
重载,因此您应该提供一个。例如:
inline bool operator == (Interval const& lhs, Interval const& rhs)
{
return (lhs.b == rhs.b) &&
(lhs.e == rhs.e) &&
(lhs.proteinIndex == rhs.proteinIndex);
}
结论:
经过以上所有修改后,您的代码变为:
#include <string>
#include <unordered_set>
#include <list>
using namespace std;
struct Interval {
unsigned int b;
unsigned int e;
bool updated; //true if concat. initially false
int patternIndex; //pattern index. valid for single pattern
int proteinIndex; //protein index. for retrieving the pattern
};
bool operator == (Interval const& lhs, Interval const& rhs)
{
return (lhs.b == rhs.b) && (lhs.e == rhs.e) && (lhs.proteinIndex == rhs.proteinIndex);
}
struct Hash {
size_t operator()(const Interval &interval) const {
string temp = to_string(interval.b) + to_string(interval.e) + to_string(interval.proteinIndex);
return (temp.length());
}
};
int main()
{
unordered_set<Interval, Hash> test;
list<Interval> concat;
for(list<Interval>::iterator i = concat.begin(); i != concat.end(); ++i){
test.insert(*i);
}
}
我认为,Andy Prowl完美地解决了代码中的问题。然而,我想在您的Interval
中添加以下成员函数,它描述了是什么使两个间隔相同:
std::string getID() const { return std::to_string(b) + " " + std::to_string(e) + " " + std::to_string(proteinIndex); }
请注意,我也听从了Andy Prowl的建议,将成员begin
重命名为b
,将end
重命名为e
。接下来,您可以通过使用lambda表达式轻松地定义散列函数和比较函数。因此,您可以将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
的前三个成员(:
2、3、0
1,2,0
如您所见,只打印了两个间隔。第三个({1, 2, true, 7, 4}
(没有插入到test
,因为它的b
、e
和proteinIndex
等于第一个间隔({1, 2, false, 3, 4}
(。
Ideone 上的代码
- 如何将一个单词拆分成字母,并将它们放入一个无序的列表/集合中
- 仅从无序集合中删除一个项目
- 将一个向量对放在一个无序映射与一个映射中
- 如何从一个无序集合中获取一个元素
- 以std::字符串作为关键字,按字典顺序对一个无序映射进行排序
- 使用shared_ptr<字符串>转换为一个无序集合<字符串>
- 如何使用一个整数数组作为一个无序映射的键
- C++将一个值存储在无序对中
- 对一个无序映射c++进行排序时数据丢失
- 如何在一个无序映射中存储2个以上的变量
- 声明一个无符号int和一个数组的无序映射(将具有不同的大小)
- 判断一个键是否在一个c++无序映射中最省时的方法是什么
- 插入到一个无序映射的无序映射中
- 将向量排序到一个无序映射c++11中
- 如何使用C++(g++编译器)初始化一个无序集
- 包含一个图形节点作为关键字的无序映射
- 如何在C++中迭代一个无序的集合
- 为什么我可以在一个文件中无序地初始化两个静态类变量,而不能初始化三个
- 填充一个无序的vector元素指针队列
- 使用自定义哈希函数插入到一个无序集合中