【问题标题】:How to compare all items in std::map?如何比较 std::map 中的所有项目?
【发布时间】:2020-03-30 14:40:36
【问题描述】:

我想将std::map 中的所有值相互比较。

我坚持:对于线性容器,比如向量,我会遍历索引i=1; v[i].isUniform(v[i-1])。但我不能用地图做到这一点。我期待听到聪明的想法。

这是我想要完成的一些伪代码:

class MyData
{
public:
    bool isUniform(const MyData& other) const
    {
        return this->speed == other.speed && this->ban == other.ban;
    }

private:
    bool ban;
    int  speed;
}

std::map<int, MyData> myMap;

bool allUniform = true;
for(item_1, item_2 : myMap) // how to implement this?
{
    if(!item_1.isUniform(item_2))
    {
        allUniform = false;
    }
}

最优雅(可读和高效)的方法是什么?

【问题讨论】:

  • isUniform 实际上是做什么的?可能有一种聪明的方法可以检查整个地图,而无需像您想的那样进行 O(n^2) 比较。
  • 比较所有值”是什么意思?检查所有值是否相同?成对比较每个值与其他值(N * N)?还是别的什么?
  • 你考虑过两个 for 循环吗? for(item_1 : myMap) {for(item_2 : myMap) {...}}
  • 添加了有关 isUniform() 的更多详细信息。它基本上只检查一些(不是所有)成员是否相等。
  • 如果您只想比较一些项目,请先将这些项目取出并放在单独的地图中。然后使用std::all_of 或该地图下方给出的任何优秀解决方案。

标签: c++ c++11 iterator stdmap c++98


【解决方案1】:

您可以将 std::all_of 与 lambda 一起使用。看起来像

bool allUniform = std::all_of(std::next(myMap.begin()), 
                              myMap.end(), 
                              [&myMap](const auto& pair)
                              { return myMap.begin()->second.isUniform(pair.second); });

这来自[1, N),并针对第一个元素调用isUniformall_of 也很短,所以一旦你有一个不一致的结果,它就会结束。

【讨论】:

    【解决方案2】:

    isUn​​iform() 是可传递的吗?也就是说,是否对任何 3 个 MyData 对象都遵循此规则:

     isUniform(A, B) && isUniform(B, C) == isUniform(A, C)
    

    如果是这样,那么您只需要 O(n) 比较。这是我的测试代码:

    #include <iostream>
    #include <map>
    
    using namespace std;
    
    class MyData {
    public:
        int value;
        MyData(int _value) : value(_value) {}
    
        bool isUniform(const MyData &obj) const { return value == obj.value; }
    
        static void checkMap(map<int, MyData> myMap) {
             bool allUniform = true;
             MyData * firstItem = nullptr;
    
             for (auto it = myMap.begin(); allUniform && it != myMap.end(); ++it) {
                 if (firstItem == nullptr) {
                      firstItem = &it->second;
                 }
                 else {
                      allUniform = firstItem->isUniform(it->second);
                 }
             }
    
             cout << "All Uniform: " << (allUniform ? "Yes" : "No") << endl;
        }
    };
    
    int main(int, char **) {
        map<int, MyData> map1;
        map<int, MyData> map2;
        MyData a1(1);
        MyData b1(1);
        MyData b2(2);
        MyData c1(1);
    
        // Should be uniform
        map1.insert(std::pair<int, MyData>(1, a1));
        map1.insert(std::pair<int, MyData>(2, b1));
        map1.insert(std::pair<int, MyData>(3, c1));
        MyData::checkMap(map1);
    
        // Should not be uniform
        map2.insert(std::pair<int, MyData>(1, a1));
        map2.insert(std::pair<int, MyData>(2, b2));
        map2.insert(std::pair<int, MyData>(3, c1));
        MyData::checkMap(map2);
    
        return 0;
    }
    

    有了这个输出:

    $ g++ -std=c++0x Foo.cpp -o Foo && Foo
    All Uniform: Yes
    All Uniform: No
    

    【讨论】:

    • 可以,或者直接使用std::all_of
    • isUniform 的传递性需求是一个非常好的观点。
    【解决方案3】:
    for(item_1, item_2 : myMap) // how to implement this?
    

    但是如果它们彼此相等(假设isUniform 是可传递且对称的),这与说它们都等于第一个元素是一样的。因此,您可以将每个元素与第一个元素进行比较。

    天真地,我们得到:

    bool allUniform(std::map<int, MyData> const &m)
    {
        if (m.empty()) return true; // or whatever makes sense for you
    
        auto first = m.begin();
        for (auto current = m.begin(); ++current != m.end(); )
        {
            if (!first->second.isUniform(current->second))
            {
                return false;
            }
        }
        return true;
    }
    

    如果您不介意将第一个元素与其自身进行比较,您可以简化这一点,因为实际上大部分复杂性只是避免这种情况。

    使用下面的std::adjacent_find 或在另一个答案中使用std::all_of 解决方案可能会更好。

    bool allUniform(std::map<int, MyData> const &m)
    {
        auto first_non_uniform =
            std::adjacent_find(
                m.begin(), m.end(),
                [](auto left, auto right)
                {
                    return !(left->second.isUniform(right->second));
                }
               );
    
        return first_non_uniform == m.end();
    }
    

    【讨论】:

      【解决方案4】:

      您可以使用标准算法std::adjacent_find

      例如

      #include <iostream>
      #include <iomanip>
      #include <map>
      #include <iterator>
      #include<algorithm>
      
      class MyData
      {
      public:
          MyData( bool ban, int speed ) : ban( ban ), speed( speed )
          {
          }
      
          bool isUniform(const MyData& other) const
          {
              return this->speed == other.speed && this->ban == other.ban;
          }
      
      private:
          bool ban;
          int  speed;
      };
      
      
      
      int main() 
      {
          std::map<int, MyData> myMap =
          {
              { 1, { true, 100 } }, { 2, { true, 100 } }, { 3, { true, 100 } }    
          };
      
          auto it = std::adjacent_find( std::begin( myMap ), std::end( myMap ),
                                        []( const auto &p1, const auto &p2 ) 
                                        {
                                          return not p1.second.isUniform( p2.second );
                                        } );
      
          bool allUniform = it == std::end( myMap );
      
          std::cout << std::boolalpha << allUniform << '\n';
      
          return 0;
      }
      

      程序输出是

      true
      

      【讨论】:

        【解决方案5】:

        将集合中的所有元素与第一个元素进行比较:

        template<class M, class F>
        bool allEqual( M const& m, F&& f ) {
          using std::begin; using std::end;
          auto b = begin(m);
          auto e = end(m);
          if (b == e) return true;
          for (auto it = std::next(b); it != e; ++it)
            if (!f( *b, *it))
              return false;
          return true;
        }
        

        成对比较所有元素:

        template<class M, class F>
        bool pairwiseCompare( M const& m, F&& f ) {
          for (auto&& a:m)
            for (auto&& b:m)
            {
              if (std::addressof(a) == std::addressof(b))
                continue;
              if (!f(a,b))
                return false;
            }
          return true;
        }
        

        这不适用于像 std::vector&lt;bool&gt; 这样的伪容器;好吧,它有点会,但它会浪费地自我比较元素。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-12
          • 2017-07-28
          • 2013-01-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多