为boost哈希映射定义自定义哈希函数

时间:2014-03-02 14:20:33

标签: c++ boost

我是C ++的新手,我需要将一些用C编写的代码转换为C ++。问题是我发现很难在运行中理解C ++语法。我希望使用无序的boost哈希映射,我希望定义自己的哈希函数。我抬头看了怎么做,我遇到了这段代码:

struct ihash
    : std::unary_function<std::string, std::size_t>
{
    std::size_t operator()(std::string const& x) const
    {
         std::size_t seed = 0;
         std::locale locale;

         for(std::string::const_iterator it = x.begin();
            it != x.end(); ++it)
         {
            boost::hash_combine(seed, std::toupper(*it, locale));
         }

         return seed;
    }
};

Which you can then use in a case insensitive dictionary:

boost::unordered_map<std::string, int, ihash, iequal_to> idictionary;

我有以下哈希函数:

unsigned long hash (unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while(c = *str++)
    hash  = ((hash << 5) + hash) + c; /* hash + 33 + c */
    hash %= outer_relation->hash_table.bucket_count();

    return hash;
}

有人可以帮我转换。特别是什么是种子和组合负责?

2 个答案:

答案 0 :(得分:1)

第一个(ihash)是一个通用哈希,以函数对象的形式实现。这有几个好处:

  • 它是通用的,意思是,您可以在不知道/关心内部组织的情况下将其与不同容量/负载因子的哈希表一起使用
  • 因为它是一个可调用的对象,并且它是默认的可构造的,你实际上可以“只”实例化无序映射:

    boost::unordered_map<std::string, int, ihash, iequal_to> idictionary;
    
    然后

    将使用默认构造的ihash实例作为哈希函数(对于iequal_to也是如此)。

第二个看起来像哈希函数硬连线到哈希表实现中。显然,这个哈希表实现假定所有键必须是unsigned char*,并且它派生一个桶索引作为哈希实现的一部分。注意:

  • 您不能轻易地将其用于默认可构造的地图:

    boost::unordered_map<std::string, int, unsigned long(*)(unsigned char*), iequal_to> idictionary;
    

    因为这将导致哈希函数的nullptr实例。所以你最终总是将&hash传递给构造函数重载。对你的代码进行推理会更难,因为只要原型匹配就不可能传递错误的函数指针

  • 此函数假定键不能包含嵌入的NUL字符(不是std::string的限制

  • 此功能不是正确的(意味着您无法使其适用于mapunordered_map,除非您添加了常量,例如size_t (*)(unsigned char const*))。

除了这些观察之外,两个函数基本上完成相同的工作,其中ihash根据全局C ++语言环境(see docs)进行大小写折叠。

我不确定你被困在哪里,因为你的样本看起来应该相对完整?所以,这里有一个小小的自包含样本,以防它让你失败:

#include <boost/unordered_map.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <iostream>

namespace hash_examples
{
    struct iequal_to
        : std::binary_function<std::string, std::string, bool>
    {
        iequal_to() {}
        explicit iequal_to(std::locale const& l) : locale_(l) {}

        template <typename String1, typename String2>
        bool operator()(String1 const& x1, String2 const& x2) const
        {
            return boost::algorithm::iequals(x1, x2, locale_);
        }
    private:
        std::locale locale_;
    };

    struct ihash
        : std::unary_function<std::string, std::size_t>
    {
        ihash() {}
        explicit ihash(std::locale const& l) : locale_(l) {}

        template <typename String>
        std::size_t operator()(String const& x) const
        {
            std::size_t seed = 0;

            for(typename String::const_iterator it = x.begin();
                it != x.end(); ++it)
            {
                boost::hash_combine(seed, std::toupper(*it, locale_));
            }

            return seed;
        }
    private:
        std::locale locale_;
    };
}

int main()
{
    using namespace hash_examples;
    boost::unordered_map<std::string, int, ihash, iequal_to> map;
    map.emplace("one",   1);
    map.emplace("two",   2);
    map.emplace("three", 3);

    std::cout << map.at("TWO");
}

请注意,hash_examples直接来自http://www.boost.org/doc/libs/1_55_0/libs/unordered/examples/case_insensitive.hpp

查看 Live On Coliru

答案 1 :(得分:-1)

如果你想使用你自己的哈希,为什么不实现像这样的ihash?

struct ihash : std::unary_function<std::string, std::size_t> {
  std::size_t operator()(std::string const& value) const {
    std::size_t hash = 5381;
    const char* str = value.c_str();
    int c;
    while(c = *str++)
    hash  = ((hash << 5) + hash) + c; /* hash + 33 + c */
    hash %= outer_relation->hash_table.bucket_count();
    return hash;
  }
}