【问题标题】:C ++ std::map with custom comparer for keeping tournament of matchesC ++ std::map 带有自定义比较器,用于保持比赛的比赛
【发布时间】:2015-05-03 04:31:38
【问题描述】:

我想将锦标赛结果保存在某个容器中。对于每场比赛,我都需要存储球员的姓名和分数。例如:

map["player1:player2"] = {2,4};

我不仅想通过键 "player1:player2" 甚至通过逆键 "player2:player1" 从这个容器中检索,而且我想得到逆向结果。

我即将使用std::map 并围绕它制作一些智能包装器。也许有一些使用自定义比较器、自定义检索和保存函数的技巧。

std::map 是一个不错的选择还是其他更好的选择?

编辑:

我将这些 cmets 总结为如下所示的解决方案:

struct Match
{
    std::string player1;
    std::string player2;

    int pointsPlayer1;
    int pointsPlayer2;

    std::string getKey()
    {
        return player1 + ":" + player2;
    }

    Match reverse()
    {
        Match reversed;
        reversed.player1 = player2;
        reversed.player2 = player1;
        reversed.pointsPlayer1 = pointsPlayer2;
        reversed.pointsPlayer2 = pointsPlayer1;
        return reversed;
    }
};

class Tournament
{   
    std::map<std::string, Match> _games;
public: 
    void insert(Match match);
};

void Tournament::insert(Match match)
{
    _games.insert({ match.getKey(), match });
    Match reversed = match.reverse();
    _games.insert({ reversed.getKey(), reversed });
}

我选择更简单的方法,我不介意每个结果都存在两次,因为插入函数每次都会替换匹配的两者并且封装可以保证这一点(它不暴露指针,只是结构)。

【问题讨论】:

  • 另一个想法是使用std::map&lt;std::set&lt;std::string&gt;, ...&gt;,这样您的密钥将是set(("player1", "player2")),这将是等效的。您还可以创建自己的自定义类,该类实现了已设置语义的operator&lt;,并将其用作键
  • 如果比赛只涉及两名玩家,最好使用 std::pair 字符串而不是 std::set。
  • 谢谢。我会努力实现的。
  • @AnthonySottile std::set 打乱了球员的顺序,这在这种情况下很重要,因为他们与分数相关。

标签: c++ stdmap


【解决方案1】:

首先,使用std::map 是行不通的。原因很简单,你想在其中插入map["player1:player2"] = {2, 4};,但从那时起,当你要求它输入map["player2:player1"] 时,你需要它返回{4, 2}。因此,您不仅需要不同的键来引用相同的数据(std::map 可以为您提供自定义比较器),而且您还需要不同格式的相同数据,具体取决于键中的顺序,std::map做不到。

现在,如何解决这个问题?首先,考虑一下您需要的接口。目前,您有插入和查询锦标赛结果的功能。我的水晶球还告诉我你会想要遍历锦标赛中的所有结果,查询是否已经进行了一场比赛,或许还需要重置表格的内容。所以,先去写下这些函数的接口,并记录它们的行为,尤其是对于极端情况。

然后,考虑如何实现它。最直接的方法可能是使用map&lt;pair&lt;string,string&gt;, pair&lt;int,int&gt;&gt; 来存储分数。现在,在插入时,您可以冗余地存储结果(即存储“player1:player2”和“player2:player1”的分数),然后使用任一变体在检索时给出正确的结果。或者,规范化顺序(按字典顺序对玩家进行排序)并在检索时,可以选择在查找之前反转键的顺序以及之后的结果以获得正确的顺序。

注意事项:

  • 还有另一种方法:如果您绘制玩家 X 与玩家 Y 的得分图,您将获得相同的信息。对应的数据结构是map&lt;string, map&lt;string, int&gt;&gt;。要插入匹配结果,您只需执行res["player1"]["player2"] = 2;res["player2"]["player1"] = 4;。我不会那样做,除非作为上述接口背后的实现。
  • 我更喜欢一对而不是字符串“player1:player2”,即使我通常必须像字符串一样显示它。原因很简单,它不会将表示与数据混为一谈,从而为您提供更简洁的代码。出于同样的原因,我不会例如存储例如3% 作为字符串或整数值 3,而不是作为浮点值 0.03,因为它有助于更​​好地进行计算(撇开浮点不准确问题不谈)。

【讨论】:

  • 听起来不错。我喜欢这个想法对作为关键,我会改变它。我也可以尝试双字典(或者你怎么称呼它)。
  • 最好的方法可能是unordered_map: player => int 加上一个(可能是稀疏的)匹配结果矩阵。
【解决方案2】:

也许这会有所帮助:

#include <map>
#include <array>
#include <string>

class Player {
private:
    std::string m_strName;
    unsigned m_uScore;

public:
    Player() : m_uScore( 0 ) {}
    explicit Player( const std::string& strName, unsigned score = 0 ) :
        m_strName( strName ),
        m_uScore( score )
    {}

    const std::string& getName() const {
        return m_strName;
    }

    const unsigned getScore() const {
        return m_uScore;
    }

    void setName( const std::string& strName ) {
        m_strName = strName;
    }

    void setScore( const unsigned& uScore ) {
        m_uScore = uScore;
    }

    bool operator==( const Player& p ) const {
        if ( m_uScorescore == p.m_uScore && m_strName == p.strName ) {
           return true;
        }
        return false;
    }

    bool operator!=( const Player& p ) const {
        return !operator==( p );
    }

    bool operator<( const Player& p ) const {
         return m_uScore < p.m_uScore;
    }

    bool operator> ( const Player& p ) const {
         return m_uScore > p.m_uScore;
    }

    const Player& lessThan( const Player& p ) const {
          return m_uScore < p.m_uScore ? *this : p;
    }

    const Player& greaterThan( const Player& p ) const {
          return m_uScore > p.m_uScore ? *this : p;
    }
}; // Player

int main() {
    unsigned uMatch = 1;
    // Since you need to compare 2 for each match I did this but isn't necessary 
    // You can choose which ever method is necessary. But since this
    // Has only 2 of the same type of object then this made sense to me.
    // I Initialized the std::array with the first results it isn't needed 
    // but used to show how 2 players are grouped to one entity but yet
    // remaining individual objects.
    std::array<Player,2> aPlayerResults = { Player( "Player1", 20 ),
                                            Player( "Player2", 10 ) };

    // This map then would hold every game-round( match results )
    std::map<unsigned, std::array<Player,2>> mMatches;
    mMatches[uMatch] = aPlayerResults;

    // Now Just Update The Values
    uMatch = 2;
    aPlayerResults.at(0).setName( std::string( "Player3" ) );
    aPlayerResults.at(0).setScore( 19 );
    aPlayerResults.at(1).setName( std::string( "Player4" ) );
    aPlayerResults.at(1).setScore( 17 );

    // Add To Map
    mMatches[uMatch] = aPlayerResults;        


    return 0;
}

正如您所看到的,玩家是一个将玩家名称与分数相关联并保持数据私有的类对象,您可以使用辅助函数来设置和获取值,有一个默认构造函数来创建一个空的玩家对象来填写稍后,加上一个显式构造函数,该构造函数至少除了一个名称的 std 字符串,并且 score 是选项。如果 score 留空,那么你有一个在任何比赛之前就存在的球员,一旦比赛结束,你就可以使用 setScore 方法。有用于测试 Player1 == Player2 或 != 的运算符,以及为 if 语句返回 true 或 false 的 运算符。如果您想比较两个玩家并根据分数是 > 还是

我使用 std::array 是因为我们知道每场比赛只有 2 名玩家相互对抗,例如在国际象棋游戏中。现在,如果您需要更多玩家,您可以增加数组元素的数量,只要值很小,说最大小于 10 或 20,如果有更多玩家,那么您将需要切换 std::array到播放器对象的 std::vector 或播放器对象指针的 std::vector 取决于您的需要。该地图只是将无符号作为键或索引与玩家的 std::array 或 std::vector 相关联。地图中的索引使它变得很好,因为每场比赛或游戏都是独一无二的,法线贴图不允许重复键。同样使用无符号可以使地图的 [] 赋值符号更易于阅读。

当然,您始终可以创建一个函数,通过常量引用接收 2 个玩家或 2 个玩家指针,然后他们会为您将这些玩家设置到地图中。

您还可以使用一堆玩家对象预先填充您的地图,这些对象都具有唯一名称且没有得分,然后在每场比赛结束后您可以使用迭代器通过 for 循环来更新所有得分值,然后创建另一个功能是遍历整个地图,以第一或第二的最大值对每个匹配项进行排序等。这只是如何实现您所说的事情的指南。从来没有一个确切的“正确方法”,因为在编写程序时有很多方法可以达到相同的预期结果。

【讨论】:

    【解决方案3】:

    您可以为此工作编写一个简单的函数:

    string getResult(map<string, string> scores, string key) {
        vecor<string> splited = split(key, ":");
        if (scores.containsKey(key))
            return scores.get(key);
        else
            return scored.get(splited[1] + ":" + splited[0]);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多