C++20 如何处理多层次unordered_map

C++20 how to deal with multi-leveled unordered_map

提问人:Ξένη Γήινος 提问时间:9/9/2023 最后编辑:Ξένη Γήινος 更新时间:9/10/2023 访问量:78

问:

我在 Python 中有一个复杂的多层字典,我想知道如何将这样的东西“翻译”成 C++。我只是C++的初学者,所以我使用 C++20 标准,我的 IDE 是 Visual Studio 2022,我想知道在 C++20 中是否有更简单的方法可以做到这一点。

在此示例中,第一级键是字符串,第一级值都是类似结构的字典。二级键都是字符串,但值可以是整数、列表和字典。如果第三级是列表,则它是整数列表,否则,如果最后一个结构是字典,则它是将整数映射到整数的字典。

Python 中的示例:

{
    "         ": {
        "over": False,
        "wins": 131184,
        "ties": 46080,
        "legal_moves": [
            0,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8
        ],
        "win_moves": {
            0: 14652,
            1: 14232,
            2: 14652,
            3: 14232,
            4: 15648,
            5: 14232,
            6: 14652,
            7: 14232,
            8: 14652
        },
        "tie_moves": {
            0: 5184,
            1: 5184,
            2: 5184,
            3: 5184,
            4: 4608,
            5: 5184,
            6: 5184,
            7: 5184,
            8: 5184
        },
        "high_stakes": {
            0: 9468,
            1: 9048,
            2: 9468,
            3: 9048,
            4: 11040,
            5: 9048,
            6: 9468,
            7: 9048,
            8: 9468
        }
    }
}

该示例显示了第一个玩家从井字游戏的起始状态获胜的所有方式,如果玩家 O 先移动,我已经计算了从起始开始可以到达的所有 5478 个状态,如果另一个先移动,我也计算了相同的状态,总共 8533 个状态。

数据显示游戏是否结束,玩家 O 可以从状态中获胜的所有方式,以及状态可能导致平局状态的所有方式,所有合法的举动,以及对于每一步,该举动可能导致胜利或平局的所有可能方式。 表示玩家可以获胜的所有可能方式减去游戏以平局结束的所有可能方式,负值设置为 0。"high_stake"

几天前我在 Python 中计算了它们,我没有使用 C++,因为我不知道如何在 C++ 中声明这个结构。

在 C++ 语法中,数据为:

{
    {"         ", {
        {"over", 0},
        {"wins", 131184},
        {"ties", 46080},
        {"legal_moves", {
            0,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8
        }},
        {"win_moves", {
            {0, 14652},
            {1, 14232},
            {2, 14652},
            {3, 14232},
            {4, 15648},
            {5, 14232},
            {6, 14652},
            {7, 14232},
            {8, 14652}
        }},
        {"tie_moves", {
            {0, 5184},
            {1, 5184},
            {2, 5184},
            {3, 5184},
            {4, 4608},
            {5, 5184},
            {6, 5184},
            {7, 5184},
            {8, 5184}
        }},
        {"high_stakes", {
            {0, 9468},
            {1, 9048},
            {2, 9468},
            {3, 9048},
            {4, 11040},
            {5, 9048},
            {6, 9468},
            {7, 9048},
            {8, 9468}
        }}
    }
    }
}

这个怪物的类型标识符是什么?知道我在同一本字典中还有其他 5477 个这样的结构。

我知道如何声明各个对象:

#include <unordered_map>
#include <string>


using std::unordered_map
using std::string

unordered_map<int, int> high_stakes = {
            {0, 9468},
            {1, 9048},
            {2, 9468},
            {3, 9048},
            {4, 11040},
            {5, 9048},
            {6, 9468},
            {7, 9048},
            {8, 9468}
        };


unordered_map<string, unordered_map<int, int>> d1 = {{"high_stakes", {
            {0, 9468},
            {1, 9048},
            {2, 9468},
            {3, 9048},
            {4, 11040},
            {5, 9048},
            {6, 9468},
            {7, 9048},
            {8, 9468}
        }}};

unordered_map<string, int[9]> d2 = {{"legal_moves", {
            0,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8
        }}};

如何声明这些对象?我将用 C++ 计算结果,并将对象序列化为文件。统计信息将计算一次,其他 C++ 程序将反序列化结果。

如何保证类型不会更改?我知道我不能使用 JSON 或任何人类可读的基于文本的序列化格式。我实际上使用 pickle 来存储数据,这就是我的 Python 程序实际加载的内容。为此,我应该使用 C++ 标准库提供的哪种二进制序列化格式?


我知道JSON不能有整数作为键,我需要整数键保持整数。另外,我已经对其进行了基准测试,JSON 序列化很慢。

C++ 嵌套 C++20 无序映射

评论

1赞 Jarod42 9/9/2023
JSON 库可能会有所帮助(作为解决方案或灵感)。
2赞 user17732522 9/9/2023
"我应该使用 C++ 标准库提供的哪种二进制序列化格式来实现此目的?:“没有。您必须自己想出一种格式并提供序列化/反序列化,或者使用已经实现该格式的第三方库。
1赞 user17732522 9/9/2023
"知道我不能使用 JSON 或任何人类可读的基于文本的序列化格式。这将是最简单的解决方案,并且存在许多 JSON 库。
1赞 Jarod42 9/9/2023
int[9]应该是 .std::array<int, 9>
2赞 Pepijn Kramer 9/9/2023
我认为唯一真正的解决方案是首先更多地研究C++及其标准库。了解它的优势(和劣势)。二进制序列化在 C++ 中并不容易,您最好使用现有的序列化库(例如 flatbuffers 或 protobuf)。看起来你可能会使用类似的东西std::map<std::string, std::variant<int,std::vector<int>>>

答:

1赞 wu1meng2 9/10/2023 #1

考虑使用 Boost PropertyTree 库。它有一个 JSON 解析器,用于将输入 JSON 文件转换为对象。您可以轻松添加和访问节点。boost::property_tree::ptree

如果您不想从 JSON 开始,可以手动将节点添加到 .从 Python 3.7 开始,保留了插入顺序,这在 C++ 中是理想的对应项。我认为键和值在检索时默认为 s,但您可以使用实用程序函数轻松转换类型。boost::property_tree::ptreedictBoost Propertystringget_value<T>()

无论如何,这里有一个示例 Live on Coliru,基于您的类似 JSON 的输入,并进行了一些修改,

#include <iostream>
#include <sstream>

#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

namespace pt = boost::property_tree;

int main() {
  std::string json_str = R"(
  {
      "info": {
          "over": false,
          "wins": 131184,
          "ties": 46080.5,
          "legal_moves": [
              0,
              1
          ],
          "win_moves": {
              "0": 14652,
              "1": 14232
          }
      }
  }
  )";

  // Parse the JSON string
  std::stringstream json_stream(json_str);
  pt::ptree root;
  pt::read_json({json_stream}, root);

  // Access value of a leave node
  {
    auto over = root.get_child("info").get_child("over").get_value<bool>();
    std::cout << "over = " << over << std::endl;
  }

  // boolean
  auto over = root.get_child("info.over").get_value<bool>();
  std::cout << "over = " << over << std::endl;

  // integer number
  auto wins = root.get_child("info.wins").get_value<int>();
  std::cout << "wins = " << wins << std::endl;

  // floating-point number
  auto ties = root.get_child("info.ties").get_value<double>();
  std::cout << "ties = " << ties << std::endl;

  // array of numbers
  std::cout << "legal_moves:\n";
  for (const auto &it : root.get_child("info.legal_moves")) {
    std::cout << it.second.get_value<int>() << std::endl;
  }

  // nested object
  std::cout << "win_moves:\n";
  for (const auto &it : root.get_child("info.win_moves")) {
    auto key = it.first;
    auto val = it.second.get_value<int>();
    std::cout << key << " : " << val << std::endl;
  }
}

请注意,它保留了值的原始顺序,而另一个流行的包 JsonCpp 将根据基础容器对值进行排序。boost::property_tree::read_json()std::map<>

我稍微修改了您原来的类似 JSON 的字符串:

  1. boost::property_tree::read_json()要求 JSON 键为 s。string
  2. 如果坚持使用作为密钥,请调用以获取子节点。" "get_child(" ").get_child("...")
  3. False => false允许 ;否则,您必须将其解析为 a,然后手动转换为。get_value<bool>()stringbool