簡體   English   中英

如何使用關聯數據的自定義比較功能比較c ++ std :: map?

[英]How to compare c++ std::map with custom comparison function for associated data?

std::map<String, double> m1,m2;
m1["A"] = 20;
m2["A"] = 20.01;

if (m1 == m2)
    cout << "True";
else
    cout << "False";

該示例代碼顯示False,因為20不等於20.1。 但是,在我的應用程序中,由於這些值之間的差異在允許的公差范圍內,因此我希望將這些值視為相等。 那么有什么方法可以為數據提供自定義比較功能(而不是Key)?

任何幫助表示贊賞。

編輯:對不起,代碼錯誤。 我復制了試圖找到該問題解決方案的代碼。 對於我的情況,密鑰必須相等。

如果您只關心整個容器是否相等,則建議使用::std::equal算法。 這是如何做:

const double tolerance = 0.01;
bool equal = m1.size() == m2.size() &&
             ::std::equal(m1.begin(), m1.end(), m2.begin(),
                          [tolerance](const decltype(m1)::value_type &a,
                                      const decltype(m2)::value_type &b) {
    return (a.first == b.first) &&
           (::std::abs(a.second - b.second) < tolerance);
});

如果您關心“小於”關系,則::std::lexicographical_compare是您想要的。 這需要C ++ 11 lambda功能才能起作用。

如果您真正想要的是以模糊方式比較相等的數據值,那么我會向您介紹一些hack(以及一些還需要幾個C ++ 11功能的東西) fuzzy-double.cpp 我禁用排序比較,因為這會誘使您將這些東西塞進訂購容器中,並且由於(2.0 == 2.1) && (2.1 == 2.2) ,但是(2.0 != 2.2) ,它們不適合此目的。

#include <cmath>
#include <iostream>

template <const double &tolerance>
class fuzzy_double {
 public:
   fuzzy_double(double x) : x_(x) {
      static_assert(tolerance >= 0, "tolerance must be >= 0");
   }

   operator double() const { return x_; }
   const fuzzy_double &operator =(double x) { x_ = x; }

   bool equals(const fuzzy_double &b) const {
      return ::std::abs(x_ - b.x_) <= tolerance;
   }
   // This cannot be used as the basis of a 'less than' comparison operator for
   // the purposes of other algorithms because it's possible for a transitive
   // equality relationship to exit that equates all fuzzy_double's to
   // eachother. There is no strict ordering that makes sense.
   bool fuzzy_less(const fuzzy_double &b) const {
      return (b.x_ - x_) > tolerance;
   }

 private:
   double x_;
};

template <const double &tolerance>
bool operator ==(const fuzzy_double<tolerance> &a,
                 const fuzzy_double<tolerance> &b)
{
   return a.equals(b);
}

template <const double &tolerance>
bool operator !=(const fuzzy_double<tolerance> &a,
                 const fuzzy_double<tolerance> &b)
{
   return !a.equals(b);
}

template <const double &tolerance>
bool operator <(const fuzzy_double<tolerance> &a,
                const fuzzy_double<tolerance> &b)
{
   // tolerance < 0 should be an impossible condition and always be false, but
   // it's dependent on the template parameter and so only evaluated when the
   // template is instantiated.
   static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
   return false;
}

template <const double &tolerance>
bool operator >=(const fuzzy_double<tolerance> &a,
                const fuzzy_double<tolerance> &b)
{
   // tolerance < 0 should be an impossible condition and always be false, but
   // it's dependent on the template parameter and so only evaluated when the
   // template is instantiated.
   static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
   return false;
}

template <const double &tolerance>
bool operator >(const fuzzy_double<tolerance> &a,
                const fuzzy_double<tolerance> &b)
{
   // tolerance < 0 should be an impossible condition and always be false, but
   // it's dependent on the template parameter and so only evaluated when the
   // template is instantiated.
   static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
   return false;
}

template <const double &tolerance>
bool operator <=(const fuzzy_double<tolerance> &a,
                 const fuzzy_double<tolerance> &b)
{
   // tolerance < 0 should be an impossible condition and always be false, but
   // it's dependent on the template parameter and so only evaluated when the
   // template is instantiated.
   static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
   return false;
}

extern constexpr double ten_e_minus_2 = 0.01;

int main()
{
   fuzzy_double<ten_e_minus_2> a(3);
   fuzzy_double<ten_e_minus_2> b(3.009);
   fuzzy_double<ten_e_minus_2> c(2.991);
   fuzzy_double<ten_e_minus_2> d(3.011);
   fuzzy_double<ten_e_minus_2> e(2.989);

   using ::std::cout;

   cout << "a == a: " << (a == a) << '\n';
   cout << "a == b: " << (a == b) << '\n';
   cout << "a == c: " << (a == c) << '\n';
   cout << "a == d: " << (a == d) << '\n';
   cout << "a == e: " << (a == e) << '\n';
   return 0;
}

C ++ 11不允許將double甚至const用作模板參數。 OTOH確實允許具有外部鏈接的對象的指針和引用作為模板參數。 因此,如果將您的公差聲明為extern constexpr double ,則可以使用命名的公差作為模板參數。

您正在比較兩個完全獨立的映射,其中包含不同的鍵和值。 我很確定這不是您的意圖。

您可以使用鍵的自定義比較操作來制作地圖,以回答我認為的要求,但是制作具有可變公差的地圖可能效果不佳。 STL對小於和等於的含義以及它們的行為方式有嚴格的要求。 如果您違反規則,則地圖將隨機失敗。 因此,我不會為此使用地圖。 在您的情況下,您對值感興趣,而不對鍵感興趣,在這種情況下,STL根本不在乎(它根本不查看您的值)。

您可以將lexicographical_compare與成對的自定義比較器一起使用,該比較器將忽略值中的細微差異,如下所示:

bool mycomp (pair<string,double> lhs, pair<string,double> rhs) {
    if (lhs.first < rhs.first) {
        return true;
    }
    if (lhs.first > rhs.first) {
        return false;
    }
    // Here is the "check tolerance" part:
    if (abs(lhs.second-rhs.second) < 0.05) {
        return false;
    }
    return lhs.second < rhs.second;
}

int main() {
    std::map<string, double> m1,m2;
    m1["A"] = 20;
    m2["A"] = 20.01;
    bool res = lexicographical_compare(
        m1.begin(), m1.end(),
        m2.begin(), m2.end(),
        mycomp
    );
    cerr << res << endl;
    return 0;
}

當您將M1與M2比較時,您是在將地圖相互比較,而不是地圖中的值。 如果您想比較雙打,請參考這篇文章

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM