Partage
  • Partager sur Facebook
  • Partager sur Twitter

[exo-gbdivers]Intervalles

    17 octobre 2015 à 17:26:19

    Yop, yop, comment se passe votre week-end ? :)

    Alors voilà, je suis actuellement l'exercice intervalle du cours de @gbdivers, et je viens de finir la partie 2 qui est la gestion d'union d'intervalle. Voici ce que permet de faire ma classe intervalle :

    • Intersection entre 2 intervalles
    • Union entre 2 intervalles
    • Affiche le minimum et le maximum d'une intervalle si il n'y a eu aucune union disjoint.
    • Stockage dans un vector
    • Construction par 2 valeurs + assertion si jamais ce n'est pas une intervalle correcte.
    • Vérifie si une valeur est bien dans une intervalle.

    Voici le code sur @github, je voulais savoir avant de passer via le stockage par std::set si jamais c'était propre ou si il y avait des soucis dans l'algo de mes tests unitaires :).

    Cordialement,

    -
    Edité par Soin... 17 octobre 2015 à 18:18:11

    • Partager sur Facebook
    • Partager sur Twitter
      17 octobre 2015 à 18:55:51

      Salut,

      • Utilise les listes d'initialisation. Le resize(0) n'a aucune raison d'être vu que le vector est par défaut construit vide.
      • Je ne vois rien qui ne nécessite <ios>
      • return m_inter[m_inter.size() - 1]; => return m_inter.back();. front() à l'inverse.
      • que se passe-t-il si je fais une intersection ou union d'un interval construit par défaut ? Ou demande le min()/max() ?
      • intersection des intervalles [0, 5[ et [6, 7[ = ?
      • union de [0, 5[ et [5, 7[ = ?
      • pour ces deux fonctions j'aurais plus vu des surcharges d'opérateurs, qui ne modifient pas l'instance this et renvoient le nouvel intervalle.
      • Partager sur Facebook
      • Partager sur Twitter
        17 octobre 2015 à 20:43:29

        Yop,

        Merci de tes conseils. Cependant je reviens vers toi pour le dernier que tu m'as filé :

        Squall31 a écrit:

        Salut,

        • pour ces deux fonctions j'aurais plus vu des surcharges d'opérateurs, qui ne modifient pas l'instance this et renvoient le nouvel intervalle.


        Dans ce cas, lequel ? Une intersection n'est pas une opération, pareil pour une union. Donc je ne savais pas vraiment lequel utiliser (je pensais "+" pour union, mais aucune idée pour l'instersection).

        PS : j'ai test une union entre [0;5[ et [5;7[, ça fonctionne correctement personnellement :) si on regarde bien, cette union est impossible.

        Cordialement,

        -
        Edité par Soin... 17 octobre 2015 à 21:21:25

        • Partager sur Facebook
        • Partager sur Twitter
          17 octobre 2015 à 22:04:02

          Soin... a écrit:

          Dans ce cas, lequel ? Une intersection n'est pas une opération, pareil pour une union. Donc je ne savais pas vraiment lequel utiliser (je pensais "+" pour union, mais aucune idée pour l'intersection).

          Ok "+" pour union. Pour l'intersection, tu peux prendre "&".

          Soin... a écrit:

          PS : j'ai test une union entre [0;5[ et [5;7[, ça fonctionne correctement personnellement :) si on regarde bien, cette union est impossible.

          Et ça te donne... ? [0;5[ U [5;7[ alors que les deux se fusionnent en une seul intervalle : [0;7[

          Et... pourquoi serait-elle "impossible" ?

          • Partager sur Facebook
          • Partager sur Twitter
            17 octobre 2015 à 23:25:22

            Re,

            Tout simplement comme ceci : soit x membre de l'intervalle [0;5[, on dit qu'il est membre de l'intervalle si 0<= x < 5, donc si x = 5, x n'appartiens pas à l'intervalle [0;5[ vue que c'est une intervalle fermé/ouverte.

            Cordialement,

            • Partager sur Facebook
            • Partager sur Twitter
              17 octobre 2015 à 23:55:25

              x < 5 || 5 <= x => x plus petit, égal ou plus grand. Donc condition toujours vrai. Donc 0 <= x < 5 || x <= 5 && x < 7 = 0 <= x < 7

              • Partager sur Facebook
              • Partager sur Twitter
                18 octobre 2015 à 11:53:19

                jo_link_noir a écrit:

                x < 5 || 5 <= x => x plus petit, égal ou plus grand.
                Donc condition toujours vrai.
                Donc 0 <= x < 5 || x <= 5 && x < 7 = 0 <= x < 7


                Je l'avais pas vu comme ça, pour moi elle était pas comprise dans l'intervalle [0;5[ donc une intersection était impossible car elle commencait à 5 donc juste après le maximum de cette intervalle qui est 4.

                Cordialement,

                • Partager sur Facebook
                • Partager sur Twitter
                  18 octobre 2015 à 12:43:25

                  Pour une intersection, je suis d'accord. Mais Squall31 parle d'union.

                  Au passage, les tests devraient indiquer les erreurs (message d'erreur, abort, etc). Pas laisser l'utilisateur comparer. C'est tellement facile de faire des erreurs en comparant (et c'est tellement pénible).

                  #define TEST(a, b, op) \
                    [](auto && x, auto && y) { \
                      if (!(x op y)) { \
                        assert(! #a " " #op " " #b); \
                        std::abort(); /*si NDEBUG définit, pas de assert*/ \
                      } \
                    }(a,b)
                  #define TEST_EQUAL(a,b) TEST(a,b,==)
                  

                  -
                  Edité par jo_link_noir 18 octobre 2015 à 15:27:45

                  • Partager sur Facebook
                  • Partager sur Twitter
                    18 octobre 2015 à 13:03:32

                    jo_link_noir a écrit:

                    Au passage, les tests devraient indiquer les erreurs (message d'erreur, abort, etc). Pas laisser l'utilisateur comparer. C'est tellement facile de faire des erreurs en comparant (et c'est tellement pénible).

                    #define TEST(a, b, op) \
                      [](auto && x, auto && y) { \
                        if (!(x op b)) { \
                          assert(! #a " " #op " " #b); \
                          std::abort(); /*si NDEBUG définit, pas de assert*/ \
                        } \
                      }(a,b)
                    #define TEST_EQUAL(a,b) TEST(a,b,==)
                    


                    Même si j'ai pas du tou compris le code que tu m'as montré (fonction lambda, mais après le reste comme le if etc... est assez obscur pour le coup ^^ même le contenu de assert). Je vois l'idée :p (j'évite de faire des assert ça abort le programme alors que si ça se trouve il y a des tests encore à faire, et comme je code sous emacs c'est ultra chiant de commenter des lignes pour continuer les tests). J'assert uniquement si le comportement devient indéterminé.

                    Si tu pouvais me donner une petite précision sur ce code ce serait sympa ^^

                    Cordialement,

                    -
                    Edité par Soin... 18 octobre 2015 à 13:04:10

                    • Partager sur Facebook
                    • Partager sur Twitter
                      18 octobre 2015 à 15:26:54

                      Les macro c'est simple, elles ne font que du remplacement de texte. S'il y #define A 1,2, un code comme foo(A, 3) sera remplacer par foo(1,2 , 3). C'est la même chose pour les paramètres des macros. op sera remplacé par ==, et la condition devient !(x == y). (Note, je me suis vautré dans la condition en mettant b au lieu de y)

                      Pour le assert, le but est d'avoir la condition d'erreur affichée. #a transforme le texte en chaîne de caractère C. Donc par exemple pour TEST_EQUAL(i + 2, 3), le contenu de assert sera remplacé par "i + 2" " " "==" " " "3", équivalent de "i + 2 == 3". Deux chaînes littérales à la suite est une forme valide. Le ! devant permet de rendre la condition toujours fausse, histoire que le assert fasse son boulot. Et si assert ne fait rien, std::abort achève le programme.

                      La lambda sert à 2 choses:

                      • Devoir mettre un point virgule (d'habitude, un do {} while(0) est utilisé)
                      • Ne pas avoir d'effet de bord dans des expressions comme TEST(*++it, 3) si on ajoute l'affiche des valeurs. Sinon, *++it est exécuter dans le if ET dans l'affichage.

                      j'évite de faire des assert ça abort le programme alors que si ça se trouve il y a des tests encore à faire, et comme je code sous emacs c'est ultra chiant de commenter des lignes pour continuer les tests.

                      Tant mieux :D ! Si un test ne fonctionne pas, corrige le code, n'enlève pas le test.

                      Après je comprends très bien l'envie d’exécuter tous les tests pour avoir une meilleure idée des problèmes. Mais des tests qui ne sont pas fichus d'indiquer par eux-mêmes qu'ils échouent ne servent que d'exemples.

                      Des frameworks comme boost.unit_test propose des macros BOOST_CHECK_* et BOOST_REQUIRE_* laissant le choix de continuer ou d'arrêter. Mais dans tous les cas, il y a les messages en cas d'échec, et un code d'échec au niveau du processus.

                      De façon naïve, imiter un tel comportement n'est pas très compliqué. Les macros s'occupent de mettre dans un conteneur globale les erreurs et le main de les lister. Sans oublier de retourner un code de retour différent de 0 s'il y a des erreurs. (Perso, je ne me rends jamais compte qu'un programme échoue si le code de retour est 0.)

                      -
                      Edité par jo_link_noir 18 octobre 2015 à 15:31:03

                      • Partager sur Facebook
                      • Partager sur Twitter
                        18 octobre 2015 à 17:00:41

                        Merci de tes conseils, mais cet exercices m'a un peut "gavé", je pense pas le reprendre de si-tôt. A vraie dire c'est vraiment la partie avec std::set qui m'a gonflé ^^ ce conteneur est vraiment super restrictif dans ce qu'il fait et ça me plait moyen.

                        Voilà le code disponnible sur @github, je le trouve moi-même extrêmement dégueulasse, mais dans certains moments vue que set ne stocke pas les doublons, qu'il stocke la taille du conteneur à son itérateur end, je ne vois pas comment faire autrement (cf intersection + operator<<).

                        Je passe le sujet en résolu :)

                        Cordialement,

                        -
                        Edité par Soin... 18 octobre 2015 à 17:02:50

                        • Partager sur Facebook
                        • Partager sur Twitter
                          18 octobre 2015 à 19:12:22

                          En relisant l'énonce, il y a un point qui ne semble pas claire (j'ai modifié) : chaque partie est indépendante et correspond à une classe différente.

                          Du coup, je ne sais pas très bien à quelle partie correspond ton code.

                          Il y a un autre point que tu n'as pas compris (si ton code correspond à la partie 2)

                          gbdivers a écrit:

                          utiliser un conteneur pouvant recevoir plusieurs Intervalle définie précédemment.

                          Donc l'idée était de réutiliser la classe créée dans la partie 1 (intervalle simple) pour créer en ensemble d'intervalles. En gros le code ressemble à quelque chose comme cela :

                          // partie 1
                          class Interval1 {
                              ...
                          private:
                              T low_value{};
                              T hight_value{};
                          };
                          
                          // partie 2
                          
                          class Inteval2 {
                              ...
                          private:
                              std::vector<Inteval1> m_intervals{};
                          };
                          
                          // partie 2 bis
                          
                          class Inteval2 {
                              ...
                          private:
                              std::set<Inteval1> m_intervals{};
                          };

                          Dans ton code, tu as fait un set<T>, ce qui complique énormément les choses en fait :)

                          HS : pourquoi tes fonctions sont template et n'utilises par le tempate de la classe ?

                          Une remarque plus générale. Le but de ce genre d'exercice n'est pas simplement de trouver une solution qui marche, mais aussi trouver une solution "élégante", qui est simple et efficace. Et d'apprendre une démarche de réflexion pour résoudre un problème.

                          En particulier, il faut apprendre à découper un problème complexe en petits problèmes simples. C'est pour cela que je découpe les exos en sous parties, pour que la progression soit plus régulière. Mais il faut quand même faire cet effort de découpage des problèmes.

                          Ici, je vois plusieurs sous-problèmes/questions au problème principal : comment ordonner des intervalles ? (pour les mettres dans un set). Comment trouver l'intersection ? Comment trouver l'union ? Comment "fusionner" des intervalles ?

                          Toutes ces problématiques devraient apparaître clairement dans ton code. Et la façon la plus simple que cela apparaisse serait d'avoir des fonctions distinctes. C'est pour cela que l'on dit souvent qu'une fonction ne doit pas faire plus de X lignes (X= 50 ? 100 ?). Si c'est trop long, c'est que le problème est probablement mal découpé en sous problèmes. Et quand je vois le code de ton intersection, cela me parait complexe.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            18 octobre 2015 à 19:31:06

                            Soin... a écrit:

                            ce conteneur est vraiment super restrictif dans ce qu'il fait et ça me plait moyen.

                            Oui, on peut dire qu'un marteau est très restrictif, il ne permet pas de visser...

                            Ou alors, on peut dire qu'un outil n'est pas plus ou moins restrictif, il est conçu pour faire un boulot et pas un autre.

                            Je "propose" (on n'est pas à l'école, si tu utilises un vector à la place du set conseillé dans l'exo, tu ne vas pas avoir une mauvaise note. Mais ca serait bien de pouvoir discuter de ce choix) un set dans l'exo, je fais cela parce que je pense que c'est adapté.

                            Mais si on te propose un marteau comme outil à utiliser, ne choisit pas les vis comme solution. Ca ne sera pas la faute du marteau d'être "restrictif" dans ce cas ;)

                            -
                            Edité par gbdivers 18 octobre 2015 à 19:31:34

                            • Partager sur Facebook
                            • Partager sur Twitter
                              18 octobre 2015 à 19:58:36

                              Yop @gbdivers merci de ta réponse,

                              En effet je suis juste stupide ^^ je vais reprendre cet exercice et le refaire bien correctement ;) on est pas à l'école, mais un sujet reste un sujet. Je m'y remet de suite et je vais tout re découper et revoir toute la conception ^^ je vous ferai un retour prochainement sur celui-ci.

                              En effet je me suis également dit que le code de mon intersection était beaucoup trop long, mais si tu regarde bien, il y a beaucoup de déclaration de variable qui font que ce code est long (pas d'overload d'opérateur [] du coup c'est problèmatique). Néanmoins j'aimerai avoir une petite explication sur std::set si c'était possible ce serait gentil de ta part.

                              std::set tri en fonction d'un argument template (soit de base il est concidéré comme un tri croissant). Je suis d'accord sur le fait qu'il puisse trier un type primitif comme un int, float, double, char. Par contre pour une classe ? Je ne vois pas vraiment comment (j'ai pas encore fait le test, et ce n'est pas le sujet, mais j'aimerai une petite explication quand même).

                              Je me suis lancé tête baissé dedans sans réfléchir j'aurais peut-être du y aller de façon plus "pro" j'aurais peut-être tout de suite compris la subtilité (à trop vouloir faire vite, on fait n'importe quoi !).

                              Pour ce qui est de ne pas utiliser le template à la classe, à vraie dire c'est un ami qui m'a dit de faire comme ça, il m'a dit qu'une classe devait contenir uniquement des définitions, que c'était plus propre comme ça (d'un côté il a pas tort, je m'y retrouve mieux :) ).

                              HS : réagir comme j'ai réagi au dessus était totalement immature en fait veuillez m'excuser :honte:

                              Cordialement,

                              -
                              Edité par Soin... 18 octobre 2015 à 20:12:58

                              • Partager sur Facebook
                              • Partager sur Twitter
                                18 octobre 2015 à 20:14:07

                                std::set prend un comparateur en second paramètre template: set<Key, Compare = std::less<Key>>. Et std::less est un bête wrapper sur l'opérateur <. Donc en fait, std::set utilise bêtement l'opérateur "plus petit que" pour les comparaisons.

                                Du coup, il y a 3 façons de trier une clef avec std::set (et avec les autres conteneurs associatives).

                                • Définir un opérateur de comparaison
                                • Spécialisation template de std::less
                                • Donner un comparateur perso
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  18 octobre 2015 à 20:26:35

                                  Soin... a écrit:

                                  on est pas à l'école, mais un sujet reste un sujet. 

                                  Oui, mais faut pas voir cela comme une contrainte stricte à respecter, mais comme un cadre d'apprentissage. Faire des choix et les justifier fait aussi partie du boulot d'un dev.

                                  Et si une solution ne te plait pas, il faut pas bloquer dessus. Il est préférable de ne pas respecter une contrainte et avoir une solution que tu trouves élégante. Et il ne faut pas hésiter à passer à autre chose et revenir plus tard sur un exo, quand tu as les idées plus claires.

                                  Pour set, pas grand chose à dire en fait. C'est une collection dont les éléments sont triés en utilisant std::less<Key> donc < par défaut (tout cela est dans la doc). Donc pour utiliser set avec une classe, il faut fournir < dans la classe ou une fonction de tri dans set (a ma connaissance, pas de raison de faire une spécialisation de less. Si un élément est triable avec less, on peut exprimer cela directement avec <)

                                  Soin... a écrit:

                                  Pour ce qui est de ne pas utiliser le template à la classe, à vraie dire c'est un ami qui m'a dit de faire comme ça, il m'a dit qu'une classe devait contenir uniquement des définitions, que c'était plus propre comme ça

                                  Pas sur de voir le rapport entre template et définitions.

                                  Mais oui, c'est mieux avec template. Cela ne veut pas dire qu'il faut commencer par des templates. Il est parfois préférable (souvent ?) de faire une première version sans template, pour laquelle tu comprends ce qu'il se passe. Et ensuite partir sur des templates.

                                  (Souviens toi de l'exo sur les fractions. La version avec template apporter de nouvelles problématiques, pour la gestion de la strong garantie exception, que n'avait pas la version sans template. C'est mieux d'aborder les problèmes un par un)

                                  -
                                  Edité par gbdivers 18 octobre 2015 à 20:28:10

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    18 octobre 2015 à 20:45:23

                                    gbdivers a écrit:

                                    Et si une solution ne te plait pas, il faut pas bloquer dessus. Il est préférable de ne pas respecter une contrainte et avoir une solution que tu trouves élégante. Et il ne faut pas hésiter à passer à autre chose et revenir plus tard sur un exo, quand tu as les idées plus claires.

                                    Cependant ce genre de "choix" ne peut se faire uniquement si tu connais bien le langage, j'en suis à 2 mois de programmations, donc je pense pas vraiment avoir les bons arguments pour discuter le choix. Je serai tenter d'utiliser vector car je le connais et me fait gagner du temps, mais au final, si je ne sais pas me servir des autres conteneurs c'est ridicule ^^

                                    gbdivers a écrit:

                                    Pas sur de voir le rapport entre template et définitions.

                                    J'avais juste pas compris ce que tu m'as dit my bad :)

                                    gbdivers a écrit:

                                    Mais oui, c'est mieux avec template. Cela ne veut pas dire qu'il faut commencer par des templates. Il est parfois préférable (souvent ?) de faire une première version sans template, pour laquelle tu comprends ce qu'il se passe. Et ensuite partir sur des templates.

                                    (Souviens toi de l'exo sur les fractions. La version avec template apporter de nouvelles problématiques, pour la gestion de la strong garantie exception, que n'avait pas la version sans template. C'est mieux d'aborder les problèmes un par un)

                                    -
                                    Edité par gbdivers il y a 9 minutes


                                    Disons que je pensais maîtriser assez les template pour me lancer dedans :) et c'est ce que j'ai fais à l'instant. Je pensais pas vraiment passer par une version basique sachant que j'ai quand même pas mal manipulé les templates (cf : vector / unique_ptr / Fractions) pas assez pour tout savoir, mais quand même assez pour pouvoir me débrouiller un minimum :) .

                                    Merci pour les explications sur std::set en fait c'est tout bête :p

                                    Cordialement,

                                    -
                                    Edité par Soin... 18 octobre 2015 à 20:54:30

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      18 octobre 2015 à 21:24:43

                                      Soin... a écrit:

                                      Cependant ce genre de "choix" ne peut se faire uniquement si tu connais bien le langage, j'en suis à 2 mois de programmations, donc je pense pas vraiment avoir les bons arguments pour discuter le choix. Je serai tenter d'utiliser vector car je le connais et me fait gagner du temps, mais au final, si je ne sais pas me servir des autres conteneurs c'est ridicule ^^

                                      A supposer que je n'ai pas mis des pièges dans les exos, pour vous obligez à trouver la meilleure solution par vous même... :)

                                      (bon, ce n'est pas vrai, mais on va faire comme si ça l'était)

                                      Plus sérieusement, c'est le but des exos : approfondir tes connaissances. Et quel est le problème au final si tu avais utilisé vector ? On t'aurais expliqué les avantages de set, et tu aurais testé (je n'exclus pas d'ailleurs qu'un autre conteneur pourrait aussi convenir, il ne faut pas hésiter à proposer)

                                      (D'autant plus que l'exo est en 2 parties, la première en utilisant vector, la seconde avec set. Cela montre bien qu'on peut faire avec les 2)

                                      Soin... a écrit:

                                      Disons que je pensais maîtriser assez les template pour me lancer dedans :) et c'est ce que j'ai fais à l'instant. Je pensais pas vraiment passer par une version basique sachant que j'ai quand même pas mal manipulé les templates (cf : vector / unique_ptr / Fractions) pas assez pour tout savoir, mais quand même assez pour pouvoir me débrouiller un minimum :) .

                                      C'est le cas (cela reste de la programmation générique, pas de la méta programmation, et tu te débrouilles suffisamment avec les templates pour les utiliser dans les exos).

                                      Mais on parle de démarche à suivre pour résoudre un problème. Les templates ajoutent un niveau de généricité supplémentaires, et donc de complexité. Et il est bien de faire les choses progressivement (pas uniquement pour apprendre, pour résoudre un problème en général)

                                      Un exemple simple : quand j'écris une classe, je commence souvent pas écrire les tests correspondant à un cas particulier, puis j'étends les tests en même temps que j'améliore la classe.

                                      De toute façon, il faut bien voir une chose : un "bon" code doit être évolutif. Si ton code avec template ne réutilise pas la majorité de ton code sans template, c'est qu'il y a un problème à la base.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        18 octobre 2015 à 21:42:58

                                        D'acc merci, je regarderai demain :) Merci pour tes conseils je mettrais d'abord la conception des tests puis après la conception de la classe dans mon github avec le code :)

                                        Cordialement,

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          19 octobre 2015 à 19:06:42

                                          Yop,

                                          Je viens vers vous pour une refonte totale de la classe interval. Je suis par contre directement parti sur la classe template et non sur la classe avec des integers pour commencer. J'y ai également mis une documentation avec toute la conception du projet. Voici le lien @github, comme vous le savez j'aime bien partir sur des bases seines pour continuer.

                                          Si il y a quoi que ce soit niveau conception aussi (j'y ai laissé la documentation qui fait office de conception de la classe).

                                          Cordialement,

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            20 octobre 2015 à 20:45:23

                                            Yop,

                                            Je viens de finir la partie 2 de la classe intervalle (la partie avec les vecteurs), je tenais à vous montrer ce que j'ai fais avant d'attaquer la partie avec std::set. Voici ce que permet de faire mon intervalle :

                                            • Vérifier si une valeur est dans une intervalle.
                                            • Effectuer une union entre 2 intervalles / intersections
                                            • Construire 2 intersections / unions d'intervalles directement sans passer par les opérateurs
                                            • Retourner la valeur minimal et maximal de chaque intervalle stocké
                                            • Avoir la taille du conteneur.
                                            • Afficher les intervalles.

                                            Voilà voilà, si jamais mon code est disponible dans mon @github si vous avez des avis, moi je continue ;) vous trouverez également la documentation de la première classe intervalle, la suite est juste un conteneur d'intervalle donc rien n'a vraiment changé. Mais les tests unitaires sont présents dans le dossier test. Je ferai un dernier post pour donner la version finale de la classe avec les 3 parties ;)

                                            Ce que je compte faire avant de faire std::set :

                                            • Opérateur de comparaison

                                            Cordialement,

                                            -
                                            Edité par Soin... 21 octobre 2015 à 8:38:41

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              20 octobre 2015 à 23:10:01

                                              Un truc qui m'a déjà fait tiquer: pourquoi mettre proto.hh dans le dossier include ?

                                              Et faut arrêter avec les try/catch... ils ne servent pas !

                                              • Pourquoi un conteneur de shared_ptr ?
                                              • Pourquoi final ?
                                              • Pas de version de operator[] constant.
                                              • Déclaration de variable trop tôt (interval::operator+)
                                              • found_value_min et found_value_max serait plus concis avec une ternaire: return (low_value_lhs > low_value_rhs) ? low_value_lhs : low_value_rhs;
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                21 octobre 2015 à 0:13:26

                                                Salut,

                                                Soin... a écrit:

                                                std::set tri en fonction d'un argument template (soit de base il est concidéré comme un tri croissant). Je suis d'accord sur le fait qu'il puisse trier un type primitif comme un int, float, double, char. Par contre pour une classe ? Je ne vois pas vraiment comment (j'ai pas encore fait le test, et ce n'est pas le sujet, mais j'aimerai une petite explication quand même).

                                                Par défaut, std::set utilise le foncteur std::less qui utilise "logiquement" l'opérateur <.

                                                Mais, si tu ne vois pas comment arriver à comparer deux intervalles, demande toi comment tu ferais pour arriver à déterminer qu'une variable dont le  type Date ressemblerait à

                                                struct Date{
                                                    unsigned int jour;
                                                    unsigned int mois;
                                                    int annee
                                                };

                                                est "plus petite" (comprends : antérieure) à une autre variable du même type.

                                                Une fois que tu auras trouvé la solution pour pouvoir comparer deux dates de la sorte, tu devrais pouvoir déterminer quelle logique "globale" doit être suivie et il se fait que cette logique "globale" restera la même pour toute classe proposant une sémantique de valeur (condition sine qua non) pour laquelle il est cohérent d'envisager la comparaison avec l'opérateur <, bien qu'elle doive certainement être adaptée à chaque situation ;)

                                                Après, tu peux utiliser cette logique de différentes manières avec un std::set :

                                                • En implémentant un opérateur < prenant deux intervalles en paramètres
                                                • En mettant en oeuvre un foncteur (pardon : un prédicat) qui s'occupe d'effectuer la comparaison (et en fournissant ce prédicat comme deuxième paramètre template à ton std::set)
                                                • y en a surement d'autre ;)
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                                  21 octobre 2015 à 8:49:33

                                                  Yop,

                                                  Merci de vos retours, j'aimerai néanmoins revenir sur certains points que vous avez relevés comme :

                                                  jo_link_noir a écrit:

                                                  Un truc qui m'a déjà fait tiquer: pourquoi mettre proto.hh dans le dossier include ?

                                                  Et faut arrêter avec les try/catch... ils ne servent pas !

                                                  Je pense que tu dois parler de mon constructeur de interval_content ? Si c'est le cas c'est parce que vu que je ne peux pas compiler avec mon compilo en c++14 je suis obligé de faire des new, et non utiliser shared_unique, or j'ai pris l'habitude que lorsque je fais des new, j'utilise un try...catch.

                                                  Si d’ailleurs t'avais la solution pour mettre à jour g++ sous Kubuntu ce serait cool :p (j'ai essayé un sudo apt-get update mais sans succès).

                                                  jo_link_noir a écrit:

                                                  • Pourquoi final ?

                                                  Car la classe ne contient pas d'héritage ^^

                                                  jo_link_noir a écrit:

                                                  • found_value_min et found_value_max serait plus concis avec une ternaire: return (low_value_lhs > low_value_rhs) ? low_value_lhs : low_value_rhs;

                                                   Ouais c'est vraie, j'avais un peut zappé cette partie de la programmation, faut dire que j'apprécie pas des masses le ternaires car si quelqu'un relis mon code et qu'il ne sait pas ce que c'est, ça devient vite moche/incompréhensible.

                                                  jo_link_noir a écrit:

                                                  • Déclaration de variable trop tôt (interval::operator+)

                                                  Là j'ai pas très bien compris, dans tous les cas, je n'ai qu'un scope, après c'est vraie que j'aurais pu les déclarer au moment ou je récupère le retour de found_value_min et found_value_max, mais fondamentalement c'est pas bien gênant ^^

                                                  Merci beaucoup de vos retours, je prends note et je vais corriger tout ça ;)

                                                  koala01 a écrit:

                                                  Salut,

                                                  • En mettant en oeuvre un foncteur (pardon : un prédicat) qui s'occupe d'effectuer la comparaison (et en fournissant ce prédicat comme deuxième paramètre template à ton std::set)

                                                  Mais au final, ce prédicat va prendre en paramètre une intervalle et va utiliser l'operateur < donc, ça revient au même d'utiliser directement l'opérateur < nan ? En plus si je comprends bien le fonctionnement j'aurais pas besoin de modifier le std::less de base de std::set.

                                                  Cordialement,



                                                  -
                                                  Edité par Soin... 21 octobre 2015 à 8:50:21

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    21 octobre 2015 à 13:53:24

                                                    Soin... a écrit:

                                                    je suis obligé de faire des new, et non utiliser shared_unique

                                                    ??? make_shared existe en C++11. Si on parle de make_unique: tu peux le recoder. Mais la question se porte plus sur: pourquoi un pointeur ?

                                                    Soin... a écrit:

                                                    or j'ai pris l'habitude que lorsque je fais des new, j'utilise un try...catch.

                                                    Alors fait le autour de make_unique, make_shared, vector::push_back et autres fonctions qui font de l'allocation. Un catch qui ne fait que relancer l'exception ne sert à rien.

                                                    Soin... a écrit:

                                                    Si d’ailleurs t'avais la solution pour mettre à jour g++ sous Kubuntu ce serait cool :p (j'ai essayé un sudo apt-get update mais sans succès).

                                                    Quel version de Kubuntu ? Avec la 14.10 j'ai g++4.9.1 et un g++-5.0 expérimental. Et clang++-3.5. Tous supportent c++14 (en partie, avec des bugs chelous pour gcc). sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y sudo apt-get update sudo apt-get install gcc-snapshot g++-4.9 echo 'alias g++-5=/usr/lib/gcc-snapshot/bin/g++ >> ~/.bash_rc

                                                    Soin... a écrit:

                                                    c'est vraie que j'aurais pu les déclarer au moment ou je récupère le retour de found_value_min et found_value_max, mais fondamentalement c'est pas bien gênant ^^

                                                    Si :D. Cela m'a donné l'impression qu'assert en avait besoin, mais en fait non...

                                                    Soin... a écrit:

                                                    Mais au final, ce prédicat va prendre en paramètre une intervalle et va utiliser l'operateur < donc, ça revient au même d'utiliser directement l'opérateur < nan ?

                                                    Non, car tu es totalement libre de l'implémentation du prédicat. Comme ne comparer que la partie gauche, utiliser >, <=, etc.

                                                    -
                                                    Edité par jo_link_noir 21 octobre 2015 à 13:54:20

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      21 octobre 2015 à 14:08:08

                                                      jo_link_noir a écrit:

                                                      Soin... a écrit:

                                                      je suis obligé de faire des new, et non utiliser shared_unique

                                                      ??? make_shared existe en C++11. Si on parle de make_unique: tu peux le recoder.
                                                      Mais la question se porte plus sur: pourquoi un pointeur ?

                                                      Ah j'ai pas lu la doc correctement alors, j'ai fais un pointeur pour éviter de faire des copies, en fait c'est plus un mécanisme que j'ai pris quand je fais un conteneur de classe je fait en pointeur pour éviter de faire des copies.

                                                      jo_link_noir a écrit:

                                                      Soin... a écrit:

                                                      or j'ai pris l'habitude que lorsque je fais des new, j'utilise un try...catch.

                                                      Alors fait le autour de make_unique, make_shared, vector::push_back et autres fonctions qui font de l'allocation.
                                                      Un catch qui ne fait que relancer l'exception ne sert à rien.

                                                      Edité par jo_link_noir il y a 6 minutes

                                                      Le truc c'est que je ne savais pas vraiment trop quoi faire sur ce catch .... Si jamais le try échoué, ça veut dire que new a échoué, et donc que le programme s'arrêterai, mais au final toutes les ressources serait détruite vu qu'il y aurait appel du destructeur non ? Mais c'est vrai que je trouvais ça bizarre aussi ^^

                                                      jo_link_noir a écrit:

                                                      Si :D.

                                                      Cela m'a donné l'impression qu'assert en avait besoin, mais en fait non...

                                                      Je m'en suis rendu compte qu'une fois le message posté en fait ^^ .

                                                      jo_link_noir a écrit:

                                                      Soin... a écrit:

                                                      Mais au final, ce prédicat va prendre en paramètre une intervalle et va utiliser l'operateur < donc, ça revient au même d'utiliser directement l'opérateur < nan ?

                                                      Non, car tu es totalement libre de l'implémentation du prédicat. Comme ne comparer que la partie gauche, utiliser >, <=, etc.

                                                      -
                                                      Edité par jo_link_noir il y a 6 minutes

                                                      Là je suis d'accord, mais au final, définir un simple opérateur de comparaison < me semble plus judicieux, et donc ne pas toucher à ce paramètre finalement.

                                                      Merci pour les commandes ;) mais je ne sais plus quelle version de kubuntu j'utilise ^^ je regarderai ça ce soir ;)

                                                      Cordialement,

                                                      -
                                                      Edité par Soin... 21 octobre 2015 à 14:09:10

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        21 octobre 2015 à 14:14:41

                                                        Soin... a écrit:

                                                        Mais au final, ce prédicat va prendre en paramètre une intervalle et va utiliser l'operateur < donc, ça revient au même d'utiliser directement l'opérateur < nan ? En plus si je comprends bien le fonctionnement j'aurais pas besoin de modifier le std::less de base de std::set.

                                                        Pas forcément...  Un prédicat est un foncteur qui renvoie true ou false.   Mais rien ne dit que tu vas forcément utiliser l'opérateur < sur les objets complets qui sont transmis à ton prédicat, ni même que cet opérateur existera ;)

                                                        Après tout, tu peux avoir une classe ou une structure dont l'interface ressemble à quelque chose comme

                                                        class MaClass{
                                                            /* ... */
                                                        };
                                                        bool operator ==(MaClass const & a, MaClass const & b){
                                                            /* ... */
                                                        }
                                                        bool operator < (MaClass const & a, MaClass const & b){
                                                        
                                                        }
                                                        bool operator != (MaClass const & a, MaClass const & b){
                                                            return !(a==b);
                                                        }
                                                        bool operator <= (MaClass const & a, MaClass const & b){
                                                            return (a==b) || (a<b);
                                                        }
                                                        bool operator >(MaClass const & a, MaClass const & b){
                                                            return !((a==b)|| (a <b));
                                                        }
                                                        bool operator >=(MaClass const & a, MaClass ocnst & b){
                                                            return !(a<b);
                                                        }

                                                        qui pourrait s'avérer cohérente pour toute classe pour laquelle il est cohérent de vérifier si un objet est "plus petit" qu'un autre, mais tu pourrais aussi avoir une classe pour laquelle il n'y a (et encore!) que les opérateurs == et != qui sont définis  mais pour lesquels, dans certaines circonstances, un prédicat (quel qu'il soit) pourrait s'avérer intéressant, sous une forme proche de

                                                        class MaClass{
                                                            /* ... */
                                                        };
                                                        /* ces deux opérateurs peuvent ne pas exister */
                                                        bool operator == (MaClass const & a, MaClass const & b){
                                                            /* ... */
                                                        }
                                                        bool operator != (MaClass const & a, MaClass const & b){
                                                            return !(a==b);
                                                        }
                                                        
                                                        class Holder{
                                                           struct lesByXXX{
                                                                bool operator ()(MaClass const & a, MaClass const & b) const{
                                                                    /* .... */
                                                                }
                                                           };
                                                           using collection_t = std::set<MaClass, lessByXX>;
                                                           /*  */
                                                        };

                                                        A la limite, MaClass pourrait tout aussi bien avoir sémantique d'entité, cela te permettrait de la trier selon l'un de ses états particuliers ;)


                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                                                          21 octobre 2015 à 14:25:04

                                                          Je crois avoir saisi un peut la chose, en fait ce que j'avais un peut de mal à comprendre c'est :

                                                          • La définition d'un std::set est std::set<T, std::less<Key>>, le std::less<Key> on m'a dit que c'était juste un wrapper sur l'opérateur <, mais pour moi ce conteneur devais Impérativement utiliser std::less<Key>.

                                                          Mais en fait non, il peut également utiliser une classe au hasard qui utiliserait un prédicat, mais dans ce cas, comment saurait-il que c'est le prédicat qu'il faut appeler ? sachant qu'on lui passe uniquement std::set<MyClass, lessByXX> ?

                                                          Note qu'il faudra aussi que je me renseigne sur les foncteurs / prédicat mais c'est une autre histoire. Par contre je ne comprends pas très bien, Pourquoi passer MaClass en sémantique d'entité vu qu'elle est dans une collection, elle peut donc être comparée non ?

                                                          Cordialement,

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            21 octobre 2015 à 14:29:26

                                                            Soin... a écrit:

                                                            Le truc c'est que je ne savais pas vraiment trop quoi faire sur ce catch .... Si jamais le try échoué, ça veut dire que new a échoué, et donc que le programme s'arrêterai, mais au final toutes les ressources serait détruite vu qu'il y aurait appel du destructeur non ? Mais c'est vrai que je trouvais ça bizarre aussi

                                                            C'est une partie de mon cours qui n'est pas encore rédigée, du coup, je vais donner quelques explications rapides sur try-catch.

                                                            Quand a-t-on besoin de faire un try catch ? Quand on a une exception qui peut être lancée et que l'on a un traitement spécifique a réaliser en conséquence.

                                                            Dans la majorité des classes a sémantique de valeur, comme celle que tu as dev dans les exos, on n'aura a faire en général qu'un seul traitement : libérer les ressources et retourner l'exception au code appelant.

                                                            Sauf que libérer une ressource, c'est le boulot du RAII. Donc un code "moderne" va séparer les responsabilités : la classe que tu implémentes ne gère pas directement les ressources, elle utilise des capsules RAII qui font cela pour elle. Soit directement du RAII de la STL (vector, array, smart ptr, etc), soit sur une classe (interne généralement) dédiée. (Souvent toi de vector et vector_base dans le papier de Stroustrup).

                                                            Donc au final, il n'y aura plus aucun traitement spécifique a réaliser dans la classe si tu utilises correctement le RAII (et notes, cela n'a aucun coût, contrairement a l'utilisation de try-catch). Et donc plus besoin de try-catch dans cette classe.

                                                            Par contre, le try-catch sera intéressant dans les classes métier qui appellent ta classe.

                                                            Par exemple, si tu écris une classe Buffer et que tu as une exception, le code appelant pourra nettoyer la mémoire et retenter de créer le buffer, il pourra essayer un plus petit buffer, il pourra utiliser un algo qui a moins besoin de mémoire, etc. Mais la classe Buffer a juste besoin de libérer la mémoire, ce qui sera fait via RAII.

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              21 octobre 2015 à 14:34:25

                                                              Soin... a écrit:

                                                              Note qu'il faudra aussi que je me renseigne sur les foncteurs / prédicat mais c'est une autre histoire. Par contre je ne comprends pas très bien, Pourquoi passer MaClass en sémantique d'entité vu qu'elle est dans une collection, elle peut donc être comparée non ?

                                                              Cordialement,

                                                              Dans l'exemple que j'ai utilisé, oui, MaClasse aurait forcément sémantique de valeur, vu qu'elle serait stockée sous forme de valeur.  Modifions donc la classe Holder en
                                                              class Holder{
                                                                 struct lesByXXX{
                                                                      bool operator ()(MaClass const & a, MaClass const & b) const{
                                                                          /* .... */
                                                                      }
                                                                 };
                                                                 using wrapper_t = std::reference_wrapper<MaClass>;
                                                                 using collection_t = std::set<wrapper_t, lessByXX>;
                                                                 /*  */
                                                              };
                                                              Ca fonctionne aussi bien pour la sémantique de valeur (est-ce utile ? à voir) que pour la sémantique d'entité ;)
                                                              • Partager sur Facebook
                                                              • Partager sur Twitter
                                                              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait

                                                              [exo-gbdivers]Intervalles

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown