0

I'm programming a family tree, and I was instructed to use lambda in the depth first search. I've tried to implement it, and I understand the basics of lambdas. I can't for the life of me understand how to make it work with the instructions I was getting from the teacher. Here is how I've tried to apply the code.

void depthFirst(const std::function<void(Node* )>& node) {
    auto traverse = [](Node* node) {
        node(this);
        for( auto search: Person) {
            search->depthFirst(node);
        }
    };
}

    template<typename T>
    class Node {
    
    public:
        explicit Node(const T& data, Node* parent = nullptr) : data_(data), parent_(parent) {}
        explicit Node(T data): data_(std::move(data)) {}
        virtual ~Node() = default;
    
        T getData() const {
            return data_;
        }
        void setData(T data) {
            data_ = data;
        }
    
        Node *getParent() const {
            return parent_;
        }
        void setParent(Node *parent) {
            parent_ = parent;
        }
    
        bool leftExists() {
            return this->left_ != nullptr;
        }
        void setLeft(const std::unique_ptr<Node> &left) {
            left_ = left;
        }
        const std::unique_ptr<Node> &getLeft() const {
            return left_;
        }
    
        bool rightExists() {
            return this->right_ != nullptr;
        }
        const std::unique_ptr<Node> &getRight() const {
            return right_;
        }
        void setRight(const std::unique_ptr<Node> &right) {
            right_ = right;
        }
    
    private:
        T data_; // node's data value with use of template
    
        Node *parent_; // pointer to point at the parent node
        std::unique_ptr<Node> left_;
        std::unique_ptr<Node> right_;
    
    };
    
      

  
    template<typename T>
    class Person {
    public:
    
        Person();
        Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive);
    
        // setters
        void setFirstName(const std::string &firstName);
        void setLastName(const std::string &lastName);
        void setGender(const std::string &gender);
        bool isAlive() const;
        void setAlive(bool alive);
        void setAge(int age);
        void setPerson();
    
        // getters
        const std::string& getFirstName() const;
        const std::string& getLastName() const;
        const std::string& getGender() const;
        int getAge() const;
        bool getAlive() const;
    
    
        //operators
        void displayPerson()const; // for testing atm
    
        void setPerson(const Person& Person);
    
    private:
        std::string firstName_;
        std::string lastName_;
        int age_{};
        std::string gender_;
        bool alive_ = true;
    };
    
    // Functions that sets the data for the Person --->
    template<typename T>
    void Person<T>::setFirstName(const std::string &firstName) {
        firstName_ = firstName;
    }
    template<typename T>
    void Person<T>::setLastName(const std::string &lastName) {
        lastName_ = lastName;
    }
    template<typename T>
    void Person<T>::setGender(const std::string &gender) {
        gender_ = gender;
    }
    template<typename T>
    bool Person<T>::isAlive() const {
        return alive_;
    }
    template<typename T>
    void Person<T>::setAge(int age) {
        age_ = age;
    }
    template<typename T>
    void Person<T>::setAlive(bool alive) {
        alive_ = alive;
    }
    
    // This is the default constructor, overload constructor and destructor for the person class --->
    template<typename T>
    Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) :
            firstName_(std::move(firstName)), lastName_(std::move(lastName)), age_(age), gender_(std::move(gender)), alive_(alive) {}
    template<typename T>
    Person<T>::Person() {
    }
    
    
    // Functions that gets the data for the Person --->
    template<typename T>
    int Person<T>::getAge() const {
        return 0;
    }
    template<typename T>
    const std::string &Person<T>::getFirstName() const {
        return this->firstName_;
    }
    template<typename T>
    const std::string &Person<T>::getLastName() const
    {
        return this->lastName_;
    }
    template<typename T>
    const std::string &Person<T>::getGender() const
    {
        return this->gender_;
    }
    template<typename T>
    bool Person<T>::getAlive() const {
        return false;
    }


    template<typename T> 
    class FamilyTree
    {
    public:
        FamilyTree();
        explicit FamilyTree(Node<T>* root); // Create new tree
        FamilyTree(T data):
    
    /*
        void addNewPerson();
        void addFather();
        void addMother();
    */
    
        void addNode(T data);
        bool isEmpty();
    
    private:
        Node<T> *root_;
        void addNode(Node<T>* root, T data);
    };
    
    template<typename T>
    FamilyTree<T>::FamilyTree(Node<T> *root) {
        this->root_ = root;
    }
    
    template<typename T>
    bool FamilyTree<T>::isEmpty() {
        return this->root_ == nullptr;
    }
    
    template<typename T>
    FamilyTree<T>::FamilyTree() {
        this->root_ = nullptr;
    }
    
    template<typename T>
    void FamilyTree<T>::addNode(T data) {
        if(root_ == nullptr)
            root_ = std::make_unique(Node<T>(data));
        else
            addNode(root_, data);
    }

//main

//just for test

void Person::displayPerson() const {
    std::cout << "First Name: " << this->getFirstName() << std::endl;
    std::cout << "Last Name: " << this->getLastName() << std::endl;
    std::cout << "Age: " << this->getAge() << std::endl;
    std::cout << "Gender: " << this->getGender() << std::endl;
    std::cout << "Alive: " << this->getAlive() << std::endl << std::endl;
}

    //main
    int main(){
    
            
            
        
            // Node test
            Node node;
        
            Node* setLeft(reinterpret_cast<Node *>(1));
            Node* setRight(reinterpret_cast<Node *>(2));
        
        
            std::cout << node.getData() << std::endl;
            std::cout << node.getLeft() << std::endl;
            std::cout << node.getRight() << std::endl;
        
        
            //Person test
            Person p0, p1("Robert", "Dane", 37, "Male", 1), p2("John", "Doe", 35, "Female", 1);
        
            p0.displayPerson();
            p1.displayPerson();
            p2.displayPerson();
        
        
            // FT test
        
            return 0;
        }
    
    void ignoreLine() // inspiration from here: https://stackoverflow.com/questions/11523569/how-can-i-avoid-char-input-for-an-int-variable
    {
        std::cin.clear();
        std::cin.ignore(INT_MAX, '\n');
    }
    
    void showMainMenu() // hold the output for the main menu
    {
        std::cout << "Welcome" << std::endl;
        std::cout << "Please enter a number for your choice below:\n" << std::endl;
        std::cout << "(1) Add new person to tree" << std::endl;
        std::cout << "(2) Show information for a person" << std::endl;
        std::cout << "(3) Print complete family-tree" << std::endl;
        std::cout << "(4) Used for testing new choices" << std::endl;
        std::cout << "(0) Quit" << std::endl;
        std::cout << "\nYour choice: " << std::endl;
    }
    
    int main()
    {
        familyTree fT; // used to access/init. familytree class.
    
        bool exit = true;
        int option;
    
        while (exit)
        {
            showMainMenu();
            std::cin >> option;
    
            while (std::cin.fail())
            {
                ignoreLine();
                std::cout << "\nOnly a number between 0 and 10 is allowed: ";
                std::cin >> option;
            }
    
            switch (option)
            {
                case 1:
                    fT.addNewPerson();
                    break;
                case 2:
                    std::cout << "Enter name of person to show information: ";
                    int temp;
                    std::cin >> temp;
                    fT.show(fT.search(temp));
                    break;
                case 3:
                    fT.printInOrder(fT.root, 0);
                    break;
                case 4:
                    /* n/a */
                    break;
                case 0:
                    exit = false;
                    break;
                default: ;
    
            }
            std::cout << "\nPress enter to continue.." << std::endl;
            ignoreLine();
        }
        return 0;

Old code that worked:

    person *familyTree::traverseLeft(person *ptr, const std::string& person)
    {
        ptr = ptr->left;
    
        while (ptr != nullptr)
        {
            if ((ptr->firstName) == person) {
                return ptr;
            }
    
            else if (traverseRight(ptr, person) != nullptr)
            {
                return traverseRight(ptr, person);
            }
            else
            {
                ptr = ptr->left;
            }
        }
        return nullptr;
    }
    
    person *familyTree::traverseRight(person *ptr, const std::string& person)
    {
    
        ptr = ptr->right;
    
        while (ptr != nullptr)
        {
            if ((ptr->firstName) == person)
            {
                return ptr;
            }
            else if (traverseLeft(ptr, person) != nullptr)
            {
                return traverseLeft(ptr, person);
            }
            else
                ptr = ptr->right;
        }
        return nullptr;

edit: The teacher told me that node(this); was supposed to point to the current node being searched. I may not have the most pedagogical correct teacher. But it is supposed to search the binary tree depth first, node for node. There is no use of vector or indexes, as I was told it was not needed. There is a class node and a class person that is implemented in to the node. If there is a better way of traversing a tree than this, feel free to let me know.

edited to add Person and Node.

edited to show old code that we got told to burn. I only got the instructions on lambda in person, but in short, was told to create lambda to use on a current node within a void function search, then go right, then go left. It could be reused in delete and other functions.

edited to add last of all code. Should I just go back to the old code (but less OOP) that I know compile and works? I got so much bad reviews on the old one that my group decided to start a new. But right now it's just a mess. (Keep in mind that the "new" code now is on different header files, so it might be more messy in regards to the console and main)

Shayeza
  • 3
  • 2
  • 2
    Fully unclear for me what it should do and what the problem is. Your line `node(this);` is a mystery, as I can't see any object nor class context. Please update your question to give us the needed content. – Klaus May 16 '22 at 09:11
  • Edited, is it enough information? I started coding in January so I'm sorry if there's any confusion – Shayeza May 16 '22 at 09:22
  • 1
    I am not fully aware or all C++ modernization, but it seem you are writing C++ like Javascript... Does this code compile ? –  May 16 '22 at 09:25
  • It's better if you post a complete reproducible code snippet... with the defintion of `Node` and `Person` – Marco Beninca May 16 '22 at 09:26
  • No it's not compiling. I'm contemplating going back to a less OOP code, because this isn't making sense to me. – Shayeza May 16 '22 at 09:27
  • I still are not aware of your data structures, especially no idea how your `Node` is build. From a first view, it looks a "reverse" of normal implementation :-) I can't really see where you do a search, as there is no comparison. And passing a node as a function looks also wrong... Please provide a minimal example which can be reproduced. Currently no data structure is known and the given code looks confusing. – Klaus May 16 '22 at 09:29
  • Please show the definition of `Person` and `Node` classes as well as the complete instructions you've gotten (and perhaps the original solution not utilizing lambdas?). – alagner May 16 '22 at 09:29
  • OK, several side questions to help you clean this up before proceeding: 1. Why is `Person` a class template? 2. How do you imagine iterating over it `for( auto search: Person) {` without it providing the right interface? 3. Didn't you mean `Node` to be a template specialized to hold `Person`, e.g. `Node parent`? – alagner May 16 '22 at 09:36
  • Yeah I was puzzled about that one @alagner. That makes a lot more sense. I was told the template is a much better use than an ordinary class because it doesn't care about what kind of object it gets – Shayeza May 16 '22 at 09:39
  • Edited to add a lot more code! I hope it makes sense (or you see what isn't haha) – Shayeza May 16 '22 at 09:42
  • `Node` class is off, btw, it seems to be more a single linked list than a tree. Again, please paste the complete working code, as the DFS functions don't seem to be working with your classes. – alagner May 16 '22 at 09:45
  • I think I've added it all now (had the wrong Node class, sorry). But it's just a mess. Should I just go back to the old code that I know works? – Shayeza May 16 '22 at 09:55
  • I'd start over with the working solution (make sure it's working though). – alagner May 16 '22 at 10:05
  • Yeah thank you, I'll do that. It definitely works. – Shayeza May 16 '22 at 10:08

2 Answers2

1

Is there a reason why you direct initialize your private variables in class Person as rvalues (ie. std::move?) ? std::string can bind permitted rvalues as long as they're const.

For instance the code below:

        template<typename T>
        Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) \
        : firstName_(std::move(firstName)), lastName_(std::move(lastName)), age_(age), gender_(std::move(gender)), alive_(alive) {}

Could be:

        template <typename T>
        Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) \
        : firstName_{firstName}, lastName_{lastName}, age_{age}, gender_{gender}, alive_{alive} {}

Making the the members in Person rvalues would be preparing them for a move, which it does not look like you're doing earlier in the code.

        template <typename T>
        void Person<T>::setFirstName(const std::string &firstName)
        {
        firstName_ = firstName;
        }

The values are being passed as lvalue references in the function parameters of Person, which you are changing to rvalues in the constructor of said class. There is no need to do this. They are not meant to be temporary values. The use of {} instead of () eliminates the chance of implicit conversion (narrowing) on part of the members.

  • It's what I've been taught. The teacher said it was the better way. Why, he doesn't properly explain. It's getting very frustrating. – Shayeza May 16 '22 at 10:45
  • Hi ifindoubt29, this seems to be more of a question/comment than an answer, I'd recommend putting things like this in the comments section under the OP rather than as an answer. If you think `std::string` rvalue binding can be of use here, please consider improving your answer by adding a code snippet to demonstrate how this addresses this question in the OP. – shelleybutterfly May 16 '22 at 14:33
  • I see that, now at least. I'm new to commenting. Excuse this – ifindoubt29 May 16 '22 at 14:49
0

You're thinking inside out or upside down - you should pass a lambda (or another function) to this function, and this should apply that function in a depth-first manner.

You also need a helper function that takes a Node* that indicates the current node.

A very simple example, with a preorder traversal:

private:

void traverse(const std::function<void(Node*)>& action, Node* current) 
{
    if (current != nullptr)
    {
        action(current);
        traverse(action, current->getLeft());
        traverse(action, current->getRight());
    }
}

public:

void traverse(const std::function<void(Node*)>& action)
{
    traverse(action, root_); 
} 

And you are supposed to use it something like this:

FamilyTree tree = ... whatever ...;
auto process = [](const Node* p) { ... print p ... };
// This will now print in preorder.
tree.traverse(process);
molbdnilo
  • 64,751
  • 3
  • 43
  • 82