Jedynym sposobem na uproszczenie byłoby predykat boolowski: szablon <typ nazwy T> element bool (T const i element). I to byłoby zaimplementowane (pod przykryciem) w odniesieniu do linii, o którą pytasz.
Don Wakefield,
Odpowiedzi:
399
Typowym sposobem sprawdzenia istnienia w wielu pojemników STL, takich jak std::map, std::set... jest:
jest to specyficzne dla zestawów i map. wektory, listy itp. nie mają funkcji znajdowania członka.
wilhelmtell,
8
IMO korzystające z count () jest lepsze, ponieważ jest po prostu krótsze i konwertuje na bool, jak zauważono w odpowiedzi Pietera. Nie rozumiem, dlaczego ta odpowiedź została zaakceptowana i tyle punktów ...
Огњен Шобајић
4
Dla kompletności wywodu: wektorach / listy można użyć std :: znaleźć: std::find(container.begin(), container.end(), element) != container.end(); Problem O (N) pozostaje, oczywiście ...
Aconcagua,
10
@MichaelMathews W Twoim wariancie: if(container.find(foo) == container.end())musisz najpierw wyszukać drzewo, aby najpierw znaleźć element - jeśli nie zostanie znaleziony, musisz zrobić drugie drzewo, aby znaleźć poprawną lokalizację wstawiania. Oryginalny wariant if(container.insert(foo).second) {...}ma urok, że potrzebuje tylko jednego wyszukiwania drzewa ...
Aconcagua,
23
istnieje standard set.contains(x)zwracający wartość bool w standardzie C ++ 20. Nie wiem, dlaczego zajęło nam to do 2020 roku.
Gremwell
215
Innym sposobem na stwierdzenie, czy element istnieje, jest sprawdzenie count()
if(myset.count(x)){// x is in the set, count is 1}else{// count zero, i.e. x not in the set}
Najczęściej jednak potrzebuję dostępu do elementu wszędzie tam, gdzie sprawdzam jego istnienie.
W każdym razie musiałbym znaleźć iterator. Wtedy oczywiście lepiej po prostu to porównać end.
set< X >::iterator it = myset.find(x);if(it != myset.end()){// do something with *it}
Pamiętaj, że używanie count()zamiast find()nigdy nie jest lepsze, ale potencjalnie gorsze. To dlatego, find()że powróci po pierwszym meczu, count()zawsze będzie iterować po wszystkich elementach.
Frerich Raabe
34
@Frerich, który dotyczy tylko multiseti multimappomyślałem? Wciąż warto jednak zwrócić uwagę :)
Pieter,
83
std :: set jest zwykle implementowany z uporządkowaną strukturą drzewa, więc count () i find () będą miały O (logn). Ani też nie będzie iterował wszystkich elementów zestawu.
Alan
14
@FrerichRaabe - Czy jesteś pewien? Ponieważ możliwe setjest zawarcie tylko jednego pasującego elementu, czy funkcja nie zostałaby zaimplementowana w taki sposób, aby zatrzymać po zlokalizowaniu pierwszego elementu, w tym przypadku, jak zauważa Pieter? Przydatna odpowiedź w każdym razie!
Dan Nissenbaum
14
@ DanNissenbaum Tak, masz dokładnie rację (podobnie jak + Peter i + Alan): dla std :: set te dwie funkcje są równoważne pod względem wydajności. Tak więc, mimo że pierwsza część mojego komentarza ( count()nigdy nie jest szybsza niż find()) nadal obowiązuje, druga część rzeczywiście nie ma zastosowania std::set. Wydaje mi się jednak, że można argumentować na korzyść find(): jest bardziej wyrazisty, tzn. Podkreśla, że próbujesz znaleźć element zamiast liczyć liczbę wystąpień.
Frerich Raabe
42
Aby wyjaśnić, powodem, dla którego nie ma członka takiego jak contains()w tych typach kontenerów, jest to, że otworzyłoby cię to do pisania nieefektywnego kodu. Taka metoda prawdopodobnie po prostu wykona this->find(key) != this->end()wewnętrznie, ale zastanów się, co robisz, gdy klucz jest rzeczywiście obecny; w większości przypadków będziesz chciał zdobyć element i coś z nim zrobić. Oznacza to, że musisz zrobić sekundę find(), co jest nieefektywne. Lepiej jest użyć funkcji znajdź bezpośrednio, aby można było buforować swój wynik w następujący sposób:
auto it = myContainer.find(key);if(it != myContainer.end()){// Do something with it, no more lookup needed.}else{// Key was not present.}
Oczywiście, jeśli nie zależy Ci na wydajności, zawsze możesz rzucić własną, ale w takim przypadku prawdopodobnie nie powinieneś używać C ++ ...;)
Co z zestawami? Zwykle masz już element, ale po prostu chcesz sprawdzić, czy jest w
środku
8
Czy masz jakieś odniesienia do tego, czy jest to rzeczywisty powód, dla którego taka metoda / funkcja nie jest uwzględniona w STL, czy jest to tylko twoje wykształcone przypuszczenie?
Fabio A.
3
@FabioA. To moje wykształcone przypuszczenie.
Tim
1
@Adhemar, spójność nie jest dokładnie mocną stroną STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich
3
Nie ma dla mnie sensu nie włączać funkcji, ponieważ ktoś mógłby użyć jej niepoprawnie, gdyby nie wiedział, co robi. Programowanie jest przeznaczone dla osób, które mogą myśleć same za siebie i są odpowiedzialne za swój kod i jego działanie
slawekwin
13
W C ++ 20 w końcu otrzymamy std::set::containsmetodę.
#include<iostream>#include<string>#include<set>int main(){
std::set<std::string> example ={"Do","not","panic","!!!"};if(example.contains("panic")){
std::cout <<"Found\n";}else{
std::cout <<"Not found\n";}}
Jeśli chcesz dodać containsfunkcję, może to wyglądać następująco:
#include<algorithm>#include<iterator>template<classTInputIterator,class T>inlinebool contains(TInputIterator first,TInputIteratorlast,const T&value){return std::find(first,last,value)!=last;}template<classTContainer,class T>inlinebool contains(constTContainer& container,const T&value){// This works with more containers but requires std::begin and std::end// from C++0x, which you can get either:// 1. By using a C++0x compiler or// 2. Including the utility functions below.return contains(std::begin(container), std::end(container),value);// This works pre-C++0x (and without the utility functions below, but doesn't// work for fixed-length arrays.//return contains(container.begin(), container.end(), value);}template<class T>inlinebool contains(const std::set<T>& container,const T&value){return container.find(value)!= container.end();}
Działa to z std::setinnymi kontenerami STL, a nawet tablicami o stałej długości:
Jak wskazano w komentarzach, przypadkowo użyłem nowej funkcji w C ++ 0x ( std::begini std::end). Oto prawie trywialna implementacja VS2010:
namespace std {template<class_Container>inlinetypename_Container::iterator begin(_Container&_Cont){// get beginning of sequencereturn(_Cont.begin());}template<class_Container>inlinetypename_Container::const_iterator begin(const_Container&_Cont){// get beginning of sequencereturn(_Cont.begin());}template<class_Container>inlinetypename_Container::iterator end(_Container&_Cont){// get end of sequencereturn(_Cont.end());}template<class_Container>inlinetypename_Container::const_iterator end(const_Container&_Cont){// get end of sequencereturn(_Cont.end());}template<class_Ty,size_t_Size>inline_Ty*begin(_Ty(&_Array)[_Size]){// get beginning of arrayreturn(&_Array[0]);}template<class_Ty,size_t_Size>inline_Ty*end(_Ty(&_Array)[_Size]){// get end of arrayreturn(&_Array[0]+_Size);}}
@Adhemar, to faktycznie było nieefektywne, ale wcale nie z powodu, o którym wspomniałeś.
Sam Harwell,
@Paul: Upewnij się, że podałeś specjalizację std::seti pamiętaj, że jest ona odpowiednia tylko wtedy, gdy jedyne , co musisz wiedzieć, to istnienie.
Sam Harwell,
@ 280Z28: std :: begin (container)? Co to jest standard STL? Nie kompiluje się na moim gcc.
stefaanv
@stefannv: heh, jest nowy dla C ++ 0x. Dodałem implementację z mojego kompilatora powyżej.
Sam Harwell,
2
@Adhemar: Jeśli wiesz, że zestaw zawiera wartość, to już jesteś tą wartością. Jedynym powodem, dla którego potrzebujesz iteratora, jest usunięcie elementu ze zbioru. Jeśli wszystko, czego potrzebujesz, to wiedzieć, czy dana kolekcja zawiera wartość, to rozwiązanie jest nie mniej wydajne niż jakiekolwiek inne rozwiązanie.
Sam Harwell,
4
Możesz także sprawdzić, czy element jest w zestawie, czy nie podczas wstawiania elementu. Wersja z jednym elementem zwraca parę, a jej para elementów :: najpierw ustawiona na iterator wskazujący albo na nowo wstawiony element, albo na równoważny element już w zestawie. Para :: drugi element w tej parze jest ustawiony na true, jeśli wstawiono nowy element, lub false, jeśli element równoważny już istniał.
Na przykład: Załóżmy, że zestaw ma już 20 jako element.
std::set<int> myset;
std::set<int>::iterator it;
std::pair<std::set<int>::iterator,bool> ret;
ret=myset.insert(20);if(ret.second==false){//do nothing}else{//do something}
it=ret.first //points to element 20 already in set.
Jeśli element jest nowo wstawiony niż para :: najpierw wskaże pozycję nowego elementu w zestawie.
właśnie to zrobiłem: szablon <klasa T> statyczny wbudowany bool zawiera (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton,
4
@paul nie tworzy statycznych funkcji globalnych. zamiast tego umieść swoją funkcję w anonimowej przestrzeni nazw: to sposób tworzenia funkcji, które nie będą łączyły się z innymi jednostkami kompilacji. również twój parametr T powinien być stałym odniesieniem, dla stałej poprawności i wydajności.
wilhelmtell
-1: Nie matrycy, a nie w ogóle w stylu STL. Jest to w porządku, jeśli nie używasz STL, ale jeśli używasz STL, powinieneś przynajmniej spróbować przestrzegać jego standardów.
Sam Harwell,
1
@ 280Z28: Przykro mi, że mój kod nie odpowiada twoim standardom, po prostu pokazałem, że jeśli nie podoba ci się interfejs STL, możesz napisać własny. Jezu, nie na szablonie? Jak musi być szablonem? Twój przykład wygląda dobrze, to nie znaczy, że mój jest zły. Jest po prostu bardziej skoncentrowany na planie, o co poprosił PO.
stefaanv
1
@ 280Z28: Właśnie miałem rację. Myślałem, że ludzie będą wystarczająco inteligentni, aby uzyskać zdjęcie.
stefaanv
2
używam
if(!my_set.count(that_element))//Element is present...;
Ale to nie jest tak wydajne jak
if(my_set.find(that_element)!=my_set.end())....;
Moja wersja oszczędza tylko mój czas na pisanie kodu. Wolę to w ten sposób do konkurencyjnego kodowania.
Tak count(). Każdy, kto nie jest w stanie pojąć, że funkcja zwracająca liczby całkowite używane w wyrażeniu logicznym testuje na wartość niezerową, będzie miał wiele, wiele innych problemów na wielkim morzu idiomów C / C ++. I, jak wspomniano powyżej, naprawdę powinno być tak samo wydajne dla zestawów, co było pytaniem.
Ron Burk,
0
Byłem w stanie napisać ogólną containsfunkcję dla std::listi std::vector,
Przykro mi, ale nie mogę tak naprawdę napisać kodu blokowego w komentarzu, ale co z template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton
To nie działa, ponieważ std :: vector wymaga dodatkowego alokatora jako argumentu szablonu, a std :: set potrzebuje alokatora i mniej argumentu szablonu. Te wiersze działają: szablon <szablon <klasa, klasa> klasa STLContainer, klasa T, klasa A> bool zawiera (STLContainer <T, A> kontener, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } szablon <szablon <klasa, klasa, klasa> klasa STLContainer, klasa T, klasa L, klasa A> bool zawiera (STLContainer <kontener T, A, L>, T elt) {return find (container.begin (), kontener .end (), elt)! = container.end (); }
tgmath
0
// ogólna składnia
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * w poniższym kodzie próbuję znaleźć element 4 i int ustawić, jeśli jest obecny, czy nie * /
set<int>::iterator ii = find(set1.begin(),set1.end(),4);if(ii!=set1.end()){
cout<<"element found";
set1.erase(ii);// in case you want to erase that element from set.}
Odpowiedzi:
Typowym sposobem sprawdzenia istnienia w wielu pojemników STL, takich jak
std::map
,std::set
... jest:źródło
std::find(container.begin(), container.end(), element) != container.end()
; Problem O (N) pozostaje, oczywiście ...if(container.find(foo) == container.end())
musisz najpierw wyszukać drzewo, aby najpierw znaleźć element - jeśli nie zostanie znaleziony, musisz zrobić drugie drzewo, aby znaleźć poprawną lokalizację wstawiania. Oryginalny wariantif(container.insert(foo).second) {...}
ma urok, że potrzebuje tylko jednego wyszukiwania drzewa ...set.contains(x)
zwracający wartość bool w standardzie C ++ 20. Nie wiem, dlaczego zajęło nam to do 2020 roku.Innym sposobem na stwierdzenie, czy element istnieje, jest sprawdzenie
count()
Najczęściej jednak potrzebuję dostępu do elementu wszędzie tam, gdzie sprawdzam jego istnienie.
W każdym razie musiałbym znaleźć iterator. Wtedy oczywiście lepiej po prostu to porównać
end
.C ++ 20
W C ++ 20 zestaw dostaje
contains
funkcję, więc możliwe staje się, jak wspomniano na: https://stackoverflow.com/a/54197839/895245źródło
count()
zamiastfind()
nigdy nie jest lepsze, ale potencjalnie gorsze. To dlatego,find()
że powróci po pierwszym meczu,count()
zawsze będzie iterować po wszystkich elementach.multiset
imultimap
pomyślałem? Wciąż warto jednak zwrócić uwagę :)set
jest zawarcie tylko jednego pasującego elementu, czy funkcja nie zostałaby zaimplementowana w taki sposób, aby zatrzymać po zlokalizowaniu pierwszego elementu, w tym przypadku, jak zauważa Pieter? Przydatna odpowiedź w każdym razie!count()
nigdy nie jest szybsza niżfind()
) nadal obowiązuje, druga część rzeczywiście nie ma zastosowaniastd::set
. Wydaje mi się jednak, że można argumentować na korzyśćfind()
: jest bardziej wyrazisty, tzn. Podkreśla, że próbujesz znaleźć element zamiast liczyć liczbę wystąpień.Aby wyjaśnić, powodem, dla którego nie ma członka takiego jak
contains()
w tych typach kontenerów, jest to, że otworzyłoby cię to do pisania nieefektywnego kodu. Taka metoda prawdopodobnie po prostu wykonathis->find(key) != this->end()
wewnętrznie, ale zastanów się, co robisz, gdy klucz jest rzeczywiście obecny; w większości przypadków będziesz chciał zdobyć element i coś z nim zrobić. Oznacza to, że musisz zrobić sekundęfind()
, co jest nieefektywne. Lepiej jest użyć funkcji znajdź bezpośrednio, aby można było buforować swój wynik w następujący sposób:Oczywiście, jeśli nie zależy Ci na wydajności, zawsze możesz rzucić własną, ale w takim przypadku prawdopodobnie nie powinieneś używać C ++ ...;)
źródło
list::remove
,remove(makes_sense_only_for_vector, iterators)
...)W C ++ 20 w końcu otrzymamy
std::set::contains
metodę.źródło
Jeśli chcesz dodać
contains
funkcję, może to wyglądać następująco:Działa to z
std::set
innymi kontenerami STL, a nawet tablicami o stałej długości:Edytować:
Jak wskazano w komentarzach, przypadkowo użyłem nowej funkcji w C ++ 0x (
std::begin
istd::end
). Oto prawie trywialna implementacja VS2010:źródło
std::set
i pamiętaj, że jest ona odpowiednia tylko wtedy, gdy jedyne , co musisz wiedzieć, to istnienie.Możesz także sprawdzić, czy element jest w zestawie, czy nie podczas wstawiania elementu. Wersja z jednym elementem zwraca parę, a jej para elementów :: najpierw ustawiona na iterator wskazujący albo na nowo wstawiony element, albo na równoważny element już w zestawie. Para :: drugi element w tej parze jest ustawiony na true, jeśli wstawiono nowy element, lub false, jeśli element równoważny już istniał.
Na przykład: Załóżmy, że zestaw ma już 20 jako element.
Jeśli element jest nowo wstawiony niż para :: najpierw wskaże pozycję nowego elementu w zestawie.
źródło
Napisz swoje własne:
źródło
używam
Ale to nie jest tak wydajne jak
Moja wersja oszczędza tylko mój czas na pisanie kodu. Wolę to w ten sposób do konkurencyjnego kodowania.
źródło
count()
. Każdy, kto nie jest w stanie pojąć, że funkcja zwracająca liczby całkowite używane w wyrażeniu logicznym testuje na wartość niezerową, będzie miał wiele, wiele innych problemów na wielkim morzu idiomów C / C ++. I, jak wspomniano powyżej, naprawdę powinno być tak samo wydajne dla zestawów, co było pytaniem.Byłem w stanie napisać ogólną
contains
funkcję dlastd::list
istd::vector
,To nieco czyści składnię.
Ale nie mogłem użyć magii parametru szablonu szablonu, aby ta praca działała dowolnie.
Wszelkie komentarze dotyczące poprawy ostatniej odpowiedzi byłyby miłe.
źródło
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// ogólna składnia
/ * w poniższym kodzie próbuję znaleźć element 4 i int ustawić, jeśli jest obecny, czy nie * /
źródło