Menggunakan Tuple di unordered_map

Saya ingin menggunakan Tuple yang terdiri dari int,char,char di unordered_map saya. Saya melakukan seperti ini:

#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>

using namespace std;

tuple <int,char,char> kk;
unordered_map<kk,int> map;

int main()
{
    map[1,"c","b"]=23;
    return 0;
}

tapi ini memberi saya kesalahan berikut:

map.cpp:9:21: error: type/value mismatch at argument 1 in template parameter list     for ‘template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class    std::unordered_map’
map.cpp:9:21: error:   expected a type, got ‘kk’
map.cpp:9:21: error: template argument 3 is invalid
map.cpp:9:21: error: template argument 4 is invalid
map.cpp:9:21: error: template argument 5 is invalid
map.cpp:9:26: error: invalid type in declaration before ‘;’ token
map.cpp: In function ‘int main()’:
map.cpp:14:16: error: assignment of read-only location ‘"b"[map]’

Apa yang saya lakukan salah dalam hal ini?


person Xara    schedule 30.12.2013    source sumber


Jawaban (7)


Argumen template untuk unordered_map terlihat seperti ini:

template<

    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

std::hash tidak dikhususkan untuk tupel (gulir ke bawah ke Spesialisasi standar untuk jenis pustaka). Oleh karena itu Anda perlu menyediakan sendiri, seperti ini:

typedef std::tuple<int, char, char> key_t;

struct key_hash : public std::unary_function<key_t, std::size_t>
{
 std::size_t operator()(const key_t& k) const
 {
   return std::get<0>(k) ^ std::get<1>(k) ^ std::get<2>(k);
 }
};
// ..snip..
typedef std::unordered_map<const key_t,data,key_hash,key_equal> map_t;
//                                             ^ this is our custom hash

Dan terakhir, seperti yang sudah ditangani oleh jawaban Benjamin Lindley, Anda perlu menggunakan std::make_tuple:

// d is data
m[std::make_tuple(1, 'a', 'b')] = d;
auto itr = m.find(std::make_tuple(1, 'a', 'b'));

Kode diambil dari Menggunakan std::tuple sebagai kunci untuk std: :unordered_map dan inilah Contoh Langsung.

person Community    schedule 30.12.2013
comment
Bisakah Anda menjelaskan bagaimana Anda mendefinisikan key_hash? atau apa yang dilakukan di dalam key_hash? - person Xara; 01.01.2014
comment
@Zara Lihat tautan untuk informasi lebih lanjut. key_hash didefinisikan di atas. Nanti, kami menggunakannya sebagai salah satu argumen templat (di mana unordered_map memerlukan Hash). - person ; 01.01.2014
comment
Meng-XOR-kan bidang adalah fungsi hash yang buruk. Tolong jangan gunakan kode ini apa adanya. Lihat mis. open-std.org/jtc1/sc22/ wg21/docs/papers/2014/n3876.pdf - person jkff; 29.10.2016

Kesalahan pertama:

map.cpp:9:21: error:   expected a type, got ‘kk’

Seperti yang dikatakan dengan jelas dalam kesalahan, parameter templat harus berupa tipe. kk bukan tipe, itu adalah objek. Mungkin Anda bermaksud menjadikannya typedef?

typedef tuple <int,char,char> kk;
unordered_map<kk,int> map;

Kesalahan kedua:

map[1,"c","b"]=23;

Dua masalah di sini. Pertama, menempatkan koma di antara nilai tidak membuat nilai tersebut menjadi tupel. Anda harus menjelaskannya secara eksplisit, baik memanggil konstruktor tipe tupel Anda, atau menggunakan fungsi yang mengembalikan tupel (misalnya std::make_tuple). Kedua, tupel Anda mengharapkan karakter ('c','b'), bukan string ("c","b").

map[std::make_tuple(1,'c','b')] = 23;
person Benjamin Lindley    schedule 30.12.2013

Seperti yang ditunjukkan, std::hash tidak dikhususkan untuk tupel. Namun, jika tuple Anda terdiri dari tipe hashable standar seperti string dan int, kode berikut dari generic-hash-for-tuples-in-unordered-map-unordered-set akan secara otomatis menambahkan dukungan seperti itu di c++11.

Cukup tempelkan kode di file header dan sertakan kapan pun diperlukan:

#include <tuple>
// function has to live in the std namespace 
// so that it is picked up by argument-dependent name lookup (ADL).
namespace std{
    namespace
    {

        // Code from boost
        // Reciprocal of the golden ratio helps spread entropy
        //     and handles duplicates.
        // See Mike Seymour in magic-numbers-in-boosthash-combine:
        //     https://stackoverflow.com/questions/4948780

        template <class T>
        inline void hash_combine(std::size_t& seed, T const& v)
        {
            seed ^= hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        }

        // Recursive template code derived from Matthieu M.
        template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
        struct HashValueImpl
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            HashValueImpl<Tuple, Index-1>::apply(seed, tuple);
            hash_combine(seed, get<Index>(tuple));
          }
        };

        template <class Tuple>
        struct HashValueImpl<Tuple,0>
        {
          static void apply(size_t& seed, Tuple const& tuple)
          {
            hash_combine(seed, get<0>(tuple));
          }
        };
    }

    template <typename ... TT>
    struct hash<std::tuple<TT...>> 
    {
        size_t
        operator()(std::tuple<TT...> const& tt) const
        {                                              
            size_t seed = 0;                             
            HashValueImpl<std::tuple<TT...> >::apply(seed, tt);    
            return seed;                                 
        }                                              

    };
}
person Leo Goodstadt    schedule 29.01.2014

Saya memiliki persyaratan peta alih-alih peta tidak berurutan:
kuncinya adalah 3-tupel dan
nilainya adalah 4-tupel

melihat semua jawaban, saya hendak berganti berpasangan

tapi, di bawah ini berhasil untuk saya:

// declare a map called map1
map <
  tuple<short, short, short>,
  tuple<short, short, short, short>
> map1;

// insert an element into map1
map1[make_tuple(1, 1, 1)] = make_tuple(0, 0, 1, 1);

// this also worked
map1[{1, 1, 1}] = { 0, 0, 1, 1 };

Saya menggunakan ide komunitas visual studio 2015

person Manohar Reddy Poreddy    schedule 06.09.2016

Bagi mereka yang menggunakan boost mereka dapat merutekan ulang hashing untuk meningkatkan implementasi menggunakan ini

#include "boost/functional/hash.hpp"
#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>


using Key = std::tuple<int, char, char>;

struct KeyHash {
    std::size_t operator()(const Key & key) const
    {
        return boost::hash_value(key);
    }
};

using Map = std::unordered_map<Key, int, KeyHash>;

int main()
{
    Map map;
    map[1,"c","b"] = 23;
    return 0;
}
person Daniel    schedule 19.11.2019
comment
Terima kasih! Omong-omong, hash fungsional telah dipindahkan. Saya tidak yakin kapan, tetapi di boost 1.72 ada di #include <boost/container_hash/extensions.hpp> Saya tidak yakin mengapa fungsi boost hash untuk tuple tidak didokumentasikan di suatu tempat. Terima kasih untuk tip nya! - person Phil; 25.04.2020

Berikut adalah metode untuk menggunakan tuple sebagai kunci untuk unordered_map tanpa menggunakan spesialisasi hash:

#include <string>
#include <tuple>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <unordered_map>
using namespace std;

string fToStr(unordered_map<double,int>& dToI,float x)
{
   static int keyVal=0;
   stringstream ss;
   auto iter = dToI.find(x);
   if(iter == dToI.end()) {
      dToI[x]=++keyVal;
      ss << keyVal;
   } else {
      ss <<  iter->second;
   }
   return ss.str();
}

typedef tuple<int,char,char> TICC;
const char ReservedChar=',';
string getKey(TICC& t)
{
   stringstream ss;
   ss << get<0>(t) << ReservedChar << get<1>(t) << ReservedChar << get<2>(t);
   return ss.str();
}

int main()
{
   unordered_map< string,TICC > tupleMp;
   vector<TICC> ticc={make_tuple(1, 'a', 'b'),make_tuple(1, 'b', 'c'),make_tuple(2, 'a', 'b')};
   for(auto t : ticc)
      tupleMp[getKey(t)]=t;

   for(auto t : ticc) {
      string key = getKey(t);
      auto val = tupleMp[key];
      cout << "tupleMp[" << key << "]={" << get<0>(val) << "," << get<1>(val) <<  ","<< get<2>(val) << "} ";
   }
   cout << endl;

   //for float tuple elements use a second float to int key map 
   unordered_map< double,int > dToI;
   vector<float> v{1.234,1.234001,1.234001};
   cout << "\nfloat keys: ";
   for(float f : v)
      cout <<  setprecision(7) << f << "=" << fToStr(dToI,f) << " ";
   cout << endl;
   return 0;
}

Keluarannya adalah:

tupleMp[1,a,b]={1,a,b} tupleMp[1,b,c]={1,b,c} tupleMp[2,a,b]={2,a,b}

float keys: 1.234=1 1.234001=2 1.234001=2
person edW    schedule 12.09.2018

Setelah membaca beberapa lainnya postingan Saya berakhir dengan ini. Ia menggunakan algoritma kombinasi hash yang efisien dan tidak mengkhususkan hal-hal di namespace std. Anda harus melakukan lebih banyak pekerjaan jika ingin kode ini berfungsi untuk tupel elemen hashable apa pun secara umum.

Ini berfungsi di C++11 dan lebih tinggi. Di C++03 Anda dapat menggunakan boost::hash alih-alih std::hash.

typedef tuple<int, char, char> MyTuple;

// define a hash function for this tuple
struct KeyHash : public std::unary_function<MyTuple, std::size_t> {
    std::size_t operator()(const MyTuple& k) const {
        // the magic operation below makes collisions less likely than just the standard XOR
        std::size_t seed = std::hash<int>()(std::get<0>(k));
        seed ^= std::hash<char>()(std::get<1>(k)) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        return seed ^ (std::hash<char>()(std::get<2>(k)) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
    }
};

// define the comparison operator for this tuple
struct KeyEqual : public std::binary_function<MyTuple, MyTuple, bool> {
    bool operator()(const MyTuple& v0, const MyTuple& v1) const {
        return (std::get<0>(v0) == std::get<0>(v1) && std::get<1>(v0) == std::get<1>(v1) &&
                std::get<2>(v0) == std::get<2>(v1));
    }
};

typedef unordered_map<MyTuple, int, KeyHash, KeyEqual> MyMap;
person Kyle    schedule 17.07.2019