我有一个名为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();
}
然而,地图似乎只有一把钥匙。如何使其按预期工作?
答案 0 :(得分:4)
您最好创建自己的比较功能。要回答您的问题,您的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。如果那是
case,当你使用<
运算符时使用哪种情况?为了这
原因,我在课堂上将它们设置为static const
变量。class
和struct
基本相同,但除外
默认保护级别(class
默认为私有
默认情况下,struct
是公开的)。将它作为一种惯例是惯例
struct是否要直接访问成员变量。虽然,
在这种情况下,我可能会将您的类设置为不可变。去做
那样,将m_t
和m_f
设置为私有变量并拥有一个getter
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
对于multiset变体:
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
。这可能对您想要询问的情况有利我的观点是否符合我对新观点的容忍度?&#34;