递归: 就是函数调用自己。 func() { foo(); func(); bar(); }

尾调用:就是在函数的最后,调用函数(包括自己)。 foo(){ return bar(); }

尾递归:就是在函数的最后,调用自身。 func() { foo(); return func(); }

尾递归是递归的优化,优化的目的是栈深度=1,永不StackOverflow。所有的递归都能转成尾递归。简单的场景,比如计算阶乘N!和Fibonacci数列,可以用parameter代替临时变量,实现尾递归。复杂的场景,比如对二叉树进行先序遍历(pre-order traversal, 深度优先遍历),就需要使用Continuation Passing Style(CPS)才能实现尾递归。

简单的场景比较好理解,定义里有多少个递归的临时变量,就用多少个参数即可。比如:

//阶乘:N! = (N-1)! * N
static int Factorial_TailRecursion(int target, int total = 1) {
if (target <= 1) {
return total;
} else {
return Factorial_TailRecursion(target - 1, total * target);
}
} //Fibonacci数列:f(n) = f(n-1) + f(n-2)
static int Fibonacci_TailRecursion(int target, int n1 = 0, int n2 = 1) {
if (target <= 0) {
return n1;
} else {
return Fibonacci_TailRecursion(target - 1, n2, n1 + n2);
}
}

至于CPS的写法,比如func(int i, Func<int, int> continuation),要这么看:用func处理i返回的结果,还需要继续用continuation处理,这就是继续(Continuation)的含义。仍然以这个例子为例:

static int Factorial_Continuation(int target, Func<int, int> func) {
if (target <= 1) {
return func(1);
} else {
//以5为例,计算Fac(4)的值,后续*5再传入func
return Factorial_Continuation(target - 1, n => func(n * target));
}
} static int Fibonacci_Continuation(int target, Func<int, int> func) {
if (target < 2) {
return func(target);
} else {
//以5为例,计算Fib(4)的值、后续计算Fib(3)的值、两值相+再传入func
return Fibonacci_Continuation(target - 1,
r1 => Fibonacci_Continuation(target - 2,
r2 => func(r1 + r2)));
}
}
参考
  1. 《尾调用优化》- 阮一峰
  2. 《尾递归与Continuation》 - 赵劼
  3. 《探索c#之尾递归编译器优化》 - 蘑菇先生
  4. 《探索c#之递归APS和CPS》 - 蘑菇先生

尾递归(Tail Recursion)和Continuation的更多相关文章

  1. 拾遗:关于“尾递归”- tail recursion

    定义[个人理解]: 尾递归,即是将外层得出的常量计算因子,以函数参数的形式逐层向内传递,即内层调用整合外层调用的产出,整个递归的结果最终由最内层的一次函数调用得出:而通常的递归则是外层调用阻塞.等待内 ...

  2. Scala Tail Recursion (尾递归)

    Scala对尾递归进行了优化,甚至提供了专门的标注告诉编译器需要进行尾递归优化.不过这种优化仅限于严格的尾递归,间接递归等情况,不会被优化. 尾递归的概念 递归,大家都不陌生,一个函数直接或间接的调用 ...

  3. 用尾递归和普通递归实现n!算法,二者比较

    尾递归 - Tail Recursion尾递归是针对传统的递归算法而言的, 传统的递归算法在很多时候被视为洪水猛兽. 它的名声狼籍, 好像永远和低效联系在一起.尾递归就是从最后开始计算, 每递归一次就 ...

  4. Scala尾递归

    递归函数应用 首先,我们来对比两个递归方法的求值步骤. 假设有方法gcd,用来计算两个数的最大公约数.下面是欧几里得算法的实现: def gcp(a: Int, b: Int): Int = if ( ...

  5. haskell中的cps

    cps全称叫continuation passing style,简要来讲就是告诉函数下一步做什么的递归方式,由于普通递归有栈溢出的问题,而cps都是尾递归(tail recursion),尾递归则是 ...

  6. 泛函编程(3)-认识Scala和泛函编程

    接着昨天的文章,再示范一个稍微复杂一点的尾递归tail recursion例子:计算第n个Fibonacci数.Fibonacci数第一.第二个数值分别是0,1,按顺序后面的数值是前面两个数的加合.例 ...

  7. Scala 常用语法

    Clojure首先是FP, 但是由于基于JVM, 所以不得已需要做出一些妥协, 包含一些OO的编程方式 Scala首先是OO, Java语法过于冗余, 一种比较平庸的语言, Scala首先做的是简化, ...

  8. scheme Continuation

    Continuation Pass Style在函数式编程(FP)中有一种被称为Continuation Passing Style(CPS)的风格.在这种风格的背后所蕴含的思想就是将处理中可变的一部 ...

  9. 递归、尾递归和使用Stream延迟计算优化尾递归

    我们在学数据结构的时候必然会接触栈(Stack),而栈有一个重要的应用是在程序设计语言中实现递归.递归用途十分广泛,比如我们常见的阶乘,如下代码: 1234 public static int (in ...

随机推荐

  1. CentOS7使用无线网卡

    1.安装CentOS7最小化时候,在网络连接功能项中设置WIFI的设备和密码. 2.进入系统,执行nmcli查看使用NetworkManager管理的网络 [root@seastar ~] nmcli ...

  2. webservice调用服务端数据时给soapenv:Envelope 添加自定义的命名空间

    最近做第三方接口,服务端需要 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/&qu ...

  3. :nth-child

    匹配其父元素下的第N个子或奇偶元素 :eq(index) 匹配选择器指定序列的元素,而这个将为每一个父元素匹配子元素. :nth-child从1开始的,而:eq()是从0算起的!可以使用:<br ...

  4. wex5 教程 之 图文讲解 后台管理界面设计与技巧

    视频教程地址:http://v.youku.com/v_show/id_XMTgwOTAyMTkyMA==.html?from=s1.8-1-1.2&spm=a2h0k.8191407.0.0 ...

  5. CSS 笔记二(Text/Fonts/Links/Lists)

    CSS Text 1> Text Color used to set the color of the text 2> Text Alignment used to set the hor ...

  6. objective C 学习之02

    1. 函数 -(void) HelloWorld:(BOOL)ishelloworld{ //干点啥 } 前面带有减号(-) 的方法为实例方法,必须使用类的实例才可以调用的.对应的有+号, 代表是类的 ...

  7. 如何在Linux中查看所有正在运行的进程

    如何在Linux中查看所有正在运行的进程 FROM:http://os.51cto.com/art/201101/244090.htm 进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系 ...

  8. ALLOCATE语句分配FORTRAN动态数组方法(转自http://blog.csdn.net/zhuxianjianqi/article/details/8067174)

    数组的动态分配 a)    可分配数组 数组可以是静态的也可以是动态的.如果数组是静态的,则在编译时就被分配了固定的储存空间,并且直到程序退出时才被释放.程序运行时静态数组的大小不能改变.静态数组的缺 ...

  9. Web Server 配置及上传文件

    第一种方案:从github上面克隆到服务器 1.git上传文件 ( git bash ) ①切换至所要上传的文件夹 cd 文件夹homepage ②告诉系统当前文件夹要进行git管理 git init ...

  10. .sh脚本判断判断某一变量是否为某一数值

    .sh脚本中,判断某一变量(例如:OEM_CUSTOMER_SUPPORT)是否为某一数值(例如:0),并根据条件做不同处理,写法如下: if [ $OEM_CUSTOMER_SUPPORT -eq  ...