我有一个我需要实现的算法,它会大量跳过它的代码,很多条件检查和跳过步骤,回到之前的步骤,以及所有的爵士乐。
它可能可以用循环和ifs实现,但这将是一个非常混乱,维护和调试非常困难。
除了我将算法的每个'子部分'作为一个单独的函数编写并像这样处理它,所有这些函数相互调用。但是这个算法会运行很长时间(这是一个NPcomplete问题,所以是......),所以我很确定这会在某种程度上导致堆栈溢出。
所以我想到的最后一个选项是使用goto的。现在我总是听说,一旦你开始使用goto,你应该认真重新考虑你的设计,这就是为什么我问是否有更好的方法来做到这一点?
这里是伪代码:
正如你所看到的那样,它可以跳过一个并且进行大量的检查和条件跳过。我只是完全按照这个伪代码中描述的方式添加标签,并完全按照代码中的描述使用goto。我在谈论函数的另一个选择是将算法的每个点放在不同的函数中,但由于提到的原因,我认为这不是一个好主意
答案 0 :(得分:3)
这正是尾递归的用途。它伪装成看起来像一个函数调用。
使用一种语言来保证尾部调用的语义,或者使用具有良好尾部递归优化的编译器来处理不适合的语言。
例如,假设我们有一个这种形式的状态机:
{
int state = S0 // main state variable
int v1 = v1_initval, v2 = ..., ..., vn; // additional state variables
while (state != S_ACCEPT) {
switch (state) {
case S0:
// do whatever
state = S5;
break;
case S2;
...
case SN;
...
}
}
}
这可以变成尾递归:
// case S0:
void S0(int v1, int v2, ..., int vn) {
// do whatever
S5(v1, v2, ..., vn); // state = S5
}
通过调用S0启动机器,S0传递状态变量的正确初始值。然后尾调用直接进行状态转换。
在尾调用优化下,S5
调用成为无条件分支,并且传递的参数只是赋值,编译器可以将其设置为noop,因为在目标函数中,{{{ 1}}参数占用与调用者中v1
参数相同的存储位置。
此外,C ++中迭代状态机的可能骨架:
v1
你几乎可以通过添加一个memer函数使其看起来像递归,这个函数被调用以设置成员变量的下一个值。这些功能可能以此结束:
class statemachine {
private:
int sv1, sv2, ... , svn; // extra state variables
void (statemachine::* state)(); // pointer-to-member: main state var
void S0();
void S1();
// ...
void S_ACCEPT();
public:
statemachine();
void run();
};
statemachine::statemachine()
: state(&statemachine::S0) // initial state is S0
, sv1(...) // initializes for state vars
...
{
}
void statemachine::run()
{
while (state != &statemachine::S_ACCEPT)
(this->*state)();
}
void statemachine::S0()
{
// modify state vars
state = &statemachine::S5;
}
void statemachine::S_ACCEPT()
{
abort(); // never called
}
其中next(&statemachine::S42, v3, v1 + v2, ...);
只是一个包装函数,用于初始化next
变量以及其参数中的其他变量。所以在这里,下一个状态将是state
,而S42
将采用v1
的值,v3
接受v2
等等。