F# 普通递归与尾递归之间的效率区别
2012-11-06 15:51
309 查看
从上篇文章中,我们了解到为了防止overflow的异常,我们可以使用Continuation,上篇文章也告诉了我们如何将普通递归转换为Continuation,理所当然的,我们会考虑到一个问题:为什么不直接用Continuation呢,或者说,数据量小的时候,我们该作何选择?
当看完上篇文章之后,我也想过用最为简单的Fibonacci作为例子,来一次普通递归到CSP的转换,以下我们先来看看普通递归的Fibonacci实现:
View Code
由于这次测试的数据并不大,所以打开任务管理器并没有发现运行时两者的区别有多大,于是,我选择使用stopwatch来看看它们彼此的运行时间,将代码放入stopwatch中,运行后,我得到了如下结果:
首先是普通递归的结果如下:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810211191.png)
接下来是CSP的结果如下:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810220430.png)
当然,你也可以尝试着多用一些数据来证明这么一个结论,鉴于篇幅问题,本文只用这一组数据进行解释,因此,从上面两幅图来看,在数据量小到普通递归能承受的范围时,普通递归的效率会高于CSP的方式。
番外篇:
当你首次在F#中使用尾递归时,你可能会发现一件很奇怪的事情,明明文章说CSP方式不会有异常,可是在VS editor里面选择F5或者Ctrl+F5的方式运行代码时,你依然会看到StackOverflowException,即使你的数据真的很小,普通递归都没有异常的情况下,CSP居然抛出了异常,如下图所示:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810225762.png)
这不禁让我们倒吸了一口凉气,为什么会这样呢?可是,当你试着使用FSI或者fsc.exe的方式又会发现代码能够正常运行,这莫非是VS的bug么?当我请教在微软从事F#语言多年的刘涛时,他告诉我,其实不然,只是,当VS设定在Debug模式下,尾递归的功能默认是关闭的,所以当你要使用尾递归的时候,你可以将VS设定在Release的模式,或者,你也可以在Debug模式下打开尾递归的功能:
1. 在项目管理器中对选定项目右键,选择“Properties”
2. 打开“Build”选项卡
3. 确保“Debug” 配置是选中的状态
4. 将“Generate tail calls”复选框选中,如下图所示:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810231312.jpg)
好吧,接下来你可以再试着运行你的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
当看完上篇文章之后,我也想过用最为简单的Fibonacci作为例子,来一次普通递归到CSP的转换,以下我们先来看看普通递归的Fibonacci实现:
View Code
module FibContinuation = let rec fib n count = match n with | 1 | 2 -> count (1) | _ -> let first x = let second y = count(x + y) fib (n-2) second fib (n-1) first
由于这次测试的数据并不大,所以打开任务管理器并没有发现运行时两者的区别有多大,于是,我选择使用stopwatch来看看它们彼此的运行时间,将代码放入stopwatch中,运行后,我得到了如下结果:
首先是普通递归的结果如下:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810211191.png)
接下来是CSP的结果如下:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810220430.png)
当然,你也可以尝试着多用一些数据来证明这么一个结论,鉴于篇幅问题,本文只用这一组数据进行解释,因此,从上面两幅图来看,在数据量小到普通递归能承受的范围时,普通递归的效率会高于CSP的方式。
番外篇:
当你首次在F#中使用尾递归时,你可能会发现一件很奇怪的事情,明明文章说CSP方式不会有异常,可是在VS editor里面选择F5或者Ctrl+F5的方式运行代码时,你依然会看到StackOverflowException,即使你的数据真的很小,普通递归都没有异常的情况下,CSP居然抛出了异常,如下图所示:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810225762.png)
这不禁让我们倒吸了一口凉气,为什么会这样呢?可是,当你试着使用FSI或者fsc.exe的方式又会发现代码能够正常运行,这莫非是VS的bug么?当我请教在微软从事F#语言多年的刘涛时,他告诉我,其实不然,只是,当VS设定在Debug模式下,尾递归的功能默认是关闭的,所以当你要使用尾递归的时候,你可以将VS设定在Release的模式,或者,你也可以在Debug模式下打开尾递归的功能:
1. 在项目管理器中对选定项目右键,选择“Properties”
2. 打开“Build”选项卡
3. 确保“Debug” 配置是选中的状态
4. 将“Generate tail calls”复选框选中,如下图所示:
![](http://pic002.cnblogs.com/images/2012/120628/2012110810231312.jpg)
好吧,接下来你可以再试着运行你的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
相关文章推荐
- 在c++中尾递归,普通递归,循环的效率对比
- 深度学习和普通机器学习之间有何区别?
- 漂亮女生与普通女生之间的区别:你不是最漂亮的,却是我最珍贵的
- C# 中普通类、抽象类、接口之间的区别
- 服务器与普通电脑之间的区别是什么?
- 迭代 递归 尾递归 效率比较
- 分治算法,动态规划,贪婪算法以及递归之间的区别与联系
- String StringBuffer StringBuilder有什么区别?它们之间的执行效率有什么不同?
- 循环 & 递归 & 递推 之间的联系和区别
- 普通递归与尾递归以及如何在编程阶段和编译阶段支持尾递归
- 贪心,递归,动态规划,及分治算法之间的区别和联系
- 泛型数组列表和普通数组之间的区别
- 深度学习和普通机器学习之间有何区别?
- 尾递归与普通递归简单解释
- 递归与伪递归区别,Python 实现递归与尾递归
- static{}静态代码块与{}普通代码块之间的区别
- 贪心,递归,动态规划,及分治算法之间的区别和联系(三)
- 深度学习和普通机器学习之间有何区别?
- 贪心,递归,动态规划,及分治算法之间的区别和联系(四)
- 阅读“优秀程序员与普通程序员之间的区别”