Partage
  • Partager sur Facebook
  • Partager sur Twitter

retour de fonction

    13 juin 2018 à 23:14:24

    bonjour,

    je suis en train d'implémenter les évènements as3 en c++.

    Pour cela j'ai confectionné un arbre pour lequel la fonction de recherche de l'objet sur lequel l'évènement est déclenché est essentielle.

    Et je reste bloqué sur la propagation des évènements au sein du stage.

    Code principal:

    #include<iostream>
    #include"TreeEvent.h"
    
    int main(int argc, char**argv)
    {
            TreeEvent *tree = new TreeEvent();
            Goblin sprite;
            Goblin sprite1;
            Goblin sprite2;
    		Goblin sprite3;
            Goblin sprite4;
            tree->addNode(&sprite);
            tree->addNode(&sprite1);
            Node* node = tree->find(&sprite);
            tree->addNode(&sprite2, node);
            tree->addNode(&sprite4, node);
    		Node* node1 = tree->find(&sprite1);		//ici erreur
            tree->addNode(&sprite3, node1);
    		delete tree;
            return EXIT_SUCCESS;
    }

    Code de mon arbre:

    #ifndef TREEEVENT_H
    #define TREEEVENT_H
    
    #include<stdlib.h>
    #include<vector>
    
    class Goblin{
            public:
                    Goblin()
                    {
                            static int v = 0;
                            v++;
                            value=v;
                    }
    				
    				Goblin(const Goblin& g)
    				{
    					value = g.value;
    				}
    				
                    int value;
    };
    typedef struct Node_{
            Goblin* item;
            Node_* parent;
    		std::vector<Node_*> children;
            Node_(Goblin* item, Node_*parent):item(item), parent(parent){}
    		~Node_(){}
    } Node;
    
    class TreeEvent{
            public:
                    TreeEvent(){
                            root = new Node(nullptr, nullptr);
                    }
            
                    int addNode(Goblin* item, Node* parent = nullptr)
                    {
                            if(parent == nullptr)
                            {
                                   root->children.push_back(new Node(item, root));
    							   return 0;
                            }else{
                                   parent->children.push_back(new Node(item, parent));
    							   return 0;
                            }
    						return -1;
                    }
    
                    void remNode(Node** node = nullptr)
                    {
    					std::vector<Node*>::iterator it;
                            for(it = (*node)->children.begin(); it != (*node)->children.end(); it++)
                            {
                                    remNode(&(*it));
                                    delete (*it); 
                            }
                    }
    
                    Node* find(Goblin* item, Node* node = nullptr)
                    {
    						if(node != nullptr && node->item == item)
    								return node;
                            if(node == nullptr)
                                    node = root;
    						std::vector<Node*>::iterator it;
                            for(it = node->children.begin(); it != node->children.end(); it++)
                            {
    							return find(item, *it);
                            }
                    }
    
                    ~TreeEvent()
                    {
                            remNode(&root);
                            delete root;
                    }
                            Node* root;
                    
                    private:
    };
    
    #endif
    

    "node1" est initialisé à null.

    Ma fonction "find" ne trouve, par conséquent, pas le goblin associé au nœud que je recherche.

    Le problème se situe donc à ce niveau.

    En regardant le debugger, je peux constater que "find" ne passe pas du tout sur le second nœud de mon arbre.

    Est-ce que quelqu'un a une idée?

    • Partager sur Facebook
    • Partager sur Twitter
      13 juin 2018 à 23:50:03

      Je trouve ça dangereux d'implémenter un find récursif. Utilises plutôt std::find :)
      • Partager sur Facebook
      • Partager sur Twitter
      http://cpp-rendering.io : Vous trouverez tout ce dont vous avez besoin sur Vulkan / OpenGL et le rendu 3D !
        14 juin 2018 à 18:20:40

        Salut,

        Le principe, lorsque l'on veut utiliser la récursivité, c'est d'écarter ce que l'on appelle "le cas de base".  C'est à  dire, la situation dans laquelle la fonction ne sera pas appelée "une fois de plus".

        Tu as identifié un cas de base : si le noeud recherché correspond au noeud courant.  Ce qui est une bonne chose...

        Mais, es-tu sur que c'est le seul cas de base?

        Allez, regarde un peu ce schéma :

                       root
                     /  |  \
                    A   B   C
                   /   / \   \
                  D    E  F   G

        Mettons que je veuille rechercher l'élément G.  Que va-t-il se passer lorsque ma logique passera par D, E ou F ?

        • 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
          14 juin 2018 à 21:42:42

          merci, je m'en étais aperçu.

          Voici la version qui fonctionne:

          int find(Goblin* item, Node** search, Node* node = nullptr)
          {
          	if(node != nullptr && node->item == item)
          	{
          		*search = node;
          		return 0;
          	}
                  if(node == nullptr)
                          node = root;
          	std::vector<Node*>::iterator it;
                  for(it = node->children.begin(); it != node->children.end(); it++)
                  {
          		if(!find(item, search, *it))
          			return 0;
                  }
          	return -1;
          }




          es-tu d'accord avec moi sur l'arrangement de mes classes:

          • Goblin, dérivée du Sprite de la SFML, comportant une fonction addChild insérant ses fils au sein d'un vecteur d'enfants permettant ainsi la propagation
          • EventMulticaster me permettant de diffuser un événement aux autres Goblins
          • Observer, un wrapper pour Goblin paramétrée ( template ) par un événement particulier, héritée de certaines classes elles-même héritées de EventMulticaster, ce dernier permettant, en plus de la fonctionnalité sus-mentionnée, d'ajouter ou de supprimer l'écoute d'un événement
          • Observee, un wrapper pour Goblin, regroupant toutes les fonctions de type on(BLABLA)Event susceptibles d'être appelées par la diffusion ( broadcast ) d'EventMulticaster pour le traitement des événements après qu'ils aient été broadcastés par un Observer, ces mêmes fonctions appelant le callback référençant le lambda d'un écouteur d'événement placé sur cet Observer qui écoute l'Observee en question
          • (BLABLA)Event héritée de Event me permettant de représenter un certain événement et qui sera le paramètre d'un écouteur

          En réfléchissant à deux fois, je ne vois plus la nécessité d'un arbre, cela peut se gérer directement au sein de la classe Goblin comme indiqué en premier point.

          Qu'en penses-tu?

          -
          Edité par amaurybenard 14 juin 2018 à 21:49:26

          • Partager sur Facebook
          • Partager sur Twitter
            15 juin 2018 à 4:51:22

            amaurybenard a écrit:

            merci, je m'en étais aperçu.

            Voici la version qui fonctionne:

            int find(Goblin* item, Node** search, Node* node = nullptr)
            {
            	if(node != nullptr && node->item == item)
            	{
            		*search = node;
            		return 0;
            	}
                    if(node == nullptr)
                            node = root;
            	std::vector<Node*>::iterator it;
                    for(it = node->children.begin(); it != node->children.end(); it++)
                    {
            		if(!find(item, search, *it))
            			return 0;
                    }
            	return -1;
            }

            Ta correction ne rime à rien : il y a toujours un cas de base manquant ;)

            es-tu d'accord avec moi sur l'arrangement de mes classes:

            Non, ou en tout cas, pas pour tout

            • Goblin, dérivée du Sprite de la SFML, comportant une fonction addChild insérant ses fils au sein d'un vecteur d'enfants permettant ainsi la propagation

            Un sprite, c'est un élément de l'affichage. Autrement dit, c'est quelque chose qui ne doit êre utilisé qu'au niveau de la vue (et encore : pour autant que la vue en question soit effectivement graphique

            Et, au niveau de la vue, on se fout pas mal de savoir si le sprite que l'on affiche est un goblin, un arbre, une flêche ou même une pierre: c'est un élément qui doit être affiché, point-barre

            • EventMulticaster me permettant de diffuser un événement aux autres Goblins

            Pas sur du tout... Pourquoi ne pas mettre en place un système de signaux et de slots à la place : chaque élément intéressé par "ce qui peut arriver" à un autre pourrait s'y connecter sans problème ;)

            • Observer, un wrapper pour Goblin paramétrée ( template ) par un événement particulier, héritée de certaines classes elles-même héritées de EventMulticaster, ce dernier permettant, en plus de la fonctionnalité sus-mentionnée, d'ajouter ou de supprimer l'écoute d'un événement
            • Observee, un wrapper pour Goblin, regroupant toutes les fonctions de type on(BLABLA)Event susceptibles d'être appelées par la diffusion ( broadcast ) d'EventMulticaster pour le traitement des événements après qu'ils aient été broadcastés par un Observer, ces mêmes fonctions appelant le callback référençant le lambda d'un écouteur d'événement placé sur cet Observer qui écoute l'Observee en question
            • (BLABLA)Event héritée de Event me permettant de représenter un certain événement et qui sera le paramètre d'un écouteur

            De manière générale, le patron de conception observateur manque cruellement de souplesse... Que penserais tu à la place d'un système de signaux et de slots, dont voici une implémentation perso en C++ moderne?

            Mais il faut faire un choix: soit tu fais en sorte que les goblins émettent des événement (à charge pour "autre chose" d'y réagir comme il se doit), soit tu rend les goblins responsables de la réaction à certains événements (qui devront alors être émis par "autre chose")

            Mais, quoi qu'il en soit, il n'y a pas vraiment de sens à demander à une classe de réagir aux événements (ou aux signaux) qu'elle a elle-même émis ;)

            En réfléchissant à deux fois, je ne vois plus la nécessité d'un arbre, cela peut se gérer directement au sein de la classe Goblin comme indiqué en premier point.

            Encore une fois, veilles bien à respecter le SRP et l'OCP avant tout. J'ai du mal à visualier ce que tu te proposes de faire, peut-être parce que je suis fatigué, peut-être parce que tes explications ne sont pas claires...

            Mais garde toujours en mémoire

            1. que l'héritage est la relation la plus forte qui puisse exister entre deux classes, et que c'est donc celle que l'on devrait utiliser le moins
            2. que plus tu pourras limiter les responsabilités de tes classes et de tes fonctions, plus tu gagnera en souplesse lorsqu'il s'agira de les utiliser en combinaison avec d'autres
            3. que plus le nom que tu donnera aux différents éléments de ton code seront clair quant à l'usage auquel les éléments sont destinés, plus tu auras facile à t'y retrouver
            4. que plus tu arrivera à faire la distinction entre ce qui est des données "métier", ce qui a trait à une vue (particulière) et ce qui permet la communication entre les vues et les données métiers (les "controleur"), plus tu pourras faire évoluer ton projet

            -
            Edité par koala01 15 juin 2018 à 4:55:30

            • 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
              15 juin 2018 à 14:35:32

              Je ne vois pas en quoi ma correction ne rime à rien.

              Cette version de la fonction find fonctionne et est même à mon sens la seule valable.

              Je ne comprends pas quel cas de base j'ai pu louper.

              koala01 a écrit:

              Un sprite, c'est un élément de l'affichage. Autrement dit, c'est quelque chose qui ne doit êre utilisé qu'au niveau de la vue (et encore : pour autant que la vue en question soit effectivement graphique

              Et, au niveau de la vue, on se fout pas mal de savoir si le sprite que l'on affiche est un goblin, un arbre, une flêche ou même une pierre: c'est un élément qui doit être affiché, point-barre

              Je suis d'accord avec toi, Goblin ne doit pas hériter de Sprite, suivant ton rappel:

              koala01 a écrit:

              Mais garde toujours en mémoire

              1. que l'héritage est la relation la plus forte qui puisse exister entre deux classes, et que c'est donc celle que l'on devrait utiliser le moins

               et parce que ce n'en est pas un. J'avoue m'être fourvoyé.:p

              Je vais faire un petit tour du côté de ton implémentation des signaux et slots. Je n'avais pas envisagé ce point de vue.

              koala01 a écrit:

              Mais il faut faire un choix: soit tu fais en sorte que les goblins émettent des événement (à charge pour "autre chose" d'y réagir comme il se doit), soit tu rend les goblins responsables de la réaction à certains événements (qui devront alors être émis par "autre chose")

              Mais, quoi qu'il en soit, il n'y a pas vraiment de sens à demander à une classe de réagir aux événements (ou aux signaux) qu'elle a elle-même émis ;)

              Pour le moment, mes goblins réagissent, par le biais d'Observee leur wrapper, grâce à un médiateur qui joue le rôle d'Observer et qui écoute certaines interactions graphiques. Les goblins déclenchent donc des événements et le médiateur y réagit.

              Je me demande encore quels événements pourront écouter mes goblins si ce ne sont les MouseEvent, JoystickEvent, KeyPressed ( les événements de base quoi ).

              koala01 a écrit:

              En réfléchissant à deux fois, je ne vois plus la nécessité d'un arbre, cela peut se gérer directement au sein de la classe Goblin comme indiqué en premier point.

              Encore une fois, veilles bien à respecter le SRP et l'OCP avant tout. J'ai du mal à visualier ce que tu te proposes de faire, peut-être parce que je suis fatigué, peut-être parce que tes explications ne sont pas claires...

               En effet je m'embrouillais les pinceaux. Je voulais intégrer mon arbre directement dans Goblin. C'est donc moi qui devait être fatigué.

              • Partager sur Facebook
              • Partager sur Twitter
                15 juin 2018 à 17:56:31

                Allez, on va tout revoir depuis le début, si tu veux bien.  Commençons par les besoins :

                Tu veux pouvoir retrouver le noeud qui contient -- entre autre -- un (pointeur sur un) goblin particulier.

                - Comme il s'agit d'un pointeur, il va de soi que si l'utilisateur transmet une adresse "connue pour être invalide" (nullptr), on ne pourra jamais trouver l'élément en question.

                C'est un premier cas de base.  Doit-il être considéré comme une erreur de logique de la part de l'utilisateur? ou bien considères-tu que cette situation "peut très bien arriver"?  c'est à toi de voir ;)

                - Ton arbre peut être vide, et, dans ce cas, il sera impossible de trouver le noeud qui contient le pointeur recherché.  Encore une fois, tu peux considérer cette situation comme une erreur de logique (de la part de l'utilisateur) soit comme une situation "normale"; c'est à toi de voir ;)

                Mais c'est un deuxième cas de base

                - A force de parcourir les enfants de tes noeuds, tu arriveras forcément à "une feuille": c'est l'enfant d'un de tes noeud qui n'a pas d'enfant à lui.

                Deux situations s'offrent alors à toi:

                • soit le noeud en quesiton contient le pointeur vers le goblin que tu recherches, et il faut carrément "remonter" jusqu'à la fonction qui a fait appel à ta fonction find avec le résultat;
                • soit le noeud en quesiton ne contient pas le pointeur vers le goblin que tu recherches et, dans ce cas, tu dois "remonter" pour avoir une chance de tester les noeuds par lesquels tu n'es pas encore passé.
                Ce sont les cas de base 3 et 4 (à noter que le cas de base 3 est pour l'instant le seul qui fournit un résultat "trouvé" ;) )

                Enfin, ce n'est pas parce que tu décide de parcourir tous les enfants d'un noeud (sous entendu : le noeud ne correspond pas à l'élément recherché) que tu trouveras forcément l'élément recherché parmi ses enfants. et lorsque tu auras parcouru tous le enfants de ton noeuds (sous entendu: sans trouver l'élément recherché), il faudra bien que tu retourne vers le parent de ton noeud, pour tester le "frère" du noeud que tu viens de quitter (s'il existe). 

                C'est un cinquième cas de base.

                Avec cinq cas de base (dont quatre correspondent à "l'impossibilité" de trouver l'élément recherché).

                On peut remplacer les cas de base 1 et 2 par des assertions : si l'utilisateur fournit nullptr pour le goblin ou pour le noeud à partir duquel on doit effectuer la recherche, cela peut être considéré comme une erreur de logique de sa part, et il devra la corriger avant de pouvoir aller plus loin.

                Mais ca laisse toujours trois cas de base, dont 2 correspondent à l'impossibilité de trouver l'élément recherché.

                Ensuite, la règle est simple : pour chaque cas de base, tu dois avoir un test qui empêchera la logique d'atteindre un point du code où "un nouvel appel à ta fonction" sera effectué. Avec trois à cinq cas de base, ta logique doit donc contenir ... entre trois et cinq tests.  Je n'en vois que deux, il en manque donc au moins un (et potentiellement trois).

                -Pour saluer l'effort que tu as fait  en essayant de mettre en place la récusivité terminale, je vais te faire grâce de mon laïus sur l'importance d'organiser correctement les paramètres de fonction (même si j'aurai trouvé "plus cohérent) de transmettre le "noeud courant" en premier, suivi du goblin et enfin du résultat ;) )

                Mais c'est la seule largesse que cet effort te vaudra de ma part, car tu vas quand même te faire taper sur les doigts ;)

                D'après ton code, la fonction find est une fonction libre.  Or, une fonction libre n'a que trois sources d'informations:

                • les variables qu'elle déclare elle-même,
                • les paramètres qu'elles reçoit de la part des fonctions qui l'appellent et
                • les variables globales.

                Or, root n'est pas une variable locale et n'est pas non plus un paramètre.  La seule conclusion possible, est donc qu'il s'agit d'une variable globale. Et les variable globales C'EST MAL!!!!

                En plus, ta logique est complètement foireuse! Pour nous en convaincre, faisons "comme si" nous étions "aussi bête qu'un ordinateur" pendant quelques instants.  C'est un truc qui marche toujours pour vérifier la validité d'un algorithme.

                Et je vais même être très sympa : je vais accepter l'idée que root puisse être une variable globale ;). Nous appelons donc cette fonction sous une forme qui devrait être proche de

                int main(){
                   Goblin goblin;
                   Node * result{nullptr};
                   int found =find(&goblin, &result);
                   if(found){
                       // si trouvé
                   }else{
                       // si pas trouvé
                   }
                }

                Lors du premier appel à la fonction, le test if(node != nullptr && node->item == item) renvoie false(car node == nullptr)... On passe.

                Ensuite, on arrive à

                if(node==nullptr)
                    node=root;
                

                Soit, node contient maintenant l'adresse à laquelle se trouve root. Mais... Pour l'instant root n'a pas encore été testé!!! et la suite ne s'occupera que de tester les enfants de root!!!

                Enfin continuons, et voyons ce qui se passe dans la boucle:

                        std::vector<Node*>::iterator it;                                   // -1-
                        for(it = node->children.begin(); it != node->children.end(); it++) // -2-
                        {
                            if(!find(item, search, *it))                                   // -3-
                            return 0;                                                      // -4-
                        }
                        return -1;                                                         // -5-                          

                Avant de commencer: on est en C++ ici. Le type bool et les valeur true et false sont connue du compilateur.  Pourquoi renvoyer un int au lieu d'un booléen? (mais bon, ce n'est qu'un détail ;) )

                1-1 et -2- : on déclare un itérateur (-1-) qui sera utilisé dans une boucle (-2-): aucun problème jusque là

                -3- traduction : si l'apel de find en lui transmettant:
                • item qui ne change pas (et n'a aucune raison de le faire)
                • search qui ne change pas et qui n'a aucune raison de le faire et
                • ce qui est pointé par it (l'un des enfants du noeud)

                renvoie false, alors on renvoie 0 (qui équivaut à ... false)

                Soit... Mettons donc que nous sommes dans une situation proche de l'arbre dont j'ai montré quelques interventions plus haut. Node correspond à ... root.  Si bien que lors du premier passage dans la boucle *it correspond au noeud A

                Mettons -- pour ne pas devoir le faire 110 fois -- que le goblin que l'on recherche corresponde justement au noeud A. 

                Nous arrivons donc : *search== nullptr (normal), node == A et A->item (ou préferes  tu node->item == item? cela revient au même).

                Lorsque l'on arrive (pour la deuxième fois) au  test if(node != nullptr && node->item == item)l'expression renvoie ... true (car node n'est pas égal à nullptr et que node->item est bien égal à item.  Que se passe-t-il alors?  Hé bien, c'est facile!

                        *search = node; // -1a-
                        return 0;       // -1b-

                -1a- traduction "ce qui est pointé par search prend l'adresse représentée par node" :c'est parfait : cela te permet de faire en sorte de récupérer l'adresse de node au niveau de la fonction appelante.

                -2- : renvoie 0.  Cela met fin à la récursivité (génial !) mais 0 correspond à ... false!!!

                du coup, on quitte le deuxième appel de la fonction, et on continue au niveau du premier appel juste après le -3-.

                Et là, vu que la fonction a renvoyé false la condition du -3- est vérifiée : find(item,search, * it) a bel et bien renvoyé false.

                On passe donc dans -4- dont la traduction est: "met fin à cette fonction et renvoie 0".  Mais, encore une fois, 0 équivaut à ... false!!!

                on va donc quitter find et retourner dans la fonction qui l'a appelée (c'est à dire main).  Et comme on a renvoyé 0, found faudra ... 0.  C'est à dire... false!!!

                Du coup, quand l'expression if(found) sera évaluée, elle sera évaluée à ... false.  Et nous passerons dans la branche ... else du test (// si pas trouvé).

                C'est moche, car il me semblait justement avoir dit qu'on avait trouvé item dans A!!!

                -
                Edité par koala01 15 juin 2018 à 18:01:43

                • 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
                  16 juin 2018 à 1:03:25

                  koala01 a écrit:

                  - Comme il s'agit d'un pointeur, il va de soi que si l'utilisateur transmet une adresse "connue pour être invalide" (nullptr), on ne pourra jamais trouver l'élément en question.

                  Je prends en compte... Il faut se débarrasser des indigents coûte que coûte. Tu me fais penser à la gestion des erreurs: "Trop élevé pour être perçue".

                  koala01 a écrit:

                  - Ton arbre peut être vide, et, dans ce cas, il sera impossible de trouver le noeud qui contient le pointeur recherché.  Encore une fois, tu peux considérer cette situation comme une erreur de logique (de la part de l'utilisateur) soit comme une situation "normale"; c'est à toi de voir ;)

                  Ce n'est pas une situation normale... J'intégrerai ça au cas où un malade serait incapable de repérer que la fonction addChild est à la base de ce qu'il fait. au suivant:

                  koala01 a écrit:

                  - A force de parcourir les enfants de tes noeuds, tu arriveras forcément à "une feuille": c'est l'enfant d'un de tes noeud qui n'a pas d'enfant à lui.

                  Deux situations s'offrent alors à toi:

                  • soit le noeud en quesiton contient le pointeur vers le goblin que tu recherches, et il faut carrément "remonter" jusqu'à la fonction qui a fait appel à ta fonction find avec le résultat;
                  • soit le noeud en quesiton ne contient pas le pointeur vers le goblin que tu recherches et, dans ce cas, tu dois "remonter" pour avoir une chance de tester les noeuds par lesquels tu n'es pas encore passé.

                  ma fonction offre des solutions pour les deux situations ( surtout pour la première :p), mais je pense que tu le sais pertinemment.

                  koala01 a écrit:

                  Enfin, ce n'est pas parce que tu décide de parcourir tous les enfants d'un noeud (sous entendu : le noeud ne correspond pas à l'élément recherché) que tu trouveras forcément l'élément recherché parmi ses enfants. et lorsque tu auras parcouru tous le enfants de ton noeuds (sous entendu: sans trouver l'élément recherché), il faudra bien que tu retourne vers le parent de ton noeud, pour tester le "frère" du noeud que tu viens de quitter (s'il existe).

                  Dans le cas où je ne trouve pas le nœud désiré, ma fonction me renvoie -1 ( alors qu'elle devrait me renvoyer 0 selon tes arguments ) et de toute façon le pointeur sur Node*, j'ai nommé seacrh**, ne pointera sur rien.

                  De toute façon cette erreur est gérée par le retour de la fonction auquel je pense que tu feras allusion... DEVIN :magicien:

                  koala01 a écrit:

                  -Pour saluer l'effort que tu as fait  en essayant de mettre en place la récusivité terminale, je vais te faire grâce de mon laïus sur l'importance d'organiser correctement les paramètres de fonction (même si j'aurai trouvé "plus cohérent) de transmettre le "noeud courant" en premier, suivi du goblin et enfin du résultat ;) )

                  Je vérifierai dans mon bouquin "algorithmes en c". Mais là je suis en plein déménagement.

                  koala01 a écrit:

                  Et les variable globales C'EST MAL!!!!

                  Ma variable root est dores et déjà inclue parmi les variables référencées "private".

                  koala01 a écrit:

                  if(node==nullptr)
                      node=root;
                  

                  Soit, node contient maintenant l'adresse à laquelle se trouve root. Mais... Pour l'instant root n'a pas encore été testé!!! et la suite ne s'occupera que de tester les enfants de root!!!

                  Je te signale que root est instancié au sein même de la classe. Ce n'est pas un agrégat. ALLO? Lorsque tu viens juste de créer une variable, t'arrive-t-il de vérifier si elle existe au cas où tu ne t'en souviendrais plus !! J'avais oublié de préciser que je poste souvent mes codes pour identifier les bugs qui m'assaillent et non pas pour aller plus loin. Mais là, tu me file la pilule rouge dis moi. root devrait être private, JE LE SAIT !!

                  Au passage, je vais utiliser les booléens à l'avenir, PROMIS...

                  Et pour finir, si found vaut zero ou false alors même que mon Node a été trouvé, je comprends pourquoi tu me disais que ma fonction ne rimait à rien...

                  tu m'as eu sur ce coup là:o.

                  on remet ça

                  • Partager sur Facebook
                  • Partager sur Twitter
                    16 juin 2018 à 2:04:17

                    amaurybenard a écrit:


                    Ma variable root est dores et déjà inclue parmi les variables référencées "private".

                    Mais gros malin, si root fait partie d'un Tree ta fonction libre, elle fait comment pour y accéder si elle ne dispose pas d'une instance de Tree????

                    C'est d'autant plus vrai si root est dans l'accessibilité privée de Tree, car il n'y aura que les fonctions membres de Treequi pourront y accéder!!!, et que find, en tant que fonction libre, n'aura de toute façon pas le droit d'aller s'intéresser à root, même si elle connait l'arbre à partir duquel elle doit essayer d'utiliser root!

                    amaurybenard a écrit:

                    Je te signale que root est instancié au sein même de la classe. Ce n'est pas un agrégat.

                    Ah??? Parce que, sous prétexte que root est une donnée membre de ta classe, tu estimes peut être qu'il ne vaut la peine ni de tester si l'adresse qu'il contient (vu que c'est un pointeur) nous amène à un élément valide, ni de le tester si, par hasard, il ne contiendrait pas l'élément que tu recherche???

                    amaurybenard a écrit:

                    Lorsque tu viens juste de créer une variable, t'arrive-t-il de vérifier si elle existe au cas où tu ne t'en souviendrais plus !!

                    Quand tu es dans la fonction find, tout ce que tu sais du programme, c'est:

                    • qu'il y a "quelque part" un appel result=find(X, Y, Z)
                    • les données locales à find
                    • les paramètres que tu lui a transmis

                    Tu crois que tu "viens" de créer un noeud auquel on peut accéder au travers de root? Rien n'est moins sur, vu que, au niveau de find, tu n'a aucune idée de par où est passé ton programme avant l'appel à find!

                    Une petite précision au passage: lorsque j'écrivais

                    Pour l'instant root n'a pas encore été testé!!!

                    Je ne voulais pas dire "pour savoir s'il correspond à un noeud valide" (pour faire simple: si sa valeur est différente de nullptr), mais bien "pour savoir si, à tout hasard, ce ne serait pas lui qui contient l'élément recherché"...

                    • 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
                      16 juin 2018 à 11:55:37

                      le message ajouté à "passage de pointeur" aurait dû être posté ici, désolé.

                      il serait préférable que l'on choisisse un topic et qu'on s'y tienne...

                      Saleté de doublon

                      -
                      Edité par amaurybenard 16 juin 2018 à 22:02:11

                      • Partager sur Facebook
                      • Partager sur Twitter
                        16 juin 2018 à 13:52:51

                        amaurybenard a écrit:

                        find n'est pas une fonction libre. Elle est intégrée à la classe DoubleLinkedTree. J'ai écrit la définition de ma fonction dans le header, je comprends que ça puisse porter à confusion.

                        A voir le code de find de ta "correction", le doute était permis...

                        amaurybenard a écrit:

                        Je n'arrangerai pas mon code afin de prendre en compte le fait que root puisse contenir un item ( Component ) et que ce dernier soit testé au sein de find.

                        C'est une erreur...

                        La racine est un élément, qui n' a aucune raison d'être ecarté de la logique

                        amaurybenard a écrit:

                        Sinon je serais contraint de ne laisser plus qu'un seul nœud à item à la racine.

                        C'est pourtant le but et la manière de fonctionner des arbres : un noeud racine, qui fait partie intégrante de l'ensemble et auquel se rattachent (de manière directe ou indirecte) les autres éléments.

                        Regarde HTML, XML ou les AST pour t'en convaincre ;)

                        • 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
                          16 juin 2018 à 21:56:25

                          J'ai finalement essayé de prendre en compte tes remarques et j'ai un peu de temps pour aligner du code...

                          J'en suis à la mise en template. mais voilà je reste bloqué la dessus:

                          std::vector<Node<COMPONENT>*> children;

                          children n'est pas reconnu. Je ne sais pas d'où ça vient. J'ai essayé avec typename également mais ça non plus.

                          J'en profite pour te montrer où j'en suis ( j'ai une version de find qui risque de te plaire ):

                          #ifndef DoubleLinkedTree_H
                          #define DoubleLinkedTree_H
                          
                          #include<vector>
                          
                          template<class COMPONENT>
                          class Node{
                          	public:
                                  Node(COMPONENT* component, Node*parent):component(component), parent(parent){}
                          		
                          		COMPONENT* getComponent()
                          		{
                          			return component;
                          		}
                          
                          		Node<COMPONENT>* getParent()
                          		{
                          			return parent;
                          		}
                          
                          		std::vector<Node<COMPONENT>* > getChildren()
                          		{
                          			return children;
                          		}
                          
                          		int addChild(Node<COMPONENT>* node)
                          		{
                          			children.push_back(node);
                          			return 0;
                          		}
                          		
                          		~Node(){}
                          	private:
                                  COMPONENT* component;
                                  Node<COMPONENT>* parent;
                          		std::vector<Node<COMPONENT>*> children;
                          };
                          
                          template<class COMPONENT>
                          class DoubleLinkedTree{
                          public:
                          
                          		DoubleLinkedTree(){}
                          
                          		int addNode(COMPONENT* component, Node<COMPONENT>* parent = nullptr)
                          		{
                          			if(parent == nullptr)
                          			{
                          				root = new Node<COMPONENT>(component, nullptr);
                          				return 0;
                          			}else{
                          				parent->addChild(new Node<COMPONENT>(component, parent));
                          				return 0;
                          			}
                          			return -1;
                          		}
                          
                          		int find(COMPONENT* component, Node<COMPONENT>** search, Node<COMPONENT>* node = nullptr)
                          		{
                          			if(node == nullptr)
                          				node = root;
                          			if(component == nullptr || root == nullptr || node == nullptr)
                          				return -1;
                          			if(node->getComponent() == component)
                          			{
                          				*search = node;
                          				return 1;
                          			}
                          			typename std::vector<Node<COMPONENT>*>::iterator it;
                          			for(it = node->getChildren().begin(); it != node->getChildren().end(); it++)
                          			{
                          				if(find(component, search, *it) == 1)
                          					return 1;
                          			}
                          			return 0;
                          		}
                          
                          		void remNode(Node<COMPONENT>** node = nullptr)
                          		{
                          			typename std::vector<Node<COMPONENT>*>::iterator it;
                          			for(it = (*node)->getChildren().begin(); it != (*node)->getChildren().end(); it++)
                          			{
                          				remNode(&(*it));
                          				delete (*it); 
                          			}
                          		}
                          
                          		~DoubleLinkedTree()
                          		{
                          			remNode(&root);
                          			delete root;
                          		}
                                          
                          private:
                                  Node<COMPONENT>* root;
                          };
                          
                          #endif
                          



                          Que penses tu de cette variable children qui ne tolère pas mon autorité et de mon code en général? Je sais, ce n'est pas énorme...

                          Au fait, je sais ce que c'est qu'un arbre. Jusqu'alors, je voulais utiliser un seul arbre pour tout le programme et ça posait problème.

                          Après j'ai crée un arbre pour chaque Component et cela changeait tout.

                          Mon implémentation de l'arbre a été modifiée pour intégrer cette découverte.

                          Mais ça c'était avant que je passe à la représentation UML. Dès lors je me suis aperçu de l'inutilité d'un arbre.

                          Je te poste ce diagramme. Pour le visionner utilise UMLet, ça marche aussi sur windows.

                          voilà le lien:

                          https://drive.google.com/file/d/1KQxm3s1dd8w_ZhBUrmqEZzrQzRNJNvqA/view?usp=sharing

                          merci pour ton attention


                          -
                          Edité par amaurybenard 17 juin 2018 à 3:20:15

                          • Partager sur Facebook
                          • Partager sur Twitter

                          retour de fonction

                          × 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