A única maneira de simplificar isso seria um predicado booleano: template <typename T> membro bool (T const & item). E isso seria implementado (nos bastidores) em termos da linha que você está perguntando.
9119 Don Wakefield
Respostas:
399
A forma habitual para verificar se há existência em muitos contêineres STL, como std::map, std::set... é:
isso é específico para conjuntos e mapas. vetores, listas etc. não têm uma função de membro find.
11139 wilhelmtell
8
O IMO usando count () é melhor porque é simplesmente mais curto e converte em bool, conforme observado na resposta de Pieter. Eu não entendo pontos porque esta resposta foi aceito e tantos ...
Огњен Шобајић
4
Por uma questão de exaustividade: vetores / listas pode usar std :: encontrar: std::find(container.begin(), container.end(), element) != container.end(); O (N) problema permanece, é claro ...
Aconcagua
10
@MichaelMathews Com a sua variante: é if(container.find(foo) == container.end())necessário fazer uma pesquisa em árvore para encontrar o elemento primeiro - se não for encontrado, você precisará fazer uma segunda pesquisa em árvore para encontrar o local de inserção correto. A variante originais if(container.insert(foo).second) {...}tem o charme que ele precisa de apenas uma única pesquisa árvore ...
Aconcagua
23
existe um set.contains(x)que retorna um bool no padrão C ++ 20. Eu não sei por que ele nos levou até 2020 para conseguir que no.
gremwell
215
Outra maneira de simplesmente dizer se existe um elemento é verificar o count()
if(myset.count(x)){// x is in the set, count is 1}else{// count zero, i.e. x not in the set}
Na maioria das vezes, no entanto, me vejo precisando de acesso ao elemento sempre que verifico sua existência.
Então, eu teria que encontrar o iterador de qualquer maneira. Então, é claro, é melhor simplesmente compará-lo endtambém.
set< X >::iterator it = myset.find(x);if(it != myset.end()){// do something with *it}
Observe que usar em count()vez de find()nunca é melhor, mas potencialmente pior. Isso ocorre porque find()retornará após a primeira partida, count()sempre iterará sobre todos os elementos.
Frerich Raabe
34
@ Frichich isso é relevante apenas para multisete multimapeu pensei? Ainda bom ressaltar embora :)
Pieter
83
O std :: set normalmente é implementado com uma estrutura em árvore ordenada, portanto, count () e find () terão O (logn). Nem iterará sobre todos os elementos no conjunto.
Alan
14
@FrerichRaabe - Você tem certeza? Como só é possível setconter um membro que corresponda, a função não seria implementada de forma a parar após localizar o primeiro elemento, neste caso, como Pieter aponta? Resposta útil em qualquer caso!
Dan Nissenbaum 27/03
14
@ DanNissenbaum Sim, você está exatamente certo (assim como + Peter e + Alan): para std :: set, as duas funções são equivalentes em termos de desempenho. Portanto, mesmo que a primeira parte do meu comentário ( count()nunca sendo mais rápida que find()) ainda seja válida, a segunda parte não é de fato aplicável a std::set. No entanto, acho que outro argumento pode ser feito a favor de find(): é mais expressivo, ou seja, enfatiza que você está tentando encontrar um elemento em vez de contar o número de ocorrências.
Frerich Raabe 27/03
42
Apenas para esclarecer, a razão pela qual não há membros como contains()esses tipos de contêineres é porque isso abriria você para escrever código ineficiente. Esse método provavelmente faria apenas this->find(key) != this->end()internamente, mas considere o que você faz quando a chave está realmente presente; na maioria dos casos, você deseja obter o elemento e fazer algo com ele. Isso significa que você teria que fazer um segundo find(), o que é ineficiente. É melhor usar o find diretamente, para que você possa armazenar em cache o resultado da seguinte maneira:
auto it = myContainer.find(key);if(it != myContainer.end()){// Do something with it, no more lookup needed.}else{// Key was not present.}
Obviamente, se você não se importa com eficiência, sempre pode fazer o seu próprio, mas nesse caso você provavelmente não deveria estar usando C ++ ...;)
E os sets? Normalmente, você já tem o elemento, mas só quero verificar se ele está dentro.
Elazar Leibovich
8
Você tem alguma referência para saber se esse é o motivo real desse método / função não estar incluído no stl ou se é apenas um palpite?
Fabio A.
3
@FabioA. É o meu palpite.
Tim
1
@Adhemar, a consistência não é exatamente do lado forte da STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich
3
Não faz sentido não incluir um recurso, porque alguém pode usá-lo incorretamente se não souber o que está fazendo. A programação é para as pessoas que podem pensar por si mesmos e que são responsáveis pelo seu código e seu desempenho
slawekwin
13
No C ++ 20 , finalmente obteremos o std::set::containsmétodo.
#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";}}
Se você fosse adicionar uma containsfunção, ela poderia ser assim:
#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();}
Isso funciona com std::setoutros contêineres STL e até matrizes de comprimento fixo:
Conforme apontado nos comentários, usei involuntariamente uma função nova para C ++ 0x ( std::begine std::end). Aqui está a implementação quase trivial do 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, na verdade era ineficiente, mas não pelo motivo que você mencionou.
Sam Harwell
@ Paul: Certifique-se de incluir a especialização std::sete lembre-se de que isso é apropriado apenas se a única coisa que você precisa saber é a existência.
Sam Harwell
@ 280Z28: std :: begin (contêiner)? Que padrão STL é esse? Não compila no meu gcc.
stefaanv
@stefannv: heh, é novo para C ++ 0x. Eu adicionei a implementação do meu compilador acima.
Sam Harwell
2
@ Adhemar: Se você sabe que o conjunto contém um valor, então você já o valor. O único motivo para você precisar do iterador é apagar o elemento do conjunto. Se tudo o que você precisa é saber se uma coleção contém ou não um valor, essa solução não é menos eficiente do que qualquer outra solução.
Sam Harwell
4
Você também pode verificar se um elemento está definido ou não durante a inserção do elemento. A versão de elemento único retorna um par, com seu par de membros :: first definido para um iterador apontando para o elemento recém-inserido ou para o elemento equivalente já existente no conjunto. O elemento pair :: second no par é definido como true se um novo elemento foi inserido ou false se um elemento equivalente já existir.
Por exemplo: Suponha que o conjunto já tenha 20 como um elemento.
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.
Se o elemento for inserido recentemente, o pair :: first apontará para a posição do novo elemento no conjunto.
apenas o fiz: template <classe T> estático inline estático contém (const std :: set <T> & S, Tx) {return (S.find (x)! = S.end ()); }
fulmicoton
4
@paul não cria funções globais estáticas. coloque sua função em um espaço para nome anônimo: essa é a maneira C ++ de criar funções que não serão vinculadas a outras unidades de compilação. Além disso, seu parâmetro T deve ser uma referência const, para const-correção e eficiência.
11139 wilhelmtell
-1: Não templated e não em tudo em grande estilo STL. Isso é bom se você não estiver usando STL, mas se estiver usando STL, pelo menos tente seguir seus padrões.
Sam Harwell
1
@ 280Z28: Lamento que meu código não atenda aos seus padrões, eu estava apenas mostrando que se você não gosta da interface da STL, pode escrever sua própria. Eita, não templated? Como tem que ser? Seu exemplo parece bom, isso não significa que o meu seja ruim. É apenas mais focado no cenário, como foi solicitado pelo OP.
9788 stefaanv
1
@ 280Z28: Eu estava apenas fazendo um ponto. Eu pensei que as pessoas seriam inteligentes o suficiente para entender a imagem.
stefaanv
2
eu uso
if(!my_set.count(that_element))//Element is present...;
Mas não é tão eficiente quanto
if(my_set.find(that_element)!=my_set.end())....;
Minha versão economiza apenas meu tempo escrevendo o código. Eu prefiro assim para a codificação competitiva.
Sim, count(). Qualquer pessoa que não consiga entender que uma função de retorno inteiro usada em uma expressão booleana está testando diferente de zero terá muitos, muitos outros problemas no grande mar de expressões em C / C ++. E, como observado acima, realmente deve ser tão eficiente para conjuntos, que era a questão.
Ron Burk
0
Consegui escrever uma containsfunção geral para std::liste std::vector,
Desculpe, não posso realmente escrever código de bloco no comentário, mas que sobre template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton
Não está funcionando, porque std :: vector precisa de um alocador adicional como argumento de modelo e std :: set precisa de um alocador e um argumento de menos modelo. Essas linhas funcionam: modelo <modelo <classe, classe> classe STLContainer, classe T, classe A> bool contém (STLContainer <T, A> container, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } modelo <modelo <classe, classe, classe> classe STLContainer, classe T, classe L, classe A> bool contém (STLContainer <T, A, L> container, T elt) {return find (container.begin (), container .end (), elt)! = container.end (); }
tgmath 9/09/13
0
// Sintaxe geral
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * no código abaixo, estou tentando encontrar o elemento 4 e o int definido se ele estiver presente ou não * /
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.}
Respostas:
A forma habitual para verificar se há existência em muitos contêineres STL, como
std::map
,std::set
... é:fonte
std::find(container.begin(), container.end(), element) != container.end()
; O (N) problema permanece, é claro ...if(container.find(foo) == container.end())
necessário fazer uma pesquisa em árvore para encontrar o elemento primeiro - se não for encontrado, você precisará fazer uma segunda pesquisa em árvore para encontrar o local de inserção correto. A variante originaisif(container.insert(foo).second) {...}
tem o charme que ele precisa de apenas uma única pesquisa árvore ...set.contains(x)
que retorna um bool no padrão C ++ 20. Eu não sei por que ele nos levou até 2020 para conseguir que no.Outra maneira de simplesmente dizer se existe um elemento é verificar o
count()
Na maioria das vezes, no entanto, me vejo precisando de acesso ao elemento sempre que verifico sua existência.
Então, eu teria que encontrar o iterador de qualquer maneira. Então, é claro, é melhor simplesmente compará-lo
end
também.C ++ 20
No conjunto C ++ 20, obtém uma
contains
função, portanto, o seguinte se torna possível conforme mencionado em: https://stackoverflow.com/a/54197839/895245fonte
count()
vez defind()
nunca é melhor, mas potencialmente pior. Isso ocorre porquefind()
retornará após a primeira partida,count()
sempre iterará sobre todos os elementos.multiset
emultimap
eu pensei? Ainda bom ressaltar embora :)set
conter um membro que corresponda, a função não seria implementada de forma a parar após localizar o primeiro elemento, neste caso, como Pieter aponta? Resposta útil em qualquer caso!count()
nunca sendo mais rápida quefind()
) ainda seja válida, a segunda parte não é de fato aplicável astd::set
. No entanto, acho que outro argumento pode ser feito a favor defind()
: é mais expressivo, ou seja, enfatiza que você está tentando encontrar um elemento em vez de contar o número de ocorrências.Apenas para esclarecer, a razão pela qual não há membros como
contains()
esses tipos de contêineres é porque isso abriria você para escrever código ineficiente. Esse método provavelmente faria apenasthis->find(key) != this->end()
internamente, mas considere o que você faz quando a chave está realmente presente; na maioria dos casos, você deseja obter o elemento e fazer algo com ele. Isso significa que você teria que fazer um segundofind()
, o que é ineficiente. É melhor usar o find diretamente, para que você possa armazenar em cache o resultado da seguinte maneira:Obviamente, se você não se importa com eficiência, sempre pode fazer o seu próprio, mas nesse caso você provavelmente não deveria estar usando C ++ ...;)
fonte
list::remove
,remove(makes_sense_only_for_vector, iterators)
...)No C ++ 20 , finalmente obteremos o
std::set::contains
método.fonte
Se você fosse adicionar uma
contains
função, ela poderia ser assim:Isso funciona com
std::set
outros contêineres STL e até matrizes de comprimento fixo:Editar:
Conforme apontado nos comentários, usei involuntariamente uma função nova para C ++ 0x (
std::begin
estd::end
). Aqui está a implementação quase trivial do VS2010:fonte
std::set
e lembre-se de que isso é apropriado apenas se a única coisa que você precisa saber é a existência.Você também pode verificar se um elemento está definido ou não durante a inserção do elemento. A versão de elemento único retorna um par, com seu par de membros :: first definido para um iterador apontando para o elemento recém-inserido ou para o elemento equivalente já existente no conjunto. O elemento pair :: second no par é definido como true se um novo elemento foi inserido ou false se um elemento equivalente já existir.
Por exemplo: Suponha que o conjunto já tenha 20 como um elemento.
Se o elemento for inserido recentemente, o pair :: first apontará para a posição do novo elemento no conjunto.
fonte
Escreva o seu próprio:
fonte
eu uso
Mas não é tão eficiente quanto
Minha versão economiza apenas meu tempo escrevendo o código. Eu prefiro assim para a codificação competitiva.
fonte
count()
. Qualquer pessoa que não consiga entender que uma função de retorno inteiro usada em uma expressão booleana está testando diferente de zero terá muitos, muitos outros problemas no grande mar de expressões em C / C ++. E, como observado acima, realmente deve ser tão eficiente para conjuntos, que era a questão.Consegui escrever uma
contains
função geral parastd::list
estd::vector
,Isso limpa um pouco a sintaxe.
Mas eu não poderia usar a mágica de parâmetro de modelo de modelo para fazer com que isso funcionasse com contêineres stl arbitrários.
Qualquer comentário sobre como melhorar a última resposta seria bom.
fonte
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// Sintaxe geral
/ * no código abaixo, estou tentando encontrar o elemento 4 e o int definido se ele estiver presente ou não * /
fonte