从上篇文章中,我们了解到为了防止overflow的异常,我们可以使用Continuation,上篇文章也告诉了我们如何将普通递归转换为Continuation,理所当然的,我们会考虑到一个问题:为什么不直接用Continuation呢,或者说,数据量小的时候,我们该作何选择?
当看完上篇文章之后,我也想过用最为简单的Fibonacci作为例子,来一次普通递归到CSP的转换,以下我们先来看看普通递归的Fibonacci实现:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 module FibRecursive = 2 let rec fib n = 3 match n with 4 | 1 5 | 2 -> 1 6 | _ -> ( fib (n-1) ) + (fib (n-2) ) 7 8 fib 30
使用该文章的方式,我们得到如下CSP实现结果,代码前不久我也已经放到fssnip中:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 module FibContinuation = 2 let rec fib n count = 3 match n with 4 | 1 5 | 2 -> count (1) 6 | _ -> 7 let first x = 8 let second y = 9 count(x + y) 10 fib (n-2) second 11 fib (n-1) first
由于这次测试的数据并不大,所以打开任务管理器并没有发现运行时两者的区别有多大,于是,我选择使用stopwatch来看看它们彼此的运行时间,将代码放入stopwatch中,运行后,我得到了如下结果:
首先是普通递归的结果如下:
接下来是CSP的结果如下:
当然,你也可以尝试着多用一些数据来证明这么一个结论,鉴于篇幅问题,本文只用这一组数据进行解释,因此,从上面两幅图来看,在数据量小到普通递归能承受的范围时,普通递归的效率会高于CSP的方式。
番外篇:
当你首次在F#中使用尾递归时,你可能会发现一件很奇怪的事情,明明文章说CSP方式不会有异常,可是在VS editor里面选择F5或者Ctrl+F5的方式运行代码时,你依然会看到StackOverflowException,即使你的数据真的很小,普通递归都没有异常的情况下,CSP居然抛出了异常,如下图所示:
这不禁让我们倒吸了一口凉气,为什么会这样呢?可是,当你试着使用FSI或者fsc.exe的方式又会发现代码能够正常运行,这莫非是VS的bug么?当我请教在微软从事F#语言多年的刘涛时,他告诉我,其实不然,只是,当VS设定在Debug模式下,尾递归的功能默认是关闭的,所以当你要使用尾递归的时候,你可以将VS设定在Release的模式,或者,你也可以在Debug模式下打开尾递归的功能:
1. 在项目管理器中对选定项目右键,选择“Properties”
2. 打开“Build”选项卡
3. 确保“Debug” 配置是选中的状态
4. 将“Generate tail calls”复选框选中,如下图所示:
好吧,接下来你可以再试着运行你的CSP程序,相信你会看到令你满意的结果的~祝愿你F#旅程愉快!
这篇文章几天前我写在了blogspot中,一个原因是国内网络不容易访问到,还有一个原因是该文章为英文,因此写成中文版放在这里,希望对大家有一定的帮助。同时,有兴趣的朋友可以点击以下链接进行原文访问:http://jeallynduan.blogspot.com/2012/11/why-continuation-function-also-throw.html http://jeallynduan.blogspot.com/2012/11/different-efficiency-between-tail.html