Я программирую генеалогическое древо, и мне было приказано использовать лямбда в поиске в глубину. Я пытался реализовать это, и я понимаю основы лямбда-выражений. Я не могу понять, как заставить его работать с инструкциями, которые я получал от учителя. Вот как я пытался применить код.
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;
Старый код, который работал:
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;
изменить: Учитель сказал мне, что узел (это); должен был указывать на текущий искомый узел. Возможно, у меня не самый педагогически правильный учитель. Но предполагается сначала искать глубину бинарного дерева, узел за узлом. Нет использования вектора или индексов, так как мне сказали, что это не нужно. Есть узел класса и человек класса, который реализован в узле. Если есть лучший способ обхода дерева, не стесняйтесь, дайте мне знать.
отредактировано, чтобы добавить Person и Node.
отредактировано, чтобы показать старый код, который нам сказали сжечь. Я получил только инструкции по лямбде лично, но, короче говоря, мне сказали создать лямбду для использования на текущем узле в поиске функции void, затем идите направо, затем налево. Его можно повторно использовать в удалении и других функциях.
отредактировано, чтобы добавить последний из всего кода. Должен ли я просто вернуться к старому коду (но меньше ООП), который, как я знаю, компилируется и работает? Я получил так много плохих отзывов о старом, что моя группа решила создать новый. Но сейчас это просто бардак. (Имейте в виду, что «новый» код теперь находится в разных файлах заголовков, поэтому он может быть более запутанным в отношении консоли и основного)
Отредактировано, достаточно ли информации? Я начал программировать в январе, так что извините, если есть путаница.
Я не полностью осведомлен о модернизации C++, но кажется, что вы пишете C++ как Javascript... Компилируется ли этот код?
Будет лучше, если вы опубликуете полный воспроизводимый фрагмент кода... с определением Node и Person
Нет, не компилируется. Я подумываю вернуться к меньшему количеству кода ООП, потому что это не имеет для меня смысла.
Я до сих пор не знаю о ваших структурах данных, особенно о том, как построен ваш Node. На первый взгляд, это выглядит как «обратная сторона» обычной реализации :-) Я действительно не вижу, где вы выполняете поиск, так как сравнения нет. И передача узла как функции тоже выглядит неправильно... Пожалуйста, приведите минимальный пример, который можно воспроизвести. В настоящее время структура данных неизвестна, и данный код выглядит запутанным.
Пожалуйста, покажите определение классов Person и Node, а также полные инструкции, которые вы получили (и, возможно, исходное решение, не использующее лямбда-выражения?).
Хорошо, несколько дополнительных вопросов, которые помогут вам разобраться в этом, прежде чем продолжить: 1. Почему Person является шаблоном класса? 2. Как вы представляете себе его итерацию for( auto search: Person) { без предоставления правильного интерфейса? 3. Разве вы не имели в виду Node шаблон, предназначенный для хранения Person, например. Node<Person> parent?
Да, я был озадачен этим @alagner. Это имеет гораздо больше смысла. Мне сказали, что шаблон гораздо лучше использовать, чем обычный класс, потому что ему все равно, какой объект он получит.
Отредактировано, чтобы добавить намного больше кода! Надеюсь, это имеет смысл (или вы видите, что не так, ха-ха)
Node класс отключен, кстати, это больше похоже на один связанный список, чем на дерево. Опять же, пожалуйста, вставьте полный рабочий код, так как функции DFS, похоже, не работают с вашими классами.
Я думаю, что добавил все это сейчас (у меня был неправильный класс Node, извините). Но это просто беспорядок. Должен ли я просто вернуться к старому коду, который, как я знаю, работает?
Я бы начал с рабочего решения (убедитесь, что оно работает).
Да, спасибо, я так и сделаю. Это определенно работает.





Есть ли причина, по которой вы напрямую инициализируете свои частные переменные в классе Person как rvalue (т.е. std::move?)? std::string может связывать разрешенные значения r, если они являются константами.
Например, код ниже:
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(std::string firstName, std::string lastName, int age, std::string gender, bool alive) \
: firstName_{firstName}, lastName_{lastName}, age_{age}, gender_{gender}, alive_{alive} {}
Создание членов в Person rvalue будет подготавливать их к перемещению, что не похоже на то, что вы делаете ранее в коде.
template <typename T>
void Person<T>::setFirstName(const std::string &firstName)
{
firstName_ = firstName;
}
Значения передаются как ссылки lvalue в параметрах функции Person, которые вы меняете на rvalues в конструкторе указанного класса. В этом нет необходимости. Они не предназначены для временных значений. Использование {} вместо () исключает возможность неявного преобразования (сужения) части членов.
Это то, чему меня учили. Учитель сказал, что так лучше. Почему, толком не объясняет. Это очень расстраивает.
Привет, ifindoubt29, это больше похоже на вопрос / комментарий, чем на ответ, я бы рекомендовал размещать подобные вещи в разделе комментариев под ОП, а не в качестве ответа. Если вы считаете, что привязка std::string rvalue может быть здесь полезна, рассмотрите возможность улучшения своего ответа, добавив фрагмент кода, чтобы продемонстрировать, как это решает этот вопрос в ОП.
Я это вижу, по крайней мере сейчас. Я новичок в комментировании. Извините за это
Это не дает ответа на вопрос. Как только у вас будет достаточно репутация, вы сможете комментировать любой пост; вместо этого давать ответы, не требующие пояснений от спрашивающего. - Из обзора
Вы думаете наизнанку или вверх ногами - вы должны проходят лямбда (или другая функция) для этой функции, и это должно применить эту функцию в глубине.
Вам также нужна вспомогательная функция, которая принимает Node*, указывающую на текущий узел.
Очень простой пример с предварительным обходом:
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_);
}
И вы должны использовать его примерно так:
FamilyTree tree = ... whatever ...;
auto process = [](const Node* p) { ... print p ... };
// This will now print in preorder.
tree.traverse(process);
Совершенно непонятно для меня, что он должен делать и в чем проблема. Ваша строка
node(this);- загадка, так как я не вижу ни объекта, ни контекста класса. Пожалуйста, обновите свой вопрос, чтобы предоставить нам необходимое содержание.