如何从深层递归中跳出?

ariesjzj
博客专家认证
2006-02-08 06:34:43
例如我用递归求一个解,当求到解时我想直接跳到主函数结束程序,怎么做?


int haveSolution = 0;

void proc()
{
if (得到可行解)
haveSolution = 1;
//跳到main函数注释行
else
//递归调用
proc();
return;
}

void main()
{
proc();
if (haveSolution) /*跳到这里 */
cout << "got the solution";
else
cout << "not";
return;
}

可以用什么方法及详细使用方法?谢谢
...全文
1213 26 打赏 收藏 举报
写回复
26 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
ariesjzj 2006-02-10
非常感谢各位,真得学到很多很多东西,特别感谢ox_thedarkness
  • 打赏
  • 举报
回复
sjjf 2006-02-09
如果你认为它是递归的话,那就不要跳出来,"递"字就是依次,顺序的意思。
否则你用循环来做。

如果你硬要从递归中跳出来,逻辑上就是错误的。
  • 打赏
  • 举报
回复
vc_hunter 2006-02-09
楼上是什么方法?
  • 打赏
  • 举报
回复
zeusnchen 2006-02-09
何必搞这么麻烦
throw一个exception就搞定了三
  • 打赏
  • 举报
回复
vc_hunter 2006-02-09
可以用内嵌汇编的方式修改ebp或esp,这方法虽然可用,但会使程序变得很难看懂。而且,不对WIN32汇编有一定了解,实现起来不是很容易
  • 打赏
  • 举报
回复
ariesjzj 2006-02-09
我表述不太清楚,可能大家还不太明白我的意思
比如在一个10*10的格子里求一个满足一定条件的可行解(注意,一个就行了)
我希望找到一个就马上退出,否则要每次都要算完2的100次方的运算是很费时间的
另外,goto是不能在main函数和子函数之间跳的
  • 打赏
  • 举报
回复
alvachien 2006-02-09
简单,直接跳使用throw一个exception就出来了。
完全可以通过throw一个特定的类型来达到直接跳出的效果。
  • 打赏
  • 举报
回复
lisypro 2006-02-09
直接跳不可能
  • 打赏
  • 举报
回复
pagechen 2006-02-09
我想longjmp 和 setjmp 两个函数可以帮助你。
不过如果不理解内涵,比较难用
  • 打赏
  • 举报
回复
fine10000 2006-02-09
学习
  • 打赏
  • 举报
回复
lyclowlevel 2006-02-09
这个简单。
设置一个变量用于控制递归调用。
int haveSolution = 0;

void proc()
{
if(!haveSolution)
{
if (得到可行解)
haveSolution = 1;
//跳到main函数注释行
else
//递归调用
proc();
}
}

void main()
{
proc();
if (haveSolution) /*跳到这里 */
cout << "got the solution";
else
cout << "not";
return;
}
  • 打赏
  • 举报
回复
pagechen 2006-02-09
不错,思路表清晰
  • 打赏
  • 举报
回复
ox_thedarkness 2006-02-09
厄,很有趣的问题。分别有 C、 C++ 两种方法可以解决你的问题。

C方法是:使用标准库<csetjmp>或者<setjmp.h>中的 setjmp 和 longjmp。注意,前面所谓 goto 的方法是错误的。 goto 不能完成函数间跳转。

C++方法简洁得多: 抛出异常;函数调用时则用 try ... catch 捕获异常。

下面给出C++方法的一个例子。

// 判断 n 是否奇数
// 结果由 throw 抛出,奇数抛出true,否则抛出false
void isOdd( unsigned int n ){
if( n == 0 ) throw false;
if( n == 1 ) throw true;
isOdd( n - 2 );
}

不过调用起来就有些麻烦:

int main(){
// 用法类似这样
try{
isOdd( 21 );
}catch( bool odd ){
cout<< ( odd? "odd" : "even" )<< endl;
}
}
-----------------------------------------------------

一个问题是,上面三种方法都不是透明的。isOdd 不能像一般函数一样使用,而必须详细阅读和理解设计者的文档,正确使用晦涩的 __asm、 setjump 、try catch等等、否则会令程序崩溃。这不是好的设计。

一个解决方案是:给递归部分一个壳。使用两个函数,递归函数负责递归,壳函数负责这些复杂的调用过程,用户只调用壳函数。

为了避免用户调用递归函数,以及命名冲突问题,可以使用 nest function 技术。 C/C++本身不直接支持内嵌函数,但是我们可定义一个局部 struct, 把内嵌的函数定义为其 static 成员:

// 若 n 是奇数,返回true, 否则返回 false
// 内嵌函数递归 + 通过异常机制实现
bool isOdd( unsigned int n ){
// 一个Nest struct, 用于定义一个 nest 函数
struct Nst {
// 递归计算n是否奇数,计算完成时throw bool
static void recurve( unsigned int n ){
if( n == 0 ) throw false;
if( n == 1 ) throw true;
recurve( n - 2 );
}
};

try{
Nst::recurve( n );
}catch( bool odd ){
return odd;
}
}

Nst 是 isOdd 函数内部定义的结构,他的名字只在 isOdd 内可见。所以你可以在所有函数内一致使用 Nst 作为内嵌结构的名字,而不会出现任何冲突。
现在 isOdd 是一个正常函数,可以正常调用了:

int main(){
cout<< ( isOdd( 32 )?"odd":"even" ) <<endl;
}


下面是C风格的实现:
-----------------------------------------------------
我们使用<setjmp.h>中 setjmp 和 longjmp的方法实现跳转。

注意,纯C中结构不能有成员函数,所以nest function 技巧行不通了。另外C不支持bool,所以我们返回int。当然 C++ 编译器可以继续支持混合程序的技巧。

为了符合C标准,我们将定义一个额外的函数,两个额外的静态全局变量。这些额外的标识符都使用长名称,并且加上static限定词 —— 以便避免对全局名字空间更大的污染。

另外, C99之前的C标准中,外部连接的标识符只有前六个字母有效 —— 所以若是不加static限定词,我们就给上面的三个标识符取长长的名字就有危险啦,譬如那个recurve_is_odd。(C99之前的标准中,内部连接的标识符前31个字母为有效字符)

// is_odd函数的实现细节。
// !不要直接调用recurve_is_odd函数、或者访问下面两个变量
static jmp_buf jb_is_odd;
static int n_is_odd;
static void recurve_is_odd( unsigned int n ){
if( n == 0 ) { n_is_odd = 0; longjmp( jb_is_odd, 1 ); }
if( n == 1 ) { n_is_odd = 1; longjmp( jb_is_odd, 1 ); }
recurve_is_odd( n - 2 );
}

// 若 n 是奇数,返回true, 否则返回 false
// 函数递归, 通过setjmp longjmp实现
int is_odd( unsigned int n ){
if( setjmp( jb_is_odd ) == 0 )
recurve_is_odd( n );
else
return n_is_odd;
}

int main(){
if( is_odd( 23 )) printf( "odd\n" );
else printf( "even\n" );
}

代码在 VC7.1 和Dev-C++ 4.9 下测试通过。
  • 打赏
  • 举报
回复 1
pagechen 2006-02-09
#include <setjmp.h>

int haveSolution = 0;

jmp_buf mark;

void proc()
{
if (1)
{
haveSolution = 1;
//跳到main函数注释行
longjmp( mark, -1 );
}

else
//递归调用
proc();
return;
}

void main()
{
int jmpret;

jmpret = setjmp( mark );
if( jmpret == 0 ) proc();
if (haveSolution) /*跳到这里 */
cout << "got the solution";
else
cout << "not";
return;
}

测试成功
  • 打赏
  • 举报
回复
pagechen 2006-02-09
Example

/* FPRESET.C: This program uses signal to set up a
* routine for handling floating-point errors.
*/

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <string.h>

jmp_buf mark; /* Address for long jump to jump to */
int fperr; /* Global error number */

void __cdecl fphandler( int sig, int num ); /* Prototypes */
void fpcheck( void );

void main( void )
{
double n1, n2, r;
int jmpret;
/* Unmask all floating-point exceptions. */
_control87( 0, _MCW_EM );//估计是浮点运算,忽略
/* Set up floating-point error handler. The compiler
* will generate a warning because it expects
* signal-handling functions to take only one argument.
*/
if( signal( SIGFPE, fphandler ) == SIG_ERR )//设置回调

{
fprintf( stderr, "Couldn't set SIGFPE\n" );
abort(); }

/* Save stack environment for return in case of error. First
* time through, jmpret is 0, so true conditional is executed.
* If an error occurs, jmpret will be set to -1 and false
* conditional will be executed.
*/
jmpret = setjmp( mark );//设置跳转标签
if( jmpret == 0 )
{
printf( "Test for invalid operation - " );
printf( "enter two numbers: " );
scanf( "%lf %lf", &n1, &n2 );
r = n1 / n2;//出错时调用fphandler
/* This won't be reached if error occurs. */
printf( "\n\n%4.3g / %4.3g = %4.3g\n", n1, n2, r );

r = n1 * n2;
/* This won't be reached if error occurs. */
printf( "\n\n%4.3g * %4.3g = %4.3g\n", n1, n2, r );
}
else
fpcheck();
}
/* fphandler handles SIGFPE (floating-point error) interrupt. Note
* that this prototype accepts two arguments and that the
* prototype for signal in the run-time library expects a signal
* handler to have only one argument.
*
* The second argument in this signal handler allows processing of
* _FPE_INVALID, _FPE_OVERFLOW, _FPE_UNDERFLOW, and
* _FPE_ZERODIVIDE, all of which are Microsoft-specific symbols
* that augment the information provided by SIGFPE. The compiler
* will generate a warning, which is harmless and expected.

*/
void fphandler( int sig, int num )//一个浮点计算出错的回调函数
{
/* Set global for outside check since we don't want
* to do I/O in the handler.
*/
fperr = num;
/* Initialize floating-point package. */
_fpreset();
/* Restore calling environment and jump back to setjmp. Return
* -1 so that setjmp will return false for conditional test.
*/
longjmp( mark, -1 );//跳转到mark,并传参数-1
}
void fpcheck( void )
{
char fpstr[30];
switch( fperr )
{
case _FPE_INVALID:
strcpy( fpstr, "Invalid number" );
break;
case _FPE_OVERFLOW:
strcpy( fpstr, "Overflow" );

break;
case _FPE_UNDERFLOW:
strcpy( fpstr, "Underflow" );
break;
case _FPE_ZERODIVIDE:
strcpy( fpstr, "Divide by zero" );
break;
default:
strcpy( fpstr, "Other floating point error" );
break;
}
printf( "Error %d: %s\n", fperr, fpstr );
}
Output

Test for invalid operation - enter two numbers: 5 0
Error 131: Divide by zero


你的程序应该可以修改成:
#include <setjmp.h>

int haveSolution = 0;

jmp_buf mark;


void proc()
{
if (得到可行解)
{
haveSolution = 1;
//跳到main函数注释行
longjmp( mark, -1 );
}

else
//递归调用
proc();
return;
}

void main()
{
int jmpret;

jmpret = setjmp( mark );

if( jmpret == 0 ) proc();
if (haveSolution) /*跳到这里 */
cout << "got the solution";
else
cout << "not";
return;
}


我没有测试,参数可能不正确
  • 打赏
  • 举报
回复
pagechen 2006-02-09
longjmp 和 setjmp 就是C实现远程跳转的好方法,是对修改ebp方法的封装。

throw 好吗?假设你在第100层递规中药退出,你要throw多少层啊?浪费啊
  • 打赏
  • 举报
回复
pankun 2006-02-09
递归要一步步ret,不然堆栈不会平衡.用楼上改esp的方法当然也可以.
  • 打赏
  • 举报
回复
vc_hunter 2006-02-09
瞎写的,虽然可行,但不值得推荐。。。代码结构混乱
  • 打赏
  • 举报
回复
vc_hunter 2006-02-09
/*实现了深度递归加跳出,或许该叫递推?*/
#include "stdafx.h"
#include "windows.h"
int digui(int n,int value,int retadd)
{
if(retadd==0)
{
__asm
{
mov retadd,ebp //保存第一次进入时的返回地址
}

}
if(n==1)
{
__asm //求得结果后直接返回main
{
mov eax,value
mov ebp,retadd
mov esp,ebp
pop ebp
ret
}
}
else
{
digui(n-1,value*2,retadd);
}

::Sleep(9999999999);
return 0;
}
void main(int argc, char* argv[])
{
int a=digui(5,2,0);//求2的5次方
printf("%d",a);



}
  • 打赏
  • 举报
回复
dbbush 2006-02-08
int haveSolution = 0;

void proc() //带参数n
{
if (得到可行解) //递归结束条件,比如说n==1
haveSolution = 1;
//跳到main函数注释行 //直接跳出函数就可以
else
//递归调用
*********** //n到n-1步骤需要的操作
proc(); //带参数n-1
return;
}

void main()
{
proc();
if (haveSolution) /*跳到这里 */ //proc函数结束不就执行这条指令吗
cout << "got the solution"; //为什么要跳
else
cout << "not";
return;
}
  • 打赏
  • 举报
回复
加载更多回复(6)
相关推荐
发帖
C++ 语言

6.2w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
帖子事件
创建了帖子
2006-02-08 06:34
社区公告
暂无公告