[英]How to create a map with custom class/comparator as key
我有一個名為ItemType
的類。 它有兩個成員-均為double,分別名為m_t
和m_f
。 類型的兩個項ItemType
被認為如果這兩個部件彼此各自公差水平內不同相等。 通過這種邏輯,也可以定義比較器功能。 但是,當我將這種類型的對象作為鍵插入到映射中時,即使應該至少存在三個這樣的鍵,映射中也只會生成一個鍵:
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
class ItemKey
{
public:
ItemKey(double t, double f)
{
m_t = t;
m_f = f;
}
double m_t;
double m_f;
double m_tEpsilon = 3;
double m_fEpsilon = 0.1;
bool operator<(const ItemKey& itemKey) const
{
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
return s_cmp < 0;
}
};
int main()
{
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range, m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys - (109.9, 9.0) -> count 2, (109.5, 10.0) -> count 3 and (119.9, 19.0) -> count 1
cout << itemMap.size();
}
但是,該地圖似乎只有1個鍵。 如何使它按預期工作?
創建自己的比較功能的過程做得不錯。 要回答您的問題,您的operator<()
函數中有一個錯誤,使得僅當m_f
超出公差且m_t
處於公差范圍內時才返回true,我猜這不是您想要的。 讓我們來看看。
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
上一行基本上是檢查this->m_f
和itemKey.m_f
是否在彼此的公差范圍內(意味着彼此相等)。 那可能就是原意。 那你說
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
如果s_cmp
是true
,那么它將值1
,這將有一個值0
為false
(這意味着它們不是彼此的公差范圍內)。 如果m_t
值在公差范圍內,則返回true。 到現在為止,你返回true
如果m_f
(根據公差),如果不等於m_t
是相等的(根據公差)。 然后是您的最后一行代碼
return s_cmp < 0;
始終將返回true
因為轉換為整數的布爾值永遠不能為負。
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
struct ItemKey
{
double m_t;
double m_f;
static constexpr double t_eps = 3;
static constexpr double f_eps = 0.1;
ItemKey(double t, double f) : m_t(t), m_f(f) {}
bool operator<(const ItemKey& other) const
{
// Here it is assumed that f_eps and t_eps are positive
// We also ignore overflow, underflow, and NaN
// This is written for readability, and assumed the compiler will be
// able to optimize it.
auto fuzzy_less_than = [] (double a, double b, double eps) {
return a < b - eps;
};
bool f_is_less_than = fuzzy_less_than(this->m_f, other.m_f, f_eps);
bool f_is_greater_than = fuzzy_less_than(other.m_f, this->m_f, f_eps);
bool f_is_equal = !f_is_less_than && !f_is_greater_than;
bool t_is_less_than = fuzzy_less_than(this->m_t, other.m_t, t_eps);
return f_is_less_than || (f_is_equal && t_is_less_than);
}
};
int main()
{
using namespace std;
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket
// -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range,
// m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any
// of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys
// - (109.9, 9.0) -> count 2
// - (109.5, 10.0) -> count 3
// - (119.9, 19.0) -> count 1
cout << itemMap.size();
cout << "itemMap contents:" << endl;
for (auto& item : itemMap) {
cout << " (" << item.first << ", " << ")" << endl;
}
return 0;
}
我在上面更改了幾件事。 我也有一些與編程錯誤無關的建議:
bool
類型是有原因的。 m_tEpsilon
和m_fEpsilon
可能不好成為該類的可更改變量。 實際上,如果一個對象的epsilon與另一對象的epsilon不同,那可能是不好的。 如果是這種情況,那么在執行<
運算符時會使用哪個? 因此,我在類中將它們設置為static const
變量。 class
和struct
除了默認的保護級別(在默認情況下, class
是私有的,而struct
在默認情況下是公共的)之外,基本上是相同的。 如果要直接訪問成員變量,通常將其作為結構。 盡管在這種情況下,我可能會將您的類設置為不可變的。 為此,請將m_t
和m_f
設置為私有變量,並使用吸氣劑m()
和f()
。 插入映射后,修改映射中的ItemKey
實例可能不是一個好主意。 您的方法存在的問題之一是,這將取決於添加元素的順序。 考慮添加以下幾對: (3.0, 10.0) (5.0, 10.0) (7.0, 10.0)
。 如果我們按此順序添加它們,則將得到(3.0, 10.0) (7.0, 10.0)
,因為(5.0, 10.0)
被認為等於(3.0, 10.0)
。 但是,如果我們先插入(5.0, 10.0)
,然后再插入另外兩個,該怎么辦? 那么,該列表將僅包含一個元素(5.0, 10.0)
,因為其他元素的麻煩將被視為與此元素相等。
相反,我建議您使用std::multiset
代替,當然這取決於您的應用程序。 考慮以下測試:
void simple_test_map() {
std::map<ItemKey, size_t> counter1;
counter1[{3.0, 10.0}] += 1;
counter1[{5.0, 10.0}] += 1;
counter1[{7.0, 10.0}] += 1;
for (auto &itempair : counter1) {
std::cout << "simple_test_map()::counter1: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
std::map<ItemKey, size_t> counter2;
counter2[{5.0, 10.0}] += 1;
counter2[{3.0, 10.0}] += 1;
counter2[{7.0, 10.0}] += 1;
for (auto &itempair : counter2) {
std::cout << "simple_test_map()::counter2: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
}
輸出:
simple_test_map()::counter1: (3, 10) - 2
simple_test_map()::counter1: (7, 10) - 1
simple_test_map()::counter2: (5, 10) - 3
對於多集變體:
void simple_test_multiset() {
std::multiset<ItemKey> counter1 {{3.0, 10.0}, {5.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter1: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::multiset<ItemKey> counter2 {{5.0, 10.0}, {3.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter2) {
std::cout << "simple_test_multiset()::counter2: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::cout << "simple_test_multiset()::counter2.size() = "
<< counter2.size() << std::endl;
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter2.count({"
<< item.m_t << ", "
<< item.m_f << "}) = "
<< counter1.count(item) << std::endl;
}
std::cout << std::endl;
}
這個輸出
simple_test_multiset()::counter1: (3, 10)
simple_test_multiset()::counter1: (5, 10)
simple_test_multiset()::counter1: (7, 10)
simple_test_multiset()::counter2: (5, 10)
simple_test_multiset()::counter2: (3, 10)
simple_test_multiset()::counter2: (7, 10)
simple_test_multiset()::counter2.count({3, 10}) = 2
simple_test_multiset()::counter2.count({5, 10}) = 3
simple_test_multiset()::counter2.count({7, 10}) = 2
simple_test_multiset()::counter2.size() = 3
請注意,這里的count()
返回多集內被視為等於傳入的ItemKey
的元素數。這對於以下情況可能是有利的:您想問“我有多少點在我的新點的公差范圍內? “
祝好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.