Używam mocno std::set<int>
i często po prostu muszę sprawdzić, czy taki zestaw zawiera liczbę, czy nie.
Uznałbym za naturalne napisanie:
if (myset.contains(number))
...
Ale z powodu braku contains
członka muszę napisać uciążliwe:
if (myset.find(number) != myset.end())
..
lub nie tak oczywiste:
if (myset.count(element) > 0)
..
Czy istnieje powód takiej decyzji projektowej?
count()
podejściem jest to, że wykonuje ono więcej pracy, niżcountains()
byłoby to konieczne.contains()
, która zwracabool
byłoby stracić cenne informacje o tym, gdzie element jest w zbiorze .find()
zachowuje i zwraca te informacje w postaci iteratora, dlatego jest lepszym wyborem dla biblioteki ogólnej, takiej jak STL. (To nie znaczy, żebool contains()
nie jest to zbyt miłe w posiadaniu ani nawet konieczne).contains(set, element)
darmową funkcję, korzystając z publicznego interfejsu zestawu. Dlatego interfejs zestawu jest funkcjonalnie kompletny; dodanie wygodnej metody po prostu zwiększa interfejs bez włączania żadnej dodatkowej funkcji, co nie jest sposobem C ++.Odpowiedzi:
Myślę, że to prawdopodobnie dlatego, że oni próbują zrobić
std::set
istd::multiset
jak najbardziej podobne. (I oczywiściecount
ma sensowne znaczeniestd::multiset
.)Osobiście uważam, że to był błąd.
Nie wygląda to tak źle, jeśli udajesz, że
count
to tylko błąd ortograficznycontains
i napiszesz test jako:Jednak nadal jest to wstyd.
źródło
.end()
).contains()
, ponieważ byłby on zbędny, ponieważ dla każdegostd::set<T> s
iT t
wyniks.contains(t)
jest dokładnie identyczny z wynikiemstatic_cast<bool>(s.count(t))
. Ponieważ użycie wartości w wyrażeniu warunkowym mogłoby niejawnie rzutować ją na tobool
, mogli czuć, żecount()
służyło to celowi wystarczająco dobrze.if (myset.ICanHaz(element)) ...
: DAby móc pisać
if (s.contains())
,contains()
musi zwrócićbool
(lub typ do konwersjibool
, co jest inną historią), jakbinary_search
robi.Podstawowym powodem za projekt decyzji nie zrobić to w ten sposób jest to,
contains()
który zwracabool
byłoby stracić cenne informacje o tym, gdzie element jest w zbiorze .find()
zachowuje i zwraca te informacje w postaci iteratora, dlatego jest lepszym wyborem dla biblioteki ogólnej, takiej jak STL. To zawsze była naczelna zasada Alexa Stiepanowa, jak często wyjaśniał (na przykład tutaj ).Jeśli chodzi o
count()
podejście ogólnie, chociaż często jest to dobre obejście, problem polega na tym, że wykonuje więcej pracy niżcontains()
musiałby zrobić .Nie oznacza to, że
bool contains()
nie jest to zbyt przyjemne, a nawet konieczne. Jakiś czas temu prowadziliśmy długą dyskusję na ten sam temat w grupie ISO C ++ Standard - Future Propozycje.źródło
contains()
oczywiście silna interakcja z istniejącymi kontenerami i algorytmami, na które duży wpływ miałyby koncepcje i zakresy - w czasie oczekiwanym w C ++ 17 - i Byłem przekonany (w wyniku dyskusji, a także kilku prywatnych wymian e-mailowych), że lepiej jest zaczekać na nie. Oczywiście w 2015 roku nie było jasne, że ani pojęcia, ani zakresy nie trafią do C ++ 17 (w rzeczywistości były duże nadzieje, że tak się stanie). Nie jestem jednak pewien, czy warto teraz to robić.std::set
(o co chodzi w pytaniu), nie widzę, jakcount
działa więcej, niżcontains
byłoby to konieczne. Implementacjacount
is (w przybliżeniu) w glibcreturn find(value) == end() ? 0 : 1;
. Poza szczegółami dotyczącymi operatora trójskładnikowego w porównaniu z samym zwracaniem!= end()
(co spodziewałbym się, że optymalizator usunie), nie widzę więcej pracy.myset.contains()
wywoła (jeśli istnieje), będzie to dość mocna wskazówka, że ta informacja nie jest wartościowa ( do użytkownika w tym kontekście).count()
więcej pracy, niżcontains()
musiałby zrobićstd::set
? Jest wyjątkowy, więccount()
może byćreturn contains(x) ? 1 : 0;
dokładnie taki sam.Brakuje go, ponieważ nikt go nie dodał. Nikt go nie dodał, ponieważ kontenery z STL, które
std
zawierała biblioteka, zostały zaprojektowane tak, aby były minimalne w interfejsie. (Zauważ, żestd::string
nie pochodzi z STL w ten sam sposób).Jeśli nie masz nic przeciwko dziwnej składni, możesz ją sfałszować:
posługiwać się:
Zasadniczo możesz pisać metody rozszerzające dla większości
std
typów C ++ przy użyciu tej techniki.O wiele bardziej sensowne jest po prostu zrobienie tego:
ale bawi mnie metoda metody rozszerzania.
Naprawdę smutne jest to, że pisanie efektywnego
contains
może być szybsze na jednym elemenciemultimap
lubmultiset
, ponieważ muszą tylko znaleźć jeden element, podczas gdycount
muszą znaleźć każdy z nich i policzyć .Multiset zawierający 1 miliard kopii 7 (wiesz, na wypadek, gdybyś się skończył) może mieć naprawdę wolny
.count(7)
, ale może mieć bardzo szybkicontains(7)
.Dzięki powyższej metodzie rozszerzenia możemy przyspieszyć to w tym przypadku, używając
lower_bound
, porównującend
, a następnie porównując z elementem. Zrobienie tego dla nieuporządkowanego miauczenia, jak również zamówionego miauczenia, wymagałoby jednak fantazyjnych SFINAE lub przeciążeń specyficznych dla kontenera.źródło
std::set
nie może zawierać duplikatów i dlategostd::set::count
zawsze wrócę0
lub1
.std::multiset::count
canbackticks
wokół słowa „zestaw” wynika z tego, że nie odnoszę się do niegostd::set
konkretnie. Żeby poczuć się lepiej, dodam multiPatrzysz na konkretny przypadek i nie widzisz większego obrazu. Jak stwierdzono w dokumentacji,
std::set
spełnia wymagania koncepcji AssociativeContainer . W przypadku tej koncepcji nie ma sensu miećcontains
metody, ponieważ jest ona prawie bezużyteczna dlastd::multiset
istd::multimap
, alecount
działa dobrze dla wszystkich. Choć metodacontains
może być dodany jako aliascount
dlastd::set
,std::map
i ich zakodowane wersje (jaklength
nasize()
wstd::string
), ale wygląda jak twórców bibliotek nie widzi realnej potrzeby.źródło
string
to potwór: istniał przed STL, gdzie istniałlength
i wszystkie te metody, które są oparte na indeksach, a następnie został „skonteneryzowany” w celu dopasowania do modelu STL ... bez usuwania istniejących metod ze względu na kompatybilność wsteczną . Zobacz GotW # 84: Monoliths Unstrung =>string
naprawdę narusza zasadę projektowania „minimalnej liczby funkcji składowych ”.contains
ma taki sam wysiłek na zestawie / mapie, ale można to zrobić szybciej niżcount
na multiset / multimap.contains
metody.size()
iempty()
są duplikatami, ale wiele kontenerów ma jedno i drugie.Chociaż nie wiem, dlaczego
std::set
nie ma,contains
alecount
które tylko zwraca,0
lub1
, możesz napisać funkcjęcontains
pomocniczą opartą na szablonach w następujący sposób:I użyj tego w ten sposób:
źródło
contains
to po prostu sprzeczne z faktem, że w rzeczywistości metoda istnieje, po prostu została nazwana w głupi sposób.std::string
kaszelstd.::string
NIE jest częścią STL! Jest częścią standardowej biblioteki i został utworzony z mocą wsteczną ...count
może działać wolniej, ponieważ efektywnie potrzebuje dwóch sekund,find
aby uzyskać początek i koniec zakresu, jeśli kod jest współdzielonymultiset
.contains
. Nie widzę w tym nic złego. @MarkRansom mała SFINAE ma zapobiec wiązaniu tego szablonu z rzeczami, których nie powinien.Prawdziwy powód
set
jest dla mnie tajemnicą, ale jednym z możliwych wyjaśnień tego samego projektu w programiemap
może być zapobieganie przypadkowemu pisaniu nieefektywnego kodu:Co spowodowałoby dwa
map
wyszukiwania.Zamiast tego jesteś zmuszony uzyskać iterator. To daje ci mentalną wskazówkę, że powinieneś ponownie użyć iteratora:
który zużywa tylko jedno
map
wyszukiwanie.Kiedy zdamy sobie z tego sprawę
set
imap
jesteśmy stworzeni z tego samego ciała, możemy zastosować tę zasadę również doset
. Oznacza to, że jeśli chcemy działać na elemencieset
tylko wtedy, gdy jest obecny wset
, ten projekt może uniemożliwić nam pisanie kodu w następujący sposób:Oczywiście wszystko to jest zwykłą spekulacją.
źródło
if (myMap.count("Meaning of universe"))
prostu dobrze pisać , więc ...?contains
za pomocą programucount
.Ponieważ c ++ 20,
bool contains( const Key& key ) const
jest dostępny.
źródło
A co z binary_search?
źródło
std::unordered_set
, ale nastd::set
to zadziała .zawiera () musi zwrócić wartość bool. Używając kompilatora C ++ 20, otrzymuję następujące dane wyjściowe dla kodu:
źródło
Innym powodem jest to, że dałoby to programiście fałszywe wrażenie, że std :: set jest zbiorem w sensie matematycznej teorii mnogości. Jeśli to zaimplementują, pojawi się wiele innych pytań: jeśli std :: set zawiera () dla wartości, dlaczego nie ma jej dla innego zestawu? Gdzie są union (), intersection () i inne operacje na zbiorach i predykaty?
Odpowiedź jest oczywiście taka, że niektóre operacje na zbiorach są już zaimplementowane jako funkcje w (std :: set_union () itp.), A inne są tak trywialnie zaimplementowane, jak include (). Funkcje i obiekty funkcji działają lepiej z abstrakcjami matematycznymi niż składowymi obiektów i nie są ograniczone do określonego typu kontenera.
Jeśli ktoś potrzebuje zaimplementować pełną funkcjonalność zestawu matematycznego, ma nie tylko wybór podstawowego kontenera, ale także ma wybór szczegółów implementacji, np. Czy jego funkcja teorii_union () działałaby z niezmiennymi obiektami, lepiej przystosowaną do programowania funkcjonalnego , czy też zmodyfikowałby swoje operandy i oszczędziłby pamięć? Czy byłby zaimplementowany jako obiekt funkcji od samego początku, czy też lepiej byłoby zaimplementować funkcję C i użyć std :: function <> w razie potrzeby?
Tak jak jest teraz, std :: set jest tylko kontenerem, dobrze przystosowanym do implementacji zbioru w sensie matematycznym, ale jest prawie tak daleki od bycia zbiorem teoretycznym jak std :: vector od bycia wektorem teoretycznym.
źródło