C++ 递归到迭代
C++ recursive to iterative
下午好,我希望这里有人能帮我看看我错过了什么。我承认这是一项家庭作业,但我们可以在代码上进行协作,所以希望这里有人不介意帮忙。
对于这个程序,我需要使用递归和迭代在 C++ 中轮换一组三项。我的递归案例没有问题,但迭代版本给我带来了很多麻烦。我尝试过的一切要么给出段错误,要么只是无限打印。这是代码,再次感谢您的帮助:
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();
}
到目前为止我的尝试:
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;
}
我将从一个概述开始,然后再举一个例子。如果您需要更具体的作业,请添加评论,但前提是您仔细考虑了这个答案。
概述
有两种将递归转换为迭代的一般方法。一种是重写递归函数以使用 tail call (i.e. no further processing happens after the recursive call; some compilers, including some C++ compilers,可以优化尾部调用,生成与交互式版本没有区别的目标代码)。另一个(例如迭代汉诺塔解决方案)是基本上使用您自己的堆栈跟踪帧。幸运的是,这个赋值可以通过尾调用重写来解决。
例子
所有代码未经测试,可能包含错误。
举一个比指定的更简单的例子,考虑一个函数range(a, b)
,它产生一个从a
(含)到b
(不含)的整数列表。递归地:
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));
}
要将其转换为尾调用,您通常会添加一个(或多个)变量来累积计算中的值,并具有用于递归和初始化的独立函数:
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>());
}
请注意还需要进行一些其他更改,主要是边界的处理方式,以便 cons
操作可以移到递归调用之前。
基本情况对应于循环条件,但它是何时退出循环。因此,让我们通过否定测试并交换两种情况来重写 range_recur()
以更接近迭代版本。
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;
}
转换为迭代版本相当简单:不是递归地传递每个变量,而是分配它们(如果值相互依赖,可能使用临时变量)。 if
条件成为循环条件。初始化函数体在循环之前,基本情况在循环之后。
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;
}
然后您可能会进行清理重写,使用更多惯用操作(递减和 for
循环;练习留给 reader)。
题外话
作业可能没有提到内存管理,但它是一个重要的主题。示例 类 像渔网一样泄漏(使用 RotateList
和临时变量会遇到麻烦),这在大多数情况下不会成为问题,因为该过程不会存活足够长的时间成为一个。但是,如果 类 在另一个程序中使用,则可能会出现很大问题。如今,生产中的最佳实践是使用某种 smart pointers 来管理对象。对于一些作业(尤其是那些你已经牢牢掌握的作业),自己处理内存管理是一个很好的练习。
下午好,我希望这里有人能帮我看看我错过了什么。我承认这是一项家庭作业,但我们可以在代码上进行协作,所以希望这里有人不介意帮忙。
对于这个程序,我需要使用递归和迭代在 C++ 中轮换一组三项。我的递归案例没有问题,但迭代版本给我带来了很多麻烦。我尝试过的一切要么给出段错误,要么只是无限打印。这是代码,再次感谢您的帮助:
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();
}
到目前为止我的尝试:
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;
}
我将从一个概述开始,然后再举一个例子。如果您需要更具体的作业,请添加评论,但前提是您仔细考虑了这个答案。
概述
有两种将递归转换为迭代的一般方法。一种是重写递归函数以使用 tail call (i.e. no further processing happens after the recursive call; some compilers, including some C++ compilers,可以优化尾部调用,生成与交互式版本没有区别的目标代码)。另一个(例如迭代汉诺塔解决方案)是基本上使用您自己的堆栈跟踪帧。幸运的是,这个赋值可以通过尾调用重写来解决。
例子
所有代码未经测试,可能包含错误。
举一个比指定的更简单的例子,考虑一个函数range(a, b)
,它产生一个从a
(含)到b
(不含)的整数列表。递归地:
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));
}
要将其转换为尾调用,您通常会添加一个(或多个)变量来累积计算中的值,并具有用于递归和初始化的独立函数:
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>());
}
请注意还需要进行一些其他更改,主要是边界的处理方式,以便 cons
操作可以移到递归调用之前。
基本情况对应于循环条件,但它是何时退出循环。因此,让我们通过否定测试并交换两种情况来重写 range_recur()
以更接近迭代版本。
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;
}
转换为迭代版本相当简单:不是递归地传递每个变量,而是分配它们(如果值相互依赖,可能使用临时变量)。 if
条件成为循环条件。初始化函数体在循环之前,基本情况在循环之后。
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;
}
然后您可能会进行清理重写,使用更多惯用操作(递减和 for
循环;练习留给 reader)。
题外话
作业可能没有提到内存管理,但它是一个重要的主题。示例 类 像渔网一样泄漏(使用 RotateList
和临时变量会遇到麻烦),这在大多数情况下不会成为问题,因为该过程不会存活足够长的时间成为一个。但是,如果 类 在另一个程序中使用,则可能会出现很大问题。如今,生产中的最佳实践是使用某种 smart pointers 来管理对象。对于一些作业(尤其是那些你已经牢牢掌握的作业),自己处理内存管理是一个很好的练习。