IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Utilisation de bind2nd et bind1st


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Décembre 2011
    Messages
    68
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2011
    Messages : 68
    Par défaut Utilisation de bind2nd et bind1st
    Bonjour,

    après avoir parcourus la Faq et étudié l'utilisation de bind2nd et bind1st qui y est décrite ainsi que mon Stroustrup, j'ai essayer de bien cerner le fonctionnement de ces deux outils.
    Je bloque sur ce qui me semble être un cas d'école :

    J'essaye de ré-écrire une fonction simple et qui me satisfait déjà mais à l'aide des opérateurs de <functionnal.h>

    Voici ma fonction :
    Elle est membre de la classe "messages" qui hérite de std::list<coMsg*>
    "nombre" est un typedef de nombre à virgule, et "uint" un typedef d'entier strictement positif. "sens" est un "uint". La fonction intervient dans le calcul d'une probabiltée sans interet ici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    nombre CalculPi(sens categorie)
    {
            uint compteur = 0;
            for(messages::iterator i = begin(); i != end(); i++)
                if ((*i)->GetCat() == categorie)
                    compteur++;
     
            if (size() == 0)
                return 0.;
            return ((nombre)compteur / (nombre)size());
    }
    J'ai donc tenté de la ré-écrire la boucle de calcul avec le module <fonctionnal.h> comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
            ptrdiff_t c =
                std::count_if(begin(),end(),
                    std::bind2nd(
                        std::bind1st(std::equal_to<sens>(),
                                     categorie
                        ),
                        std::mem_fun(&coMsg::GetCat)
                    )
                );
    Je ne cherche pas l'éfficacité en ligne de code mais juste à bien comprendre le fonctionnement de la stl sur ce module.
    Le problème... c'est que ca ne marche pas du tout, malgré les messages d'erreurs du compilateur je ne parviens pas à trouver comment disposer les fonctions pour re-créer l'équivalent de ma boucle si dessus.

    Ce qui fonctionnellement s'écrirais comme ceci en ocaml :
    " fold_left (fun r x -> if categorie = CoMsg.getCat x then r else succ r) 0 "
    comment je peu l'écrire en C++ avec les fonctions du module <fonctionnal.h>


    Est ce que vous pourriez m'aider à trouver comment disposer les fonctions pour recréer correctement ma petite boucle ?

    Merci d'avance pour votre aide.

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    Si tu n'utilise pas le C++11, les plus simple est d'avoir une fonction libre ou un foncteur à part (dans une namespace anonyme) que tu utilise lors du compte :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
    bool verifier_categorie( coMsg const* msg, uint cat )
    {
        return coMsg->GetCat() == cat;
    }
     
    //...
    ptrdiff_t c =
                std::count_if(begin(),end(),
                    std::bind2nd(
                         std::ptr_fun( verifier_categorie ),
                         categorie
                    )
                );
     
    /* OU alors : */
    struct VerifierCategorie
    {
         VerifierCategorie(uint cat): cat_(cat)
         {}
     
         bool operator() ( coMsg const* msg ) const
         {
               coMsg->GetCat() == cat_;
         }
    private:
         uint cat_;
    };
     
    //...
     
    ptrdiff_t c = std::count_if(begin(),end(), VerifierCategorie( categorie );

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut
    Autre solution sans doute pas la meilleure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <iostream>
    #include <list>
    #include <algorithm>
    #include <functional>
     
    using namespace std;
     
    struct Categorie
    {
        int m_val;
     
        Categorie ( int val ) : m_val ( val ) {}
        bool operator== ( const Categorie& c ) const { return m_val == c.m_val; }
    };
     
    struct CoMsg
    {
        Categorie m_categorie;
     
        CoMsg ( const Categorie& c ) : m_categorie ( c ) {}
        bool operator== ( const CoMsg& c ) const { return m_categorie == c.m_categorie; }
    };
     
    struct Msg : public list<CoMsg>
    {
        int calculPi ( Categorie categorie )
        {
            difference_type compteur = count_if ( begin () ,
                                                    end () ,
                                                  bind1st( equal_to<CoMsg> () , CoMsg ( categorie ) )
                                                );
            return compteur;
        }
    };
     
    int main()
    {
        Msg m;
     
        m.push_back ( CoMsg ( Categorie ( 2 ) ) );
        m.push_back ( CoMsg ( Categorie ( 1 ) ) );
        m.push_back ( CoMsg ( Categorie ( 2 ) ) );
     
        cout << m.calculPi ( Categorie (2) ) << endl;
     
        return 0;
    }

  4. #4
    Membre confirmé
    Homme Profil pro
    Ingénieur de recherche
    Inscrit en
    Décembre 2011
    Messages
    68
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur de recherche
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2011
    Messages : 68
    Par défaut
    Bonsoir,

    Merci pour le temps que vous m'avez consacré.
    Effectivement le passage à C++2011 n'est pas à l'ordre du jour.

    J'aurais aimer éviter les solutions basés sur une déclaration de fonctions (verifier_categorie( coMsg const*, sens)) et ce pour deux raisons :
    1)
    La volonté implicite de l'utilisation des opérateurs de fonctionnalité (indépendament du langage) est un gain en généricité en masquant le nommage du concept correspondant au test (aka verifier_catégorie).
    2)
    Dans l'éventualité où j'utiliserais massivement ce paradigme, je serais contraire aux normes de codage de ma boite. Je ne peux raisonnablement pas envisager de polluer l'espace de nommage avec de nombreuses fonctions outils.

    Cependant comme vous le soulignez indirectement tout les deux au travers de structure et classe concernant les "catégories", il y a effectivement un besoin pour plus de fonctionnalitées sur les "catégories"; elles sont aujourd'hui réduite à un entier non signé.
    Notament la relation d'égalité. Il est vrai que je n'ai pas précisé que deux coMsg peuvent avoir la même catégorie tout en étant très différents.


    Mon vrai objectif d'apprentissage est de réussir l'implementation juste avec les opérateurs de la stl. Je peux reformuler ma question ainsi :
    Comment utilisé l'enchainement de bind1st autour de l'égalité et bind2nd autour de la fontion d'accès GetCat pour obtenir un opérateur de test fonctionnel anonyme.


    en effet dans cette exemple j'utilise la version spécialisé du "equal" sur mes catégories (sens). Donc si leurs définition devait ne plus être 'uint' le code ne serait pas à ré-écrire.
    Par rapport à la version d'origine de mon code où je créer une boucle; cette version manipule moins de concept ( compteur, i , moins de type exprimés ). Le code n'est pas véritablement plus long :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
                count_if(begin(),end(),
                    bind2nd(bind1st(equal_to<sens>(), categorie),
                        mem_fun(&coMsg::GetCat)
                    )
    4 lignes pour obtenir le compteur, comme la boucle d'origine. Sauf que ces 4 lignes là sont une R-expression.
    On voie bien aussi qu'une nouvelle définition de la classe qui manipule les catégories (le typedef 'sens') n'aurait pas d'impact sur le code s'il implémente l'opérateur ==. la définition de == sur les coMsg n'étant pas requise.

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 118
    Par défaut
    Je me demande est ce que ton problème est vraiment ré solvable en utilisant uniquement les fonctions et classe:
    -bind1st
    -bind2nd
    -mem_fun
    -equal_to

    sans introduire de Foncteur, Prédicat, operateur== ou fonction libre faits maison.

    En effet le code de count_if est le suivant (recopié au passage sur http://www.cplusplus.com) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <class InputIterator, class Predicate>
    ptrdiff_t count_if ( InputIterator first, InputIterator last, Predicate pred )
    {
      ptrdiff_t ret=0;
      while (first != last) if (pred(*first++)) ++ret;
      return ret;
    }
    la question qui se pose est comment faire en sorte d'avoir un prédicat unaire recevant un paramètre de type CoMsg afin de tester l'égalité de leur Categorie en utilisant uniquement les fonctions et classe précitées?

    Ton impémentation, en dehors du fait qu'elle ne soit pas syntaxiquement correct, ne semble pas pouvoir résoudre le problème car tu appelles 2 fonctions(bind1st et bind2nd) qui vont "binder" successivement un foncteur qui initialement était binaire (au sens prenant 2 paramètres). De la sorte, le foncteur résultant ne peut plus rien prendre en paramètre.

    D'ailleurs est il possible de "binder" l'objet résultant d'une des fonctions bind1st,bind2nd?
    En toute logique non car il faut que les typedefs result_type,first_argument_type,second_argument_type soient définis dans la classe du nouvel objet à binder.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gallima Voir le message
    Voici ma fonction :
    Elle est membre de la classe "messages" qui hérite de std::list<coMsg*>
    "nombre" est un typedef de nombre à virgule, et "uint" un typedef d'entier strictement positif. "sens" est un "uint". La fonction intervient dans le calcul d'une probabiltée sans interet ici :
    Heu... rassures moi, tu ne parle pas d'un héritage publique, j'espère!!!

    Il faut savoir que le destructeur des conteneurs de la STL n'est pas virtuel, ce qui fait qu'on ne peut pas avoir la garantie, en cas d'héritage publique, que le destructeur de la classe héritière sera appelé, entre autres, si tu crées une fonction prenant un pointeur vers un objet de type conteneur STL.

    De plus, on se trouve dans une situation fort proche de celle qui existe lorsque l'on envisage les listes et les listes triées, les deuxième ne pouvant pas (par égard au LSP) hériter des secondes.

    Enfin, il faut garder en tete que l'héritage publique est la relation la plus forte qui puisse exister entre deux classes, et qu'il faut donc l'utiliser avec la plus grande parcimonie, et uniquement quand certaines conditions de substituabilité sont réunies

    Si donc, ta classe ressemble au
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    struct Msg : public list<CoMsg>
    {
    };
    de backlash, dépêche toi de suprimer cet héritage que nous ne pouvons voir, sous peine de cinquante coups de
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     ptrdiff_t c =
                std::count_if(begin(),end(),
                    std::bind2nd(
                        std::bind1st(std::equal_to<sens>(),
                                     categorie
                        ),
                        std::mem_fun(&coMsg::GetCat)
                    )
                );
    bind1st prend un foncteur/fonction à 2 arguments et retourne et un foncteur à un argument.
    bind2nd prend un foncteur/fonction à 2 arguments et retourne et un foncteur à un argument.

    Il s'en suite que std::bind2nd(std::bind1st... ne peut pas fonctionner car le retour de bind1st, en étant une fonction n'acceptant qu'un seul argument, n'est pas compatible avec bind2nd dont le premier argument est un foncteur qui en prend 2...

    Tu veux obtenir cette expression : std::equal_to<sens>()(cat,XXXXXX->GetCat())(it);avec XXXXX remplacé par le paramètre itDécomposons-là.
    XXXXXX->GetCat() : là il s'agit d'obtenir un foncteur qui s'appelle avec un message et retourne le résultat de GetCat sur celui-ci. C'est exactement le but de mem_fun. Cette expression s'écrit donc :
    std::mem_fun(&message::GetCat).

    L'autre partie de l'expression vise à pouvoir écrire qqchose(c) qui soit équivalent à std::equal_to<sens>()(car,c). C'est bien là que tu as besoin de std::bind1st pour réduire le foncteur à 2 arguments en foncteur à 1 argument :
    std::bind1st(std::equal_to<sens>(),cat).

    Si on reconstruit l'expression, on veut passer en argument la première expression à la seconde : std::bind1st(std::equal_to<sens>(),cat)(std::mem_fun(&message::GetCat)(__))(it); où ___ serait remplacé par it. Ben, il manque quelque chose dans la STL pour transporter cette 'placeholder'.

    Il faut attendre C++11 et l'intégration de Boost.Bind pour pouvoir écrire à l'aide de foncteur ton expression :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
       std::count_if(
          m.begin(),m.end(),
          std::bind(std::equal_to<sens>(),cat,
             std::bind(&message::GetCat,std::placeholders::_1)
             )
       );

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. utiliser les tag [MFC] [Win32] [.NET] [C++/CLI]
    Par hiko-seijuro dans le forum Visual C++
    Réponses: 8
    Dernier message: 08/06/2005, 15h57
  2. Réponses: 4
    Dernier message: 05/06/2002, 14h35
  3. utilisation du meta type ANY
    Par Anonymous dans le forum CORBA
    Réponses: 1
    Dernier message: 15/04/2002, 12h36
  4. [BCB5] Utilisation des Ressources (.res)
    Par Vince78 dans le forum C++Builder
    Réponses: 2
    Dernier message: 04/04/2002, 16h01
  5. Réponses: 2
    Dernier message: 20/03/2002, 23h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo