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;
}
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.
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With