C#函数式编程之递归调用
关于递归相信大家已经熟悉的不能再熟悉了,所以笔者在这里就不多费口舌,不懂的读者们可以在博客园中找到很多与之相关的博客。下面我们直接切入正题,开始介绍尾递归。
尾递归
普通递归和尾递归如果仅仅只是从代码的角度出发来看,我们可能发现不了他的特点,所以笔者利用两张堆栈上的图来展示具体的差距在哪,首先我们来看看普通的递归调用的情况,如下图1.1所示:

假设这里执行的函数是Func1,并且Func1中通过递归调用了自己,那么我们可以看到栈上在每次调用Func1的时候都会重新将函数返回地址等其他参数放入栈中,在递归次数较少的情况下,这样是不会有问题的。但如果递归调用次数达到一定的数量级,则会将栈空间消耗光。因此,就提出了尾递归。而尾递归的栈图如1.2所示:

一样还是递归,但是每次执行自身的时候并不会在栈空间中申请新的空间,类似于for循环的效果,面对递归次数很多的情况下也不会出现什么问题。但是新的问题就出来了,在C#中编译器不会做到这一步优化,而是在jit编译器执行时才会进行优化。并且只有64位才进行优化。在语言的层面上我们也要遵守一定的原则,才能让编译器知道去优化。当然有些喜欢看博客的人可能早就知道尾递归就是在最后return的时候调用自身。我们可以通过一串示意代码来看尾调用:
int Func1()
{
return Func1();
}
当然上面这串代码会形成一个死循环,因为这里我们没有基线条件。下面我们举一个例子:

这个函数想必应该会比较熟悉,就是计算阶乘的。但是我们可以发现函数sunfc最后的返回语句并不是直接调用函数本身,而是x*sfunc(x -1),恰恰就是因为前面这个x*就会导致编译器无法优化,从而只能采用普通的递归调用的方式去执行,那么我们就需要利用一些模式去改变,首先我们先介绍的是“累加器传递模式”,可能名字比较悬乎,其实就是将当前的计算结果传递给下一次调用函数中,这样当到达基线条件后直接根据上次计算的结果算出最终结果返回即可,如果将上面的代码采用这个模式就是下面这个样子:

采用这个模式之后我们就变回了尾递归了,当执行到基线条件时,直接返回y的值即可。根本不需要回溯到以前。除了利用这种模式,我们还可以利用一种“后继传递模式”,跟累加器传递模式一样也需要修改函数签名,增加一个参数,我们继续修改上面这串代码:

相比累加器传递模式,这种方式比较难理解,其实sfunc在到达基线条件时y就等同于下面这个lambda表达式:a => a*4*3*2,然后就是调用y(1)就直接计算最终的结果了。在简单点就是y这个函数被包装了了好几层,比如上面这段函数执行结束时y的调用顺序:
a为1传递给y(2 * a),结果就是y(2)。
a为2传递给y(3 * a),结果就是y(6)。
a为6传递给y(4 * a),结果就是y(24)。
a为24传递给x => x,输出24。
如果还是不理解只能下断点,调试自己琢磨琢磨了,实在不懂的可以Q问。
C#函数式编程之递归调用的更多相关文章
- 简学Python第三章__函数式编程、递归、内置函数
#cnblogs_post_body h2 { background: linear-gradient(to bottom, #18c0ff 0%,#0c7eff 100%); color: #fff ...
- C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
C#中的函数式编程:递归与纯函数(二) 在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...
- C#中的函数式编程:递归与纯函数(二)
在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential transparency)来定义的.如果一个 ...
- python学习,day3:函数式编程,递归和高阶函数
# coding=utf-8 # Author: RyAn Bi def calc(n): #递归 print(n) if int(n/2) > 0: #设置条件,否则会循环999 次,报错, ...
- C#函数式编程-高阶函数
随笔分类 -函数式编程 C#函数式编程之标准高阶函数 2015-01-27 09:20 by y-z-f, 344 阅读, 收藏, 编辑 何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的 ...
- paip.函数式编程方法概述以及总结
paip.函数式编程方法概述以及总结 1 函数式编程:函数式风格..很多命令式语言里支持函数式编程风格 1.1 起源 (图灵机,Lisp机器, 神经网络计算机) 1.2 函 ...
- 学会JavaScript函数式编程(第1部分)
摘要: JS函数式编程入门. 原文:学会使用函数式编程的程序员(第1部分) 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 在这篇由多部分组成的文章中,接下来将介绍函数式编程的一些概念 ...
- Python学习(26):Python函数式编程
转自 http://www.cnblogs.com/BeginMan/p/3509985.html 前言 <core python programming 2>说: Python不大可能 ...
- Java函数式编程和lambda表达式
为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...
随机推荐
- TCP控制拥塞的四种算法:慢开始,拥塞避免,快重传,快恢复
我们在开始假定: 1:数据是单方向传递,另一个窗口只发送确认. 2:接收方的缓存足够大,因此发送方的大小的大小由网络的拥塞程度来决定. 一:慢开始算法和拥塞避免算法 发送方会维持一个拥塞窗口,刚开始的 ...
- Python在安装第三方模块遇到的问题及解决办法
今天在安装第一个模块psutil的时候出现了以下问题: 1.找不到Python.h文件 解决办法:重新安装python环境:sudo apt-get install python-dev 说明:网上有 ...
- {Reship}{C#}{GDI+}GDI+画笔,线,区域类型
=================================================================================== This article is ...
- pcm跟.wav文件的关系
PCM(Pulse Code Modulation----脉码调制录音).所谓PCM录音就是将声音等模拟信号变成符号化的脉冲列,再予以记录.PCM信号是由[1].[0]等符号构成的数字信号,而未经过任 ...
- Shape comparison language
形状比较语言, 九交模型 In this topic About shape comparison language Dimensionality Extensions to the CBM SC ...
- ssh连接速度慢解决
这些天,公司新安装的几台服务器,发现SSH本地连接上去,速度非常慢,慢的难以忍受,大概30秒左右. 后来到网上查了下,尝试了一种办法,效果还可以,特发上来给大家共享. [root@data208 ~] ...
- html制作,点击文字超链接显示文本框,再点击文字超链接隐藏文本框
</head><body> <script> window.onload=function(){ document.getElementById('click'). ...
- 自定义Dialog之信息提示
对话框对于应用也是必不可少的一个组件,在Android中也不例外,对话框对于一些提示重要信息,或者一些需要用户额外交互的一些内容很有帮助. 自定义Dialog步骤: 1.主要创建Java类,并继承Di ...
- LeetCode OJ 147. Insertion Sort List
Sort a linked list using insertion sort. Subscribe to see which companies asked this question 解答 对于链 ...
- IE、FF、Safari、OP不同浏览器兼容报告
IE.FF.Safari.OP不同浏览器兼容报告 1 浏览器内核简介 Trident IE浏览器(GreenBrowser绿色浏览器, 遨游浏览器....都是IE) Geckos Fi ...