Как сравнить c ++ std :: map с пользовательской функцией сравнения для связанных данных? - PullRequest
2 голосов
/ 29 марта 2012
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.Однако в моем приложении я хочу рассматривать эти значения как равные из-за разницы между этими значениями в допустимых пределах.Так есть ли способ предоставить пользовательскую функцию сравнения для данных (не для ключа)?

Любая помощь приветствуется.

Отредактировано: извините за ошибку в коде.Я скопировал код, который пытался найти решение этой проблемы.Ключи должны быть равны для моего сценария.

Ответы [ 4 ]

2 голосов
/ 29 марта 2012

Если все, что вас волнует, это равенство для всего контейнера, я бы порекомендовал алгоритм ::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.

Если вы действительно хотите, чтобы значения данных сравнивались одинаково нечетким образом, я представляю вам немного хака (и кое-что, что также требуетпара возможностей 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, вы можете использовать именованный допуск в качестве параметра шаблона.

1 голос
/ 29 марта 2012

Вы можете использовать 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;
}
1 голос
/ 29 марта 2012

Вы сравниваете две совершенно разные карты с разными ключами и значениями внутри.Я почти уверен, что это не ваше намерение.

Вы можете создавать карты с пользовательскими операциями сравнения для ключа, чтобы ответить на то, что я думаю, что вы спрашиваете, но создавая одну с переменная терпимостьвероятно, не будет работать хорошо.STL предъявляет строгие требования о том, что означает «меньше и равно» и как они должны себя вести;если вы нарушите правила, ваша карта просто случайно выйдет из строя.Поэтому я бы не использовал карту для этого.И в вашем случае вас интересуют значения, а не ключи, и в этом случае STL просто не заботится (он вообще не смотрит на ваши значения).

0 голосов
/ 29 марта 2012

Когда вы сравниваете M1 с M2, вы сравниваете карты друг с другом, а не со значениями в картах. Если вы хотите сравнить удвоения, с каким-то толерантностью см. этот пост .

...