Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ recursive to iterative

Good afternoon, I was hoping someone here could help me to see what I am missing. I freely admit that this is a homework assignment, but we are permitted to collaborate on code, so hopefully someone here won't mind helping.

For this program, I need to rotate a set of three items in C++ using both recursion and iteration. I have the recursive case working with no issue, but the iterative version is giving me a lot of trouble. Everything I have tried either gives a segfault or just prints infinitely. Here is the code, thanks again for any help:

template<typename A, typename B, typename C>
class Triple{
public:
    A first;
    B second;
    C third;

Triple(A a, B b, C c){ first = a; second = b; third = c;}

A fst(){return first;}
B snd(){return second;}
C thd(){return third;}

// The function change1(), changes the first component of a triple. 
void change1(A a){ first = a;}
};

// A linked list of triples where the order of the triple rotates as it goes.
template<typename A, typename B, typename C>
class RotateList{ 
public:
    Triple<A,B,C> *t;
    RotateList<B,C,A> * next; // Notice the order has changed

    RotateList(Triple<A,B,C> *t1, RotateList<B,C,A> * n1){ this->t = t1; this->next = n1;}

/*
 * Implement with recursion, creating i triples, each with the order rotated from the
 * previous.
 */
static RotateList<A,B,C> * create(A a, B b, C c, int i){
if (i <= 0) return nullptr;
Triple<A,B,C> * t = new Triple<A,B,C>(a,b,c);
RotateList<B,C,A> * n = RotateList<B,C,A>::create(b,c,a, i-1); 

return new RotateList<A,B,C>(t, n);
}

/*
 * Implement with iteration, using the same instance of the same three triples
 * over and over.
 */
static RotateList<A,B,C> * create2(A a, B b, C c, int i){
}

/* Print the whole rotating list. */ 
void print(){
cout << "{" << t->fst() << " "<< t->snd() << " "<< t->thd() << "}";
if (next != nullptr) 
    next->print();
else 
    cout << endl;
}
};


int main(){
float f = 3.14;
int i = 3;
char c = 'c';

Triple<float,int,char> t = Triple<float,int,char>(f,i,c);
Triple<float,int,char> t1 = t;
cout << "Starting triple: [" << t.fst() << " "<< t.snd() << " "<< t.thd() << "]" << endl;

cout << endl << "Rotating list created recursively" << endl;
RotateList<float,int,char> * r= RotateList<float,int,char>::create(f,i,c, 10);
r->print();

r->t->change1(42.42);
r->print();

cout << endl << "Rotating list created iteratively" << endl;
RotateList<float,int,char> * s= RotateList<float,int,char>::create2(f,i,c, 10);
s->print();

s->t->change1(42.42);
s->print();

s->next->t->change1(501);
s->print();
}

My attempt thus far:

static RotateList<A,B,C> * create2(A a, B b, C c, int i) {
    RotateList<C,A,B> *l1 = new RotateList<A,B,C>(new Triple<A,B,C>, nullptr);
    RotateList<B,C,A> *l2;
    RotateList<C,A,B> *l3;        
    RotateList<A,B,C> *tmp1 = l1;
    RotateList<B,C,A> *tmp2;
    RotateList<C,A,B> *tmp3;

    int nextTriple = 2;
    for (i; i > 0; i--) {
        tmp3->next = l1;
        tmp1 = tmp3->next;
        nextTriple = 2;
    } else if {
        temp1->mext = l2;
        tmp2 = tmp1->next;
        nextTriple = 3;
    } else {
        tmp2->next = l3;
        tmp3 = tmp2->next;
        nextTriple = 1;
    }
}
return l1;
}
like image 965
jwilson Avatar asked Oct 31 '22 17:10

jwilson


1 Answers

I'm going to start out with an overview and follow that with an example. If you need something more specific to the assignment, please add a comment, but only after you've mulled over this answer.

Overview

There are two general ways of converting recursion to iteration. One is to re-write the recursive function to use a tail call (i.e. no further processing happens after the recursive call; some compilers, including some C++ compilers, can optimize away the tail call, producing object code that's indistinguishable from an interative version). The other (such as for an iterative Tower of Hanoi solution) is to basically keep track of frames using your own stack. Fortunately, the assignment is solvable via a tail-call rewrite.

Example

All code untested and may contain errors & bugs.

For an example simpler than that assigned, consider a function range(a, b) that produces a list of integers from a (inclusive) to b (exclusive). Recursively:

List<int>* range(int a, int b) {
    if (a >= b) {
       // base case
       return new List<int>();
    }
    // recursive case
    return List<int>::cons(a, range(a+1, b));
}

To convert this to a tail call, you generally add one (or more) variables to accumulate values from the computation, and have separate functions for recursion and initialization:

List<int>* range_recur (int a, int b, List<int>* xs) {
    if (b < a) {
        // base case
        return xs;
    }
    // recursive case
    return range_recur(a, b-1, List<int>::cons(b, xs));
}

List<int>* range(int a, int b) {
    return range_recur(a, b-1, new List<int>());
}

Note a few other changes were needed, mostly with how the bounds are handled, so that the cons operation could be moved to before the recursive call.

The base case corresponds to the loop condition, but it's when to exit the loop. Consequently, let's rewrite range_recur() to be closer to the iterative version by negating the test and swapping the two cases.

List<int>* range_recur (int a, int b, List<int>* xs) {
    // recursive case test
    if (b >= a) {
        // recursive case
        return range_recur(a, b-1, List<int>::cons(b, xs));
    }
    // base case
    return xs;
}

This is fairly straightforward to convert to an iterative version: instead of passing each variable recursively, assign them (possibly using temporaries, if values are mutually dependent). The if condition becomes the loop condition. The initialization function body goes before the loop, the base case after.

List<int>* range (int a, int b) {
    // range():
    List<int> *xs = new List<int>();
    b = b - 1;
    // range_recur():
    // recursive case test:
    while (b >= a) {
        // recursive case
        xs = List<int>::cons(b, xs);
        b = b - 1;
    }
    // base case
    return xs;
}

You'd probably then have a clean-up rewrite, using more idiomatic operations (decrement & a for loop; exercise left to the reader).

Off-Topic

The assignment probably doesn't mention memory management, but it's an important subject. The sample classes leak like a fishing net (and you'll get into trouble using RotateList with temporaries), which mostly won't be a problem as the process won't live long enough for it to become one. If the classes were used in another program, however, it could be very problematic. These days, best practice in production is to use smart pointers of some kind to manage objects. For some assignments (especially those you have a firm grasp of), taking care of memory management yourself is a good exercise.

like image 155
outis Avatar answered Nov 12 '22 20:11

outis