Publicité
+ Répondre à la discussion
Page 1 sur 2 12 DernièreDernière
Affichage des résultats 1 à 20 sur 38
  1. #1
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 088
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 088
    Points : 5 128
    Points
    5 128

    Par défaut comment transformer un foncteur en lambda non nommée?

    Bonjour,

    Prenons par exemple le problème suivant: nous voulons remplir un tableau d'entier avec des nombres aléatoires compris entre 0 et un nombre donné. Prenons donc le code suivant qui fais ce qui est demandé:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <algorithm>
     
    // foncteur qui génère des nombre aléatoires compris entre 0 et mod
    struct RandMod
    {
    	RandMod( int mod ) : mod_(mod) {}
    	int operator()() { return ( (int) rand() ) % mod_; }
     
    private: 
    	int mod_;
    };
     
    main()
    {
    	int* buffer = new int[10];
    	std::generate( buffer, buffer + 10, RandMod( 1000 ) );
    }
    Ok, le code ci-dessus fonctionne, mais comme je veux être à la mode, je veux utiliser des lambdas. Comment faire pour faire la même chose sans foncteur mais avec un lambda à la place?

  2. #2
    Membre Expert

    Inscrit en
    mai 2008
    Messages
    1 007
    Détails du profil
    Informations forums :
    Inscription : mai 2008
    Messages : 1 007
    Points : 1 958
    Points
    1 958

    Par défaut

    Bonsoir,

    Comme ça :

    Code :
    1
    2
    3
    4
    5
    main()
    {
    	int* buffer = new int[10];
    	std::generate( buffer, buffer + 10, []{ return rand() % 1000; });
    }

  3. #3
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 960
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 4 960
    Points : 11 257
    Points
    11 257

    Par défaut

    Il manque la gestion du max :

    Code :
    1
    2
    3
    4
    5
    6
    main()
    {
    	int* buffer = new int[10];
    	int mod = 123;
    	std::generate( buffer, buffer + 10, [=]{ return rand() % mod; });
    }
    (à noter la présence du = qui indique que tous les éléments capturés le seront par copie, comme c'était le cas dans ton foncteur).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

  4. #4
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    En terme de bonne pratique d'écriture, il vaut mieux indiquer explicitement les variables capturées ou pas ?
    Code :
    1
    2
    3
    4
    //: capture uniquement la variable mod par copie
    std::generate( buffer, buffer + 10, [=mod]{ return rand() % mod; });
    // capture tout par copie
    std::generate( buffer, buffer + 10, [=]{ return rand() % mod; });
    Edit : un peu de lecture sur les lambdas : http://cpp.developpez.com/gbdivers/cpp11/#LIV-A

  5. #5
    Membre émérite
    Inscrit en
    décembre 2008
    Messages
    532
    Détails du profil
    Informations forums :
    Inscription : décembre 2008
    Messages : 532
    Points : 801
    Points
    801

    Par défaut

    Il y a aussi la possibilité d'un code un poil plus lisible :

    Code :
    1
    2
    3
    int* buffer = new int[10];
    auto RandGenerator = [] { return rand() % 1000; };
    std::generate( buffer, buffer + 10, RandGenerator );

  6. #6
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 960
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 4 960
    Points : 11 257
    Points
    11 257

    Par défaut

    Citation Envoyé par gbdivers Voir le message
    En terme de bonne pratique d'écriture, il vaut mieux indiquer explicitement les variables capturées ou pas ?
    Code :
    1
    2
    3
    4
    //: capture uniquement la variable mod par copie
    std::generate( buffer, buffer + 10, [=mod]{ return rand() % mod; });
    // capture tout par copie
    std::generate( buffer, buffer + 10, [=]{ return rand() % mod; });
    J'ai encore du mal à parler bonne pratique, car je manque encore de pratique tout court... Je pense pour ma part ne pas expliciter les captures par valeur, mais expliciter les captures par référence, ces dernières demandant à faire attention à la durée de vie des objets capturés par rapport à celle de la lambda.

    Citation Envoyé par cob59 Voir le message
    Il y a aussi la possibilité d'un code un poil plus lisible :
    Je ne suis pas convaincu que la lisibilité en soit tant améliorée. Et comme la durée de vie de la lambda est prolongée, si jamais il y a capture par référence, je préfère la première version...
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

  7. #7
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 088
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 088
    Points : 5 128
    Points
    5 128

    Par défaut

    Citation Envoyé par cob59 Voir le message
    Il y a aussi la possibilité d'un code un poil plus lisible :

    Code :
    1
    2
    3
    int* buffer = new int[10];
    auto RandGenerator = [] { return rand() % 1000; };
    std::generate( buffer, buffer + 10, RandGenerator );
    Ce code ne prend pas en compte la variable (mod).

  8. #8
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    Même correction que pour le code d'Arzar si tu as besoin de mettre mod en paramètre, il suffit de l'ajouter dans la liste de capture

  9. #9
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 088
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 088
    Points : 5 128
    Points
    5 128

    Par défaut

    Ok. Merci à tous.

  10. #10
    Membre émérite
    Inscrit en
    décembre 2008
    Messages
    532
    Détails du profil
    Informations forums :
    Inscription : décembre 2008
    Messages : 532
    Points : 801
    Points
    801

    Par défaut

    Citation Envoyé par JolyLoic Voir le message
    Je ne suis pas convaincu que la lisibilité en soit tant améliorée. Et comme la durée de vie de la lambda est prolongée, si jamais il y a capture par référence, je préfère la première version...
    Il y a des exemples où une référence capturée par une lambda aurait une durée de vie moindre que la lambda elle-même ?
    A la rigueur, on peut se demander ce qui se passerait si la fonction retournait une copie de RandGenerator (possible?) mais autrement...

    Je suis le seul à trouver indigestes ces lambdas anonymes utilisées en "rvalue" ? Admettons, il nous faut étoffer la lambda :
    Code :
    1
    2
    3
    4
    5
    std::generate( buffer, buffer + 10, [=] {
    // code ici
    // code ici
    return rand() % mod;
    });
    Franchement, une instruction entrecoupée par une bloc {} ça ne vous parait pas hideux ?
    En plus un objet RandGenerator aurait l'avantage «d'auto-commenter» l’objectif de la lambda.

  11. #11
    Expert Confirmé Sénior
    Homme Profil pro Pierre
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    1 938
    Détails du profil
    Informations personnelles :
    Nom : Homme Pierre
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 1 938
    Points : 4 092
    Points
    4 092

    Par défaut

    Parce que justement, les lambda sont faites pour les microscopiques bouts de codes, pas pour une grande fonction.

    Pour ca, on a une fonctionnalité extremement ancienne: les fonctions.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • La plus sotte des questions est celle qu'on ne pose pas.

    Pour faire des graphes, essayez yEd.

  12. #12
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 688
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 688
    Points : 15 747
    Points
    15 747

    Par défaut

    Salut,

    Je ne vais pas proposer de réponse au problème de base, mais je voudrais quand même émettre (en fait, répéter, car je l'ai déjà émis dans une autre discussion ) mon avis au sujet des lambda expressions:

    De manière générale, je ne suis déjà pas certain qu'il soit vraiment intéressant de vouloir refaire quelque chose qui fonctionne et qui reste malgré tout conceptuellement correct simplement pour "faire moderne".

    Autant je suis convaincu de l'utilité de remplacer un pointeur de fonction par un foncteur, autant je reste plus que mitigé quand à l'idée de remplacer un foncteur par une lambda expression

    Concernant les lambda en elles-même, mon ressenti (plus que personnel, mais que je partage ) est qu'il s'agit d'une chose intéressante, tant que:
    • Soit, elle se trouve dans une fonction qui ne fait rien d'autre que d'utiliser la lambda, autrement, on en arrive "facilement" à répéter cette lambda partout dans le code (avec tout ce que cela suppose en terme de maintenance)
    • Soit elle est utilisée dans le cadre d'un POC pour s'assurer de la faisabilité d'une chose sans commencer à sortir toute "l'artillerie lourde" qui serait nécessaire si on ne l'utilisait pas
    Le premier cas m'est inspiré par ce que je considère comme l'impérieuse nécessité de respecter le SRP et, dans une moindre mesure, par l'intérêt que je porte au fameux principe d'Xp: DRY.

    Si, pour faire moderne, tu commences à placer des lambda partout, tu risque fort de te retrouver très facilement dans une situation dans laquelle tu te retrouveras à avoir trois, quatre ou N fois exactement la même lambda à des endroits différent du code.

    Si, pour une raison ou une autre, tu en viens à devoir la modifier, il est fort probable que tu doives le faire à ces N endroits, avec un risque d'autant plus accru d'en oublier un que N sera important

    Pour le deuxième cas, cela ne me pose pas vraiment de problème, tant qu'il est clairement entendu qu'un POC est un code jetable, qui devra de toutes manières être réécrit une fois que l'on a la preuve de la faisabilité de la chose.

    Encore faut il que cette règle du "code jetable" soit respectée , mais ca, c'est une politique à mettre en place au niveau du projet

    De manière générale, je crois personnellement que les lambda sont une bonne chose, tant que l'on n'oublie pas que c'est comme pour tout le reste: il ne suffit pas de se dire qu' "on peut les utiliser" pour qu'il soit opportun de les utiliser, et il ne faut certainement pas se contenter de la seule raison de "faire moderne" pour décider de le faire
    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

  13. #13
    Membre émérite
    Inscrit en
    décembre 2008
    Messages
    532
    Détails du profil
    Informations forums :
    Inscription : décembre 2008
    Messages : 532
    Points : 801
    Points
    801

    Par défaut

    Pour moi l'intérêt des lambda est de proposer une alternative au fait de polluer l'API d'une classe (ou une entête de fonctions) par la définition d'un foncteur d'une utilité très spécifique du genre supprimerLesVoyelles. Ça ne veut pas forcément dire que la lambda tient sur une ligne (surtout si on se limite à 80 colonnes de code).

  14. #14
    Inactif


    Homme Profil pro Guillaume Belz
    Biochimiste
    Inscrit en
    novembre 2008
    Messages
    5 318
    Détails du profil
    Informations personnelles :
    Nom : Homme Guillaume Belz
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Biochimiste
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 318
    Points : 17 319
    Points
    17 319

    Par défaut

    Quelle idée de vouloir mettre les foncteurs dans l'API aussi
    A priori, la place d'un foncteur est dans un .cpp ou, s'il est utilisé plusieurs fois, dans son propre .h. Donc pas de raison qu'il pollue une API.

    Concernant l'utilisation des lambdas nommées et les lambdas de plusieurs lignes, j'ai des doutes aussi. Pour rappel, le C++11 permet d'utiliser des types locaux dans des templates, donc il faut comparer :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // version avec foncteur
    void f(vector<X>& v)
    {
        struct Less {
            bool operator()(const X& a, const X& b) { return a.v < b.v; }
        };
     
        sort(v.begin(), v.end(), Less());
    }
     
    // version avec lambda
    void f(vector<X>& v)
    {
        auto Less = [] (const X& a, const X& b) { return a.v < b.v; }
        sort(v.begin(), v.end(), Less());
    }
    Bref, pour le moment, je suis dubitatif, aussi bien sur leurs utilisations que contre. J'attend d'avoir plus de pratique

  15. #15
    Expert Confirmé Sénior
    Homme Profil pro Pierre
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    1 938
    Détails du profil
    Informations personnelles :
    Nom : Homme Pierre
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 1 938
    Points : 4 092
    Points
    4 092

    Par défaut

    Un bon lambda, à mon humble avis, ne devrait faire qu'une instruction, voire deux.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • La plus sotte des questions est celle qu'on ne pose pas.

    Pour faire des graphes, essayez yEd.

  16. #16
    r0d
    r0d est déconnecté
    Expert Confirmé Sénior

    Profil pro
    Inscrit en
    août 2004
    Messages
    4 088
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2004
    Messages : 4 088
    Points : 5 128
    Points
    5 128

    Par défaut

    Je découvre quasiment les lambdas (je faisait essentiellement du c# depuis que le dernier standard a commencé à être implémenté dans les compilateurs), et à première vue, j'ai l'impression que ça peut tout de même résoudre un problème que j'avais régulièrement, qui peut se résumer en une question: "où est-ce que je met mes foncteurs?".

    Etant un utilisateur acharné de <algorithm> (je commence à me repentir mais c'est une autre histoire), je me retrouvais vite avec de nombreux foncteurs qui polluaient mes zolis en-têtes. Donc oui, dans le cas d'une opération très simple (comme par l'exemple du premier message), je trouve que les lambdas résolvent un problème existant.

    Il reste également une piste à regarder: les lambdas, les foncteurs, les pointeurs de fonction, et stl::function (anciennement boost:function) sont interchangeables. Au moins dans les paramètres de fonctions. Peut-être cette propriété ouvre-t-elle de nouveaux horizons?

  17. #17
    Membre émérite
    Inscrit en
    décembre 2008
    Messages
    532
    Détails du profil
    Informations forums :
    Inscription : décembre 2008
    Messages : 532
    Points : 801
    Points
    801

    Par défaut

    Citation Envoyé par gbdivers Voir le message
    Quelle idée de vouloir mettre les foncteurs dans l'API aussi
    A priori, la place d'un foncteur est dans un .cpp ou, s'il est utilisé plusieurs fois, dans son propre .h. Donc pas de raison qu'il pollue une API.
    C'est vrai, je reconnais. C'est d'ailleurs ce que j'avais l'habitude de faire en C++03 : des fonctions static au début de mon .cpp. Reste que dans une logique de "déclarez aussi localement que possible" je trouve les lambda plus abouties.

  18. #18
    Expert Confirmé Sénior
    Homme Profil pro Pierre
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    1 938
    Détails du profil
    Informations personnelles :
    Nom : Homme Pierre
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2007
    Messages : 1 938
    Points : 4 092
    Points
    4 092

    Par défaut

    Le pointeur de fonction est une source de variabilité.
    Code :
    1
    2
    3
    void postfix_xml(std::string& s) {
        s.append("xml");
    };
    Le foncteur rempli un besoin simple, généraliser le pointeur de fonction, pour avoir de la réutilisation paramétrisé.
    Code :
    1
    2
    3
    4
    5
    6
    7
    class postfixer{
    public:
        void operator()(std::string& s) const {s.append(postfix);}
        postfixer(const std::string& s):postfix(s){}
    private:
        const std::string& postfix;
    };
    La définition d'un lambda n'est pas déportée ailleurs dans le code. Donc, tant qu'il est petit, il peut améliorer la lisibilité.

    Pour ce que j'en vois, std::function permet surtout de prendre un lambda en argument.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • La plus sotte des questions est celle qu'on ne pose pas.

    Pour faire des graphes, essayez yEd.

  19. #19
    Modérateur
    Avatar de koala01
    Profil pro Philippe Dunski
    Inscrit en
    octobre 2004
    Messages
    9 688
    Détails du profil
    Informations personnelles :
    Nom : Philippe Dunski
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 688
    Points : 15 747
    Points
    15 747

    Par défaut

    En fait, moi, ce qui me gène dans les lambdas, c'est leur "non réutilisabilité", ou, si vous préférez, le fait de devoir en copier le code si, d'aventure, nous avons besoin de la même fonctionnalité à différents endroits

    Je ne suis pas contre le principe lorsqu'il s'agit d'implémenter un besoin bien spécifique, dans une fonction qui l'est tout autant, mais ce qui m'inquiète au plus haut point, c'est le risque d'utilisation abusive simplement "parce qu'on peut le faire", tout comme je serai toujours opposé au fait d'utiliser la récursivité là où une simple boucle ferait tout aussi bien l'affaire.

    C'est pour cela que je parlais de deux cas qui, AMHA, peuvent justifier l'utilisation des lambda, tout en me réservant, il est vrai, le droit d'en rajouter au fur et à mesure de l'expérience que j'ai des lambda.

    Mais, dans l'absolu, je considère qu'il ne faut pas les utiliser uniquement "pour faire moderne" ou "parce qu'on peut le faire"
    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

  20. #20
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 960
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 4 960
    Points : 11 257
    Points
    11 257

    Par défaut

    Je pense que le problème de la répétition est un faux problème. Enfin, disons surtout que ce n'est pas un problème lié aux lambdas.

    Il y a du code réutilisable, et il y a du code qui ne l'est pas. Il y a du code à exécution directe, et du code à exécution déportée. Et les 4 combinaisons de ces deux critères ont de l'intérêt, mais sans les lambdas, on manque d'outils pour une des combinaisons (non réutilisable et déporté).

    Imaginons une boucle for, si son contenu est réutilisable, on en fait une fonction, sinon on met directement le contenu dans la boucle.
    Imaginons maintenant un parallel_for, si son contenu est réutilisable, on en fait un foncteur nommé, sinon, on en fait une lambda.

    Après, certains vont ne pas bien respecter le critère réutilisabilité, ou ne pas être d'accord sur la frontière, mais les lambdas n'introduisent pas un nouveau problème, juste une déclinaison d'un problème ancien.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •