Lequel des énoncés suivants vous semble le plus lisible? La boucle manuscrite:
for (std::vector<Foo>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
bar.process(*it);
}
Ou l'invocation de l'algorithme:
#include <algorithm>
#include <functional>
std::for_each(vec.begin(), vec.end(),
std::bind1st(std::mem_fun_ref(&Bar::process), bar));
Je me demande si std::for_each
cela en vaut vraiment la peine, étant donné qu'un exemple aussi simple nécessite déjà beaucoup de code.
Quelles sont vos réflexions à ce sujet?
c++
algorithms
fredoverflow
la source
la source
map(bar.process, vec)
bien que la carte des effets secondaires soit déconseillée et que les compréhensions de liste / expressions de générateur soient recommandées par rapport à la carte).BOOST_FOREACH
...Réponses:
Il y a une raison pour laquelle les lambdas ont été introduits, et c'est parce que même le comité standard reconnaît que la deuxième forme est nulle. Utilisez le premier formulaire jusqu'à ce que vous obteniez le support C ++ 0x et lambda.
la source
Utilisez toujours la variante qui décrit le mieux ce que vous avez l'intention de faire. C'est
Examinons maintenant les exemples:
Nous en avons
for_each
aussi un - yippeh . Nous avons la[begin; end)
gamme sur laquelle nous voulons opérer.En principe, l'algorithme était beaucoup plus explicite et donc préférable à toute implémentation manuscrite. Mais alors ... des reliures? Memfun? Fondamentalement, C ++ interna de la façon d'obtenir une fonction membre? Pour ma tâche, je m'en fous ! Je ne veux pas non plus souffrir de cette syntaxe bavarde et effrayante.
Maintenant, l'autre possibilité:
Certes, c'est un modèle courant à reconnaître, mais ... création d'itérateurs, bouclage, incrémentation, déréférencement. Ce sont aussi des choses dont je ne me soucie pas pour accomplir ma tâche.
Certes, il semble waay mieux que la première solution (au moins, la boucle corps est flexible et tout à fait explicite), mais encore, ce n'est pas vraiment que grand. Nous utiliserons celui-ci si nous n'avions pas de meilleure possibilité, mais peut-être que nous avons ...
Une meilleure façon?
Revenons maintenant à
for_each
. Ne serait-il pas formidable de dire littéralementfor_each
et d' être flexible dans l'opération qui doit être faite aussi? Heureusement, depuis les lambdas C ++ 0x, nous sommesMaintenant que nous avons trouvé une solution abstraite et générique à de nombreuses situations connexes, il convient de noter que dans ce cas particulier, il existe un favori absolu # 1 :
Cela ne peut vraiment pas être beaucoup plus clair que cela. Heureusement, C ++ 0x get est une syntaxe similaire intégrée !
la source
for (const Foo& x : vec) bar.process(x);
, ou utilisantconst auto&
si vous le souhaitez.const auto&
est possible? Je ne savais pas que - grande info!Parce que c'est tellement illisible?
la source
int
avec unsize_t
est dangereux. De plus, l'indexation ne fonctionne pas avec tous les conteneurs, par exemplestd::set
.en général, la première forme est lisible par quasiment tous ceux qui savent ce qu'est une boucle, quel que soit leur arrière-plan.
également en général, le second n'est pas du tout lisible: assez facile à comprendre ce que for_each fait, mais si vous ne l'avez jamais vu,
std::bind1st(std::mem_fun_ref
je peux imaginer que c'est difficile à comprendre.la source
En fait, même avec C ++ 0x, je ne suis pas sûr d'
for_each
obtenir beaucoup d'amour.Est bien plus lisible.
La seule chose que je n'aime pas dans les algorithmes (en C ++), c'est qu'ils raisonnent sur les itérateurs, ce qui rend les déclarations très verbeuses.
la source
Si vous aviez écrit bar comme foncteur, ce serait beaucoup plus simple:
Ici, le code est assez lisible.
la source
Je préfère ce dernier car il est net et propre. Il fait en fait partie de nombreux autres langages mais en C ++, il fait partie de la bibliothèque. Ce n'est pas vraiment important.
la source
Maintenant, ce problème n'est pas spécifique au C ++, je dirais.
Le fait est que le passage d'un foncteur dans une autre fonction n'est pas censé remplacer les boucles .
Il est censé simplifier certains modèles, qui deviennent très naturels avec la programmation fonctionnelle, comme le visiteur et l' observateur , pour n'en nommer que deux, qui me viennent immédiatement à l'esprit.
Dans les langages qui manquent de fonctions de premier ordre (Java est probablement le meilleur exemple), de telles approches nécessitent toujours l'implémentation d'une interface donnée, qui est assez verbeuse et redondante.
Une utilisation courante que je vois beaucoup dans d'autres langues serait:
L'avantage de ceci est que vous n'avez pas besoin de savoir comment
someCollection
est réellement implémenté ou quoisomeUnaryFunctor
. Tout ce que vous devez savoir, c'est que saforEach
méthode itérera tous les éléments de la collection, en les passant à la fonction donnée.Personnellement, si vous êtes en mesure de disposer de toutes les informations sur la structure de données que vous souhaitez parcourir et toutes les informations sur ce que vous voulez faire à chaque étape de l'itération, une approche fonctionnelle consiste à trop compliquer les choses, en particulier dans une langue, où cela est apparemment assez fastidieux.
En outre, vous devez garder à l'esprit que l'approche fonctionnelle est plus lente, car vous avez beaucoup d'appels, qui ont un certain coût, que vous n'avez pas dans une boucle for.
la source
std::for_each(vec.begin(), vec.end(), std::bind1st(std::mem_fun_ref(&Bar::process), bar));
Et c'est plus lent au moment de l'exécution ou de la compilation (si vous avez de la chance). Je ne vois pas pourquoi le remplacement des structures de contrôle de flux par des appels de fonction améliore le code. À propos de toute alternative présentée ici est plus lisible.Je pense que le problème ici est que ce
for_each
n'est pas vraiment un algorithme, c'est juste une manière différente (et généralement inférieure) d'écrire une boucle écrite à la main. de ce point de vue, il devrait être l'algorithme standard le moins utilisé, et oui, vous pourriez aussi bien utiliser une boucle for. cependant, les conseils dans le titre sont toujours valables car il existe plusieurs algorithmes standard adaptés à des utilisations plus spécifiques.Lorsqu'il existe un algorithme qui fait plus étroitement ce que vous voulez, alors oui, vous devriez préférer les algorithmes aux boucles écrites à la main. des exemples évidents ici sont le tri avec
sort
oustable_sort
ou la recherche aveclower_bound
,upper_bound
ouequal_range
, mais la plupart d'entre eux ont une situation dans laquelle ils sont préférables à utiliser sur une boucle codée à la main.la source
Pour ce petit extrait de code, les deux sont également lisibles pour moi. En général, je trouve les boucles manuelles sujettes aux erreurs et préfère utiliser des algorithmes, mais cela dépend vraiment d'une situation concrète.
la source