满足动态条件时退出递归函数

Quit recursive function when a dynamic condition is met

本文关键字:退出 递归函数 条件 动态 满足      更新时间:2023-10-16

使用生成汉明距离内的所有位序列 t 中的函数:

void magic(char* str, int i, int changesLeft) {
if (changesLeft == 0) {
printf("%sn", str);
return;
}
if (i < 0) return;
// flip current bit
str[i] = str[i] == '0' ? '1' : '0';
magic(str, i-1, changesLeft-1);
// or don't flip it (flip it again to undo)
str[i] = str[i] == '0' ? '1' : '0';
magic(str, i-1, changesLeft);
}

我想退出递归函数并在发生某种情况时返回调用者函数(如果确实如此)。所以就像我的递归函数听到可能告诉她退出的声音一样!

它仅在打印str发生,如下所示:

if (changesLeft == 0) {
printf("%sn", str);
int quit_now = voices(str);
return;
}

如何做到这一点(停止展开递归并返回函数调用者)?


尝试:

if (i < 0 || quit_now == 1) return;

只是似乎阻止了执行并且永无止境!

PS - 我什至对 c 旧方法感兴趣。

鉴于函数当前没有返回值,一个简单的解决方案是使用它来指示是否满足该终止条件。 然后,如果结果变为 true,您可以使用它立即退出所有递归调用。

不确定我是否在这里正确捕获了您的预期逻辑,但直观的方法是这样的:

int magic(char* str, int i, int changesLeft) {
int result;
if (changesLeft == 0) {
printf("%sn", str);
return voices(str);
}
if (i < 0) return 0;
// flip current bit
str[i] = str[i] == '0' ? '1' : '0';
result = magic(str, i-1, changesLeft-1);
if( !result ) {
// or don't flip it (flip it again to undo)
str[i] = str[i] == '0' ? '1' : '0';
result = magic(str, i-1, changesLeft);
}
return result;
}

用最简单的形式来说,你可以做这样的事情:

void foo(bool & ret) {
// doStuff...
if (ret) return;
foo(ret);
// doStuff...
if (ret) return;
foo(ret);
}

然后启动递归:

bool ret = false;
foo(ret);

在您的情况下,您可以通过以下方式中断递归

if (!changesLeft) {
printf("%sn", str);
ret = true;
return;
}

设置为 true 将使您退出整个调用树。

你也可以用 C 语言做到这一点,只需使用指针而不是引用即可。

magic函数在两个地方递归调用自身。 因此,在每个地方,您都需要检查退出条件。 帕迪给出的答案详细说明了这一点。

立即展开堆栈的另一种方法是使用setjmplongjmp它们可以用作非本地goto.

jmp_buf magic_buf;
void magic_int(char* str, int i, int changesLeft) {
if (changesLeft == 0) {
printf("%sn", str);
if (voices(str)) {
longjmp(magic_buf, 1);
}
return;
}
if (i < 0) return;
// flip current bit
str[i] = str[i] == '0' ? '1' : '0';
magic_int(str, i-1, changesLeft-1);
// or don't flip it (flip it again to undo)
str[i] = str[i] == '0' ? '1' : '0';
magic_int(str, i-1, changesLeft);
}
void magic(char* str, int i, int changesLeft) {
if (!setjmp(magic_buf)) {
magic(str, i, changesLeft);
}
}

setjmp函数在直接调用时返回0。 调用longjmp时,实际返回的是setjmp函数,返回值是给longjmp的第二个参数。

在这里,我们有一个调用setjmp的包装函数。 这将设置调用longjmp时的跳转点。 然后调用递归函数。 稍后,当递归函数"听到声音"告诉它现在退出时,它会调用longjmp,该调用立即直接进入相应的setjmp调用。

这些功能在C99和POSIX中都有指定,因此符合POSIX的系统(即Linux)应该在C89模式下仍然可以使用这些功能。

如果要在C++中执行此操作,首选方法是在递归函数中抛出异常并在包装函数中捕获它。

struct magic_voices {
int rval;
};
void magic_int(char* str, int i, int changesLeft) {
if (changesLeft == 0) {
printf("%sn", str);
int rval = voices(str);
if (rval) {
struct magic_voices ex;
ex.rval = rval;
throw ex;
}
return;
}
if (i < 0) return;
// flip current bit
str[i] = str[i] == '0' ? '1' : '0';
magic_int(str, i-1, changesLeft-1);
// or don't flip it (flip it again to undo)
str[i] = str[i] == '0' ? '1' : '0';
magic_int(str, i-1, changesLeft);
}
void magic(char* str, int i, int changesLeft) {
try {
magic(str, i, changesLeft);
} catch (struct magic_voices ex) {
printf("rval=%dn", ex.rval);
}
}

这是一个非递归变体。基本上,它生成所有递增的序列0 <= a[0] < ... < a[dist-1] < strlen(num),并在相应的索引处还原位。

void print(const char* num, const vector<int>& a) {
size_t k = 0, n = strlen(num);
for (size_t i = 0; i < n; ++i)
if (k < a.size() && a[k] == i) {
cout << (num[i] == '0') ? '1' : '0';
++k;
}
else
cout << num[i];
cout << endl;
}
void hamming(const char* num, size_t dist) {
assert(dist > 0);
vector<int> a(dist);
size_t k = 0, n = strlen(num);
a[k] = -1;
while (true)
if (++a[k] > n - dist + k)
if (k == 0)
return;
else {
--k;
continue;
}
else
if (k == dist - 1) {
print(num, a);
// conditional return here
}
else {
a[k+1] = a[k];
++k;
}
}

可以这样使用:

int main()
{
hamming("0000", 2);
/* output:
1100
1010
1001
0110
0101
0011
*/
}

附言感谢@ruakh提到while - if条件下缺少优化。