How to handle recursion in member functions?

天涯浪子 提交于 2020-06-27 18:35:27

问题


E.g., I have an empty function to clear a linked list:

void empty(Node* head) {
        if (head->next) { empty(head->next); }
        delete head;
        head = nullptr;
    }

But then I created a class for the linked list, so now I don't need to pass the head argument:

void empty() {
        if (head->next) { empty(head->next); }
        delete head;
        head = nullptr;
    }

But empty(head->next) line is obviously wrong as empty doesn't take any arguments. The idea comes to my mind to create a function inside a function (with lambda), something like this:

void empty() {
        std::function<void(Node*)> emptyWrapper = [&] (Node* l_head) {
            if (l_head->next) { emptyWrapper(l_head->next); }
            delete l_head;
            l_head = nullptr;
        };
        emptyWrapper(head);
    }

But I'm wondering if there's any better way to do it. Lambdas became kind of idee fixe for me recently.


回答1:


A general approach is to declare a public member function that in turn calls a private static recursive member function.

Pay attention that the name empty sounds confusing. It is better to name the function for example like clear.

Here you are

#include <functional>

//...

class List
{
public:
    //...
    void clear() 
    {
        clear( head );
    }

private:
    static void clear( Node * &head )
    {
        if ( head )
        {
            delete std::exchange( head, head->next );
            clear( head ); 
        }
    }
    //...
}

The same approach can be used without defining an auxiliary static function.

void clear()
{
    if ( head )
    {
        delete std::exchange( head, head->next );
        clear();
    }
}

Here is a demonstrative program.

#include <iostream>
#include <iomanip>
#include <functional>

template <typename T>
class List
{
private:
    struct Node
    {
        T data;
        Node *next;
    } *head = nullptr;

public:
    void push_front( const T &data )
    {
        head = new Node { data, head };
    }

    friend std::ostream & operator <<( std::ostream &os, const List &list )
    {
        for ( Node *current = list.head; current; current = current->next )
        {
            os << current->data << " -> ";
        }

        return os << "null";
    }

    bool empty() const { return head== nullptr; }

    void clear()
    {
        if ( head )
        {
            delete std::exchange( head, head->next );
            clear();
        }
    }
};

int main() 
{
    List<int> list;

    const int N = 10;

    for ( int i = N; i != 0; )
    {
        list.push_front( i-- );
    }

    std::cout << list << '\n';

    list.clear();

    std::cout << "list is empty " << std::boolalpha << list.empty() << '\n';

    return 0;
}

The program output is

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> null
list is empty true



回答2:


The straightforward solution is to have a helper function that does the work that your recursive function was doing.

class List{
public:
   void empty(){
        if (head) { empty_helper(head); }
        delete head;
        head = nullptr;
    }
private:
// should probably be static to avoid propagating this.

void empty_helper(Node* head) {
        if (head->next) { empty_helper(head->next); }
        delete head;
        head = nullptr;
    }
};

Of course other options are available, such as making it non recursive.

In my opinion a lambda is unnecessary in this situation.




回答3:


Note: as others have mentioned, you don't need to use recursion here. This example assumes you want to or need for some reason you haven't mentioned. This is how you would do it using recursion. However, restructuring with a loop is probably what you should do in the long run.


You can make a public and private version of the list:

class list {
public:
void empty();

//...
private:
void empty(Node* head);
// alternatively, you could make this static instead:
static void empty(Node* head);

//...

}

Then you can call the empty() that takes a parameter from inside the other empty():

void list::empty() {
    if(this->head) {  // check here so ->next won't fail in the helper function.
                      // maybe you should add a check there instead
        empty(this->head);
    }
}

P.S. you probably shouldn't use the name head all over the place like I have done here. This was just a quick example I threw together. But at least you get the general idea this way.



来源:https://stackoverflow.com/questions/61921720/how-to-handle-recursion-in-member-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!