Qual das seguintes opções você acha mais legível? O loop escrito à mão:
for (std::vector<Foo>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
bar.process(*it);
}
Ou a chamada do algoritmo:
#include <algorithm>
#include <functional>
std::for_each(vec.begin(), vec.end(),
std::bind1st(std::mem_fun_ref(&Bar::process), bar));
Gostaria de saber se std::for_each
realmente vale a pena, dado um exemplo tão simples já requer tanto código.
Quais são os seus pensamentos em relação a este assunto?
c++
algorithms
fredoverflow
fonte
fonte
map(bar.process, vec)
embora o mapa para efeitos colaterais seja desencorajado e as compreensões de lista / expressões geradoras sejam recomendadas sobre o mapa).BOOST_FOREACH
...Respostas:
Há uma razão pela qual as lambdas foram introduzidas, e é porque até o Comitê Padrão reconhece que a segunda forma é péssima. Use o primeiro formulário, até obter suporte para C ++ 0x e lambda.
fonte
Sempre use a variante que descreve melhor o que você pretende fazer. Isso é
Agora, vamos examinar os exemplos:
Temos um
for_each
lá também - yippeh . Temos o[begin; end)
alcance em que queremos operar.Em princípio, o algoritmo era muito mais explícito e, portanto, preferível a qualquer implementação escrita à mão. Mas então ... Binders? Memfun? Basicamente C ++ interna de como se apossar de uma função de membro? Para a minha tarefa, eu não me importo com eles! Também não quero sofrer com essa sintaxe detalhada e assustadora.
Agora a outra possibilidade:
É verdade que esse é um padrão comum para reconhecer, mas ... criar iteradores, repetir, incrementar, desreferenciar. Essas também são todas as coisas de que não me importo para realizar minha tarefa.
É certo que parece melhor do que a primeira solução (pelo menos, o corpo do loop é flexível e bastante explícito), mas ainda assim, não é realmente tão bom assim. Usaremos este se não tivermos uma possibilidade melhor, mas talvez tenhamos ...
Uma maneira melhor?
Agora de volta para
for_each
. Não seria ótimo dizer literalmentefor_each
e ser flexível na operação a ser realizada também? Felizmente, desde C ++ 0x lambdas, estamosAgora que encontramos uma solução abstrata e genérica para muitas situações relacionadas, vale a pena notar que, neste caso em particular, existe um favorito nº 1 absoluto :
Realmente não pode ficar muito mais claro que isso. Felizmente, C ++ 0x get de uma sintaxe semelhante built-in !
fonte
for (const Foo& x : vec) bar.process(x);
, ou usando,const auto&
se quiser.const auto&
é possível? Não sabia disso - ótimas informações!Porque isso é tão ilegível?
fonte
int
com umsize_t
é perigoso. Além disso, a indexação não funciona com todos os contêineres, por exemplostd::set
.em geral, a primeira forma é legível para praticamente qualquer pessoa que saiba o que é um loop for, não importa o que eles tenham.
também em geral, o segundo não é tão legível: é fácil entender o que for_each está fazendo, mas se você nunca viu
std::bind1st(std::mem_fun_ref
, posso imaginar que é difícil entender.fonte
Na verdade, mesmo com C ++ 0x, não tenho certeza se
for_each
vai receber muito amor.É muito mais legível.
A única coisa que eu não gosto nos algoritmos (em C ++) é que eles raciocinam em iteradores, produzem declarações muito detalhadas.
fonte
Se você tivesse escrito a barra como um functor, seria muito mais simples:
Aqui o código é bastante legível.
fonte
Eu prefiro o último porque é arrumado e limpo. Na verdade, faz parte de muitas outras linguagens, mas em C ++ faz parte da biblioteca. Não é realmente importante.
fonte
Agora, esse problema não é específico para C ++, eu diria.
A questão é que passar algum functor para outra função não visa substituir os loops .
Supõe-se que simplifique certos padrões, que se tornam muito naturais com a programação funcional, como visitante e observador , apenas para citar dois, que vêm à minha mente imediatamente.
Em linguagens que não possuem funções de primeira ordem (Java provavelmente é o melhor exemplo), essas abordagens sempre exigem a implementação de uma determinada interface, que é bastante verbal e redundante.
Um uso comum que vejo muito em outros idiomas seria:
A vantagem disso é que você não precisa saber como
someCollection
é realmente implementado ou o quesomeUnaryFunctor
faz. Tudo que você precisa saber é que seuforEach
método irá iterar todos os elementos da coleção, passando-os para a função especificada.Pessoalmente, se você estiver na posição de ter todas as informações sobre a estrutura de dados que deseja iterar e todas as informações sobre o que você deseja fazer em cada etapa da iteração, uma abordagem funcional está complicando demais as coisas, especialmente em um idioma, onde isso é aparentemente bastante entediante.
Além disso, você deve ter em mente que a abordagem funcional é mais lenta, porque você tem muitas chamadas, que têm um certo custo, que você não tem em um loop for.
fonte
std::for_each(vec.begin(), vec.end(), std::bind1st(std::mem_fun_ref(&Bar::process), bar));
E é mais lento no tempo de execução ou no tempo de compilação (se você tiver sorte). Não vejo por que substituir estruturas de controle de fluxo por chamadas de função melhora o código. Sobre qualquer alternativa apresentada aqui é mais legível.Acho que o problema aqui é que
for_each
não é realmente um algoritmo, é apenas uma maneira diferente (e geralmente inferior) de escrever um loop escrito à mão. dessa perspectiva, ele deve ser o algoritmo padrão menos usado e, sim, você provavelmente também pode usar um loop for. no entanto, o conselho no título ainda permanece, pois existem vários algoritmos padrão personalizados para usos mais específicos.Onde houver um algoritmo que faça com mais precisão o que você deseja, sim, você deve preferir algoritmos a loops escritos à mão. exemplos óbvios aqui estão classificando com
sort
oustable_sort
ou pesquisar comlower_bound
,upper_bound
ouequal_range
, mas a maioria deles tem alguma situação em que se encontram preferível usar mais de um loop codificado mão.fonte
Para este pequeno trecho de código, ambos são igualmente legíveis para mim. Em geral, acho que os loops manuais são propensos a erros e prefiro usar algoritmos, mas isso realmente depende de uma situação concreta.
fonte