8
votes

Retravailler pour boucle sur le conteneur STL pour utiliser des techniques fonctionnelles

J'ai un std :: vecteur code> de Pointeurs Personne code> d'objets, qui ont une fonction de membre std :: string getname () const code>. Utilisation d'algorithmes STL, je veux compter toutes les objets code> de la personne code> dans le vecteur où getname () code> retourne "Tchad".

Le comportement simplement itération sur la boucle serait: P>

const int num_chads = std::count_if(vec.begin(), vec.end(),
                                    std::bind1st(std::bind2nd(std::equal_to, mem_fun(Person::getName)), "Chad"));


7 commentaires

Pas une réponse: j'aime les techniques fonctionnelles, mais j'ai abandonné sur des algorithmes stl-algorithmes pour des objets. Lambda est vraiment le lien manquant de C ++ 03. Même avec Boost.Lambda, il y a beaucoup de bizarreries. Les sl-algorithmes fonctionnent bien avec les primitives, mais j'utilise rarement des conteneurs de primitives ... cela va-t-il d'éviter les boucles lorsqu'elles sont remplacées par des constructions liées?


Est-il normal que getname () reviendrait par copie?


Boost :: Bind est beaucoup plus propre que STD :: Binday cependant, et vous pouvez combiner Boost Lambdas dans.


@stefaanv - Je pense que dans des conditions pratiques, je serais d'accord la plupart du temps. Si un exemple, ce simple est arrivé, je voudrais probablement écrire la boucle. @Matthieu - Je comprends d'où vous venez d'où vous venez, mais l'exemple est intégré à cet objectif de cette question et de revenir par copie vs retour de Cons Ref n'est pas quelque chose que j'ai vraiment pensé (je ne pense pas que cela change le cadrage de la question).


@Rodion Ingles: Pas du tout, autant que je sache, la création du temporaire compliquera légèrement le travail du compilateur mais ne devrait pas avoir une incidence sur la correction des solutions présentées.


J'essaie (et échoué) de trouver une solution à ce que vous demandez. J'espère que quelqu'un a plus de succès que moi, sinon quelqu'un pouvait expliquer pourquoi cela ne peut pas être fait.


@Captain girafe Vous ne pouviez pas faire cela car C ++ 98 n'incluait pas la partie requise de STL, voir Stackoverflow.com/questions/5325122/...


3 Réponses :


9
votes

J'ai toujours trouvé Lambdas relativement illisible. Je préfère beaucoup écrire des types explicites: xxx

bien que la définition de nommée est "hors ligne", un nom correctement choisi transmet l'intention et cache la Détails d'implémentation. Personnellement, je considère cela une bonne chose, car alors je ne dis pas les détails de la mise en œuvre ni d'essayer de comprendre ce qui se passe en ingénierie inverse du code (aussi évident que cela pourrait être).


10 commentaires

C'est une bonne solution, mais personnellement, je vois des foncteurs utilisés pour cela comme la Lambda de l'homme pauvre, principalement en raison du code de la plaque de chaudière impliqué.


@stefaanv: Je suis d'accord avec la chaudière, mais puisque nous utilisons une nommée pour avoir une intention, des fonctions anonymes n'ont pas ce niveau d'explicité, bien sûr, pour de telles fonctions simples, on pourrait affirmer que c'est évident ... Cependant la syntaxe d'arcanes de STD :: BIND * cache la simplicité.


Oui, c'est une bonne solution pour équilibrer une approche plus fonctionnelle sans curryer les foncteurs de la STL. Cependant, je cherchais vraiment quelque chose qui n'a pas impliqué d'écrire vos propres objets de fonction (je sais que cela n'a pas été spécifié dans la question). Cela dit, clairement un bon exemple d'utilisation d'un prédicat personnalisé dans le comte_if.


Mais, peut-il nommé être déclaré dans la même fonction dans laquelle il est utilisé? Chaque fois que j'essaie, je reçois des erreurs de portée.


Je pense "Taille_t const c = std :: comte_if (Vec.begin (), Vec.end (), [] (const personne & p) {retour p.getname () ==" Tchad ";});" Est toujours bien, mais cela devient probablement rapidement plus compliqué.


@ROB Adams: Malheureusement, je suggère des espaces de noms anonymes. La norme C ++ 03 interdit en réalité l'utilisation de types définis sur la fonction sous forme de paramètres de modèle (je pense que c'est un problème de lien, mais vous aurez besoin de quelqu'un de plus savant que moi pour une confirmation).


@stefaanv: True Lambdas sont assez lisibles, je suis d'accord, mais l'OP spécifié qu'il souhaitait une solution C ++ 03 et, malheureusement, je ne peux pas penser à une solution élégante avec Bind (quelle que soit la version). J'aimerais vraiment que Deadmg augmente sa réponse pour voir la syntaxe qu'il avait à l'esprit.


@Rob - Cette réponse manque des informations pour des raisons de brièveté. Nommé est une structure, ce qui signifie qu'il ne peut être défini que dans une autre définition de classe ou comme une structure seule (ceci s'applique aux classes également). Vous ne pouvez pas définir une classe à l'intérieur d'une fonction, qui a toujours été l'un des plus gros problèmes de l'utilisation des foncteurs personnalisés: ils doivent être définis à l'écart du point qu'ils sont appelés. C'est là que les lambas entrent et rendent les choses plus attrayantes en vous laissant définir efficacement une fonction dans une autre.


L'approche est juste, mais la mise en œuvre n'est pas. À l'intérieur du fonctionnement, vous stockez un pointeur à un littéral et comparant ce pointeur avec le résultat d'un appel à la fonction getname () , qui ne sera probablement pas un pointeur sur le même littéral à chaîne.


@David: Nope, en fait, selon ma remarque, il renvoie un std :: string (par copie).



1
votes

Utiliser boost :: lid , il est supérieur d'une manière tout à fait une manière aux mécanismes de liaison standard existants. boost :: lid est complètement C ++ 03 compatible.


5 commentaires

Pouvez-vous penser à une façon de le faire sans booster?


Ou, sinon, fournir une réponse de boost? (Ou les deux!: P)


@Rodion Ingles: Bien sûr, mais cela impliquerait juste écrire boost :: lid à nouveau vous-même, ce qui est plutôt inutile. En ce qui concerne la fourniture d'une telle réponse, la documentation est parfaitement suffisante pour que vous puissiez le faire vous-même. C'est le travail de la réponse à vous aider, pas de votre main.


Eh bien, peut-être que vous avez un point sur la fourniture d'un exemple de boost, mais votre TRITE répondit de "Écriture boost :: lidez-vous à nouveau" ne servent personne mais vous.


@Rodion Ingles: Ce n'est pas vrai du tout. Boost :: Lier Est-ce que c'est fait. Soit vous utilisez boost :: lid ou vous roulez votre propre boost :: lid , mais à la fin de la journée, c'est toujours boost :: lié < / Code> Sous quel que soit le nom que vous le donnez.



5
votes

Puisque personne n'a déjà publié le code de boost réel, C ++ 98 avec boost: xxx

test de test https://ideone.com/pavje

comme pour pure C ++, je ne pense pas qu'il soit possible sans le compose1 adaptateur, présent dans STL mais pas en C ++ stdlib ...

Et ici, c'est (à l'aide de la mise en œuvre de GCC de stl) xxx

essai de test: https://ideone.com/eqbs5

modifier: corrigé pour tenir compte de personne *


4 commentaires

Petite différence, l'OP utilise std :: vecteur qui appartient à tous les problèmes. Cela signifie que vous devez avoir besoin (apparemment) à Déréférence personne * avant d'appliquer la fonction membre, et il est possible que c'est null (dans le cas général).


@Matthieu M Fait: S / MEM_FUN_REF / MEM_FUN / M Bien que la possibilité de la désérofacturation du pointeur NULL sera certainement présente avec la plupart des solutions à une doublure. C'est-à-dire que cela peut être pris en charge, mais la solution deviendrait illisible.


@Matthieu - Le fait que le vecteur contient personne * fait une différence (autre que le numéro de pointeur NULL mentionné ci-dessus)? Le seul changement que je peux voir est que vous devez utiliser MEM_FUN au lieu de MEM_FUN_RF, ce qui réduit efficacement la différence entre. * Et -> * *


@RODION: J'espère que non: P Je pense que boost :: lid s'écoute automatiquement avec elle.