• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Czegoś nie rozumiem? [std::unordered_map]

Object Storage Arubacloud
0 głosów
472 wizyt
pytanie zadane 18 września 2015 w C i C++ przez Radfler VIP (101,030 p.)
zamknięte 29 maja 2016 przez Radfler

Na wstępie dodam, że dopiero uczę się działania tego typu kontenerów. I przepraszam za taki chaotyczny opis, jestem trochę zmęczony :)

Otóż napisałem sobie taki programik:

#include <unordered_map>
#include <functional>
#include <algorithm>
#include <iostream>
#include <string>
#include <cctype>

struct ci_char_traits : public std::char_traits<char> {

  using std::char_traits<char>::char_type;

  static bool eq(char_type lhs, char_type rhs) noexcept {
    return std::tolower(lhs) == std::tolower(rhs);
  }

  static bool lt(char_type lhs, char_type rhs) noexcept {
    return std::tolower(lhs) < std::tolower(rhs);
  }

  static int compare(const char_type* lhs, const char_type* rhs, std::size_t size) {
    while(size--) {
      if(lt(*lhs, *rhs)) return -1;
      if(lt(*rhs, *lhs)) return 1;
      ++lhs;
      ++rhs;
    }
    return 0;
  }

  static const char_type* find(const char_type* str, std::size_t size, const char_type& target) {
    while(size--) {
      if(eq(*str, target)) return str;
      ++str;
    }
    return nullptr;
  }

};

// ci_string to typ stringu, w którym wielkość liter nie ma znaczenia, tzn.
// ci_string("ABC") == ci_string("abC")
typedef std::basic_string<char, ci_char_traits> ci_string;

struct ci_string_hash {

  typedef std::size_t result_type;
  typedef ci_string argument_type;

  result_type operator()(const argument_type& arg) const {
    return std::hash<std::string>()(std::string(arg.data(), arg.size()));
  }

};

inline std::ostream& operator<<(std::ostream& os, const ci_string& cs) {
  return os << std::string(cs.data(), cs.size());
}

int main() {

  std::unordered_map<ci_string, double, ci_string_hash> constants;

  constants["pi"] = 3.14159;

  // Wyświetlono 0, spodziewano się 3.14159
  std::cout << constants["PI"] << std::endl;

}

Jak widać w funkcji main umieściłem w kontenerze std::unordered_map wartość liczby PI z kluczem 'pi'. Typ będący kluczem to ci_string. Nie korzysta on ze standardowych cech (chyba mogę tak powiedzieć) std::char_traits tylko ze specjalnej struktury ci_char_traits. Jakie są skutki korzystania z tych cech? Otóż klasa ta ignoruje wielkość liter, tzn. znaki 'A' i 'a' traktuje tak samo. Zgodnie z tym, taki kod powinien pokazać na ekranie 'true':

std::cout << std::boolalpha << (ci_string("PI") == ci_string("pi")); // Jest 'true'? Jest.

Jednak mojemu kontenerowi chyba to nie pasuje. Skoro 'PI' i 'pi' to jest to samo, to dlaczego przy wyświetlaniu pokazuje mi 0 (tj. tworzy nowy element)? Przecież klucz jest identyczny!

Moje pytanie brzmi: dlaczego tak się dzieje?

komentarz zamknięcia: Rozwiązane.

1 odpowiedź

+1 głos
odpowiedź 18 września 2015 przez adrian17 Ekspert (344,860 p.)
edycja 18 września 2015 przez adrian17
 
Najlepsza

Na pierwszy rzut oka, to ci_string_hash wciąż generuje hash z oryginalnego stringa. Czyli i tak będziesz porównywał hash("pi") z hash("PI"), które będą różne.

Taka wersja działa poprawnie. Zauważ brak trzeciego argumentu szablonu dla unordered_map, jest niepotrzebny bo kompilator sam znajdzie specjalizację std::hash którą zdefiniowałem.

typedef std::basic_string<char, ci_char_traits> ci_string;

namespace std {
	template<>
	struct hash<ci_string> {
		typedef std::size_t result_type;
		typedef ci_string argument_type;

		result_type operator()(const argument_type& arg) const {
			ci_string other = arg;
			// zamiana na lowercase
			std:transform(other.begin(), other.end(), other.begin(), ::tolower);
			return std::hash<std::string>()(std::string(other.data(), other.size()));
		}
	};
}

int main() {
  std::unordered_map<ci_string, double> constants;
  constants["pi"] = 3.14159;
  std::cout << constants["PI"] << std::endl;
}

 

(zakładam że potrzebujesz ci_string z dodatkowych powodów i nie możesz się go pozbyć)

komentarz 18 września 2015 przez Radfler VIP (101,030 p.)

Faktycznie, kompletnie o tym zapomniałem (jak mówiłem, dopiero się uczę). A co do specjalizacji std::hash to po prostu nie chciało mi się w to bawić :) Co do funkcji zrobiłem, ją trochę inaczej, ale rezultat jest identyczny:

result_type operator()(const argument_type& arg) const {
    std::string other;
    std::for_each(arg.begin(), arg.end(), [&](char c){ other.push_back(std::tolower(c)); });
    return std::hash<std::string>()(other);
  }

 

komentarz 18 września 2015 przez adrian17 Ekspert (344,860 p.)

To też jest ok. Z wydajnościowego punktu widzenia pewnie najlepsze byłoby:

result_type operator()(const argument_type& arg) const {
	std::string other(arg.data(), arg.size());
	std::transform(other.begin(), other.end(), other.begin(), ::tolower);
	return std::hash<std::string>()(other);
}

 

Podobne pytania

0 głosów
1 odpowiedź 1,056 wizyt
pytanie zadane 3 września 2016 w C i C++ przez niezalogowany
+1 głos
1 odpowiedź 378 wizyt
pytanie zadane 21 maja 2015 w C i C++ przez Radfler VIP (101,030 p.)
0 głosów
1 odpowiedź 1,218 wizyt
pytanie zadane 16 czerwca 2017 w C i C++ przez excavelty Bywalec (2,480 p.)

92,568 zapytań

141,420 odpowiedzi

319,622 komentarzy

61,954 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...