【问题标题】:How to create a recursive nested hashmap in C++?如何在 C++ 中创建递归嵌套哈希图?
【发布时间】:2021-07-24 12:01:40
【问题描述】:
def recursiveDict():
   return collections.defaultdict(recursiveDict)

# I can create a dictionary like the following
dic = recursiveDict()
dic['a'] = 1
dic['a']['a'] = 1
dic['a']['a']['a'] = 1
dic['a']['a']['a']['a'] = 1
and so on....

在使用 Python 和其他动态语言工作了 4 年后,我重新开始使用 C++。我希望能够使用 unordered_map 或 map 类创建一个递归嵌套列表(就像上面 Python 代码中显示的那样)。

我找不到这样做的方法。

我想要实现的是键应该是char/string类型,但要插入的值应该是int类型。

请让我知道如何做到这一点。

我最接近的就是这个

struct CharMap {
    std::unordered_map<char,CharMap> map;
} root_map;

and use it like

root_map.map['a'].map['b'];

但是我应该重载什么才能获得上面的确切语法?

提前致谢。

【问题讨论】:

  • std::string 呢?
  • 标准 C++ 库中没有类似的东西。你必须自己实现这个容器。在 C++ 中当然可以做到这一点,但这需要对 C++ 容器、算法和运算符重载有透彻的理解和了解。这在 Stackoverflow 上一两段短文无法解释,这方面的知识只能通过from a good C++ textbook 学习。祝你好运!
  • @SamVarshavchik ,也许我问问题的方式有问题。我很清楚所有这些概念。但我不知道如何编码。我也会对原始问题进行更改。
  • @πάνταῥεῖ ,字符串类对我有什么帮助?
  • 你可能会从这个使用这种语法实现Json 解析器的库中找到一些灵感。 github.com/nlohmann/json 在此处查看示例:github.com/nlohmann/json#examples

标签: c++ recursion data-structures stl hashmap


【解决方案1】:

我希望能够创建一个递归嵌套列表(就像上面 Python 代码中显示的那样)

我怀疑你是否真的想这样做。当然,我们不知道您尝试将其用于什么的详细信息,但是:

  • 如果您希望关联结构以短字符串作为键,请考虑使用 std::string 键,例如std::unoredered_map&lt;std::string, int&gt;
  • 如果您希望您的关联结构仅由 K 个字符序列作为键 - 使用类似 struct key { char[K] data; } 作为键(并可能为该键类添加相关方法/转换运算符),或者可能使用 std::array&lt;char, K&gt;

我怀疑的是,您确实需要能够说“我正在部分应用密钥”,而且不能将一般哈希和部分密钥(例如字符串前缀)视为您的“子哈希映射”。


PS - 如果性能是一个问题,请记住 std::unordered_map is rather slow。而std::string 作为键也会减慢您的速度,因为它们通常将字符串数据存储在堆上,这意味着一堆解除/分配工作以及对堆上可能任意位置的访问。

【讨论】:

    【解决方案2】:

    由于您已阐明您熟悉运算符重载的基本概念,因此我将勾勒出一个基本蓝图,您可以按照它来实现此语法。

    您的容器将实现一个operator[] 重载,该重载返回一个辅助对象。假设你的容器叫做CharMap,说到底,你的容器存储了ints。

    class CharMap {
    
        struct key {
    
            key operator[](const std::string &);
    
            operator int();
    
            key &operator=(int n);
        };
    
    public:
    
        key operator[](const std::string &);
    
    };
    

    这将允许,例如:

    CharMap container;
    
    container["A"]["B"]["C"]=5;
    
    int n=container["D"]["E"]["F"};
    

    CharMap::operator[] 返回一个 key 辅助对象。该对象还实现了自己的operator[] 重载,该重载返回另一个key。最后,key 对象实现了 operator= 重载,因此您可以分配给它,或者 operator int() 重载以从容器返回值。

    很明显key 将在内部存储指向它来自的CharMap 容器的指针,以便它可以更新或从容器返回适当的值。 key 还以零碎的方式在内部跟踪所有用于创建它的键。 CharMap::key 在内部记录第一个字符串。然后,它的key::operator[] 返回另一个key,在内部记录原始字符串和另一个。

    一旦operator[]operator int() 被调用,它们将使用所有累积的字符串以您需要弄清楚的某种形式或方式来完成它们的工作。

    还有一些效率问题可能会或可能不会被解决。这种方法实现了您想要的语法,但根据您的情况,它可能需要一些微调来优化底层实现,以消除一堆内部复制和洗牌。

    此外,还需要一些额外的工作来实现正确的const-正确性。但所有这些都只是实现细节,这就是您如何实现这种语法来访问由多个字符串索引的容器。

    【讨论】:

    • 感谢@SamVarshavchik 提供了这个惊人的解决方案。我只有一个问题:关键运算符语句和仅运算符语句有什么区别。如果operator int(); 是为返回值,那你为什么不把它放到结构体之外呢?
    • 知道了。很抱歉上面的问题。 key 只是返回类型!
    • 是的,key 只是内部类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-08
    • 2016-09-21
    • 2014-12-06
    • 1970-01-01
    • 2015-01-02
    • 2022-06-21
    • 2021-02-03
    相关资源
    最近更新 更多