首页
学习
活动
专区
圈层
工具
发布

尾递归函数

怯懦的朋友在叛离之后,会成为最凶残的仇敌——埃·斯宾塞 中文文档 Kotlin 支持一种称为尾递归的函数式编程风格。 这允许一些通常用循环写的算法改用递归函数来写,而无堆栈溢出的风险。...当一个函数用 tailrec 修饰符标记并满足所需的形式时,编译器会优化该递归,留下一个快速而高效的基于循环的版本: val eps = 1E-10 // "good enough", could be...10^-15 tailrec fun findFixPoint(x: Double = 1.0): Double = if (Math.abs(x - Math.cos(x)) 函数必须将其自身调用作为它执行的最后一个操作。...在递归调用后有更多代码时,不能使用尾递归,并且不能用在 try/catch/finally 块中。目前在 Kotlin for JVM 与 Kotlin/Native 中支持尾递归。

1.5K20

将非尾递归函数转换为循环或尾递归形式

1、问题背景在 Python 中,非尾递归函数可能会导致递归深度限制问题。当递归深度超过限制时,程序将引发 RecursionError 异常。...为了避免这个问题,我们可以将非尾递归函数转换为循环或尾递归形式。2、解决方案2.1 循环形式我们可以使用循环来实现非尾递归函数的功能。...例如,我们可以将以下非尾递归函数:def fact(n): if n == 0: return 1 else: return n * fact(n-1)转换为以下循环形式...尾递归函数可以很容易地转换为循环形式,因为递归函数的最后一步可以被一个循环来代替。...然而,尾递归形式更易于理解和维护,因为它是直接递归的。2.4 转换技巧将非尾递归函数转换为循环或尾递归形式时,我们可以使用以下技巧:确定递归函数的基线情况,即不需要递归调用的情况。

1.1K10
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    匿名函数、高阶函数与尾递归优化

    尾递归优化:递归的救星递归是解决问题的优雅方式,但在处理大规模数据时常常因栈溢出而失效。这是我初学递归时经常遇到的痛点。尾递归优化正是解决这一问题的关键技术。...尾递归优化的核心是将递归调用作为函数的最后一个操作,这样编译器可以复用当前的栈帧而不是创建新的,从而避免栈溢出。...通过实践,我总结了普通递归与尾递归的主要区别:特性普通递归尾递归栈帧使用每次递归创建新栈帧可复用同一栈帧内存消耗与递归深度成正比常量级内存消耗代码风格通常更直观需要传递累加器参数易用性直接表达问题可能需要包装辅助函数风险栈溢出风险高取决于语言是否支持优化一个典型的例子是计算斐波那契数列...我曾经写过这样的普通递归版本:function fib(n) { if (n 非尾递归}这个实现在n较大时性能极差...// 尾递归形式}不同语言对尾递归优化的支持程度不同,这也是我在跨语言编程时需要特别注意的:语言尾递归优化支持情况备注Scheme完全支持语言规范要求实现Scala良好支持使用@tailrec注解验证JavaScript

    31111

    Kotlin中尾递归函数

    Kotlin尾递归函数理解 kotlin中,如果某个函数的末尾又调用了函数自身,这种就称为尾递归函数。 尾递归函数需要在 fun 前面添加 tailrec。...尾递归函数会使用循环的方式替代递归,从而避免栈溢出。 尾递归不能在异常处理的try、 catch 、 finally 块中使用 。...,且递归调用后没有更多代码,因此可 以将该函数改为尾递归语法。...此时,上面函数可改为如下形式 //使用尾递归函数的语法 tailrec fun factRec(n: Int, total : Int= 1): Int = if (n == 1) total else...factRec(n - 1 , total * n) 优势 与普通递归相比,编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的 版本,这样就可以减少可能对内存的消耗。

    1.6K10

    「函数」递归与迭代

    一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量...其他解释 递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。...构成递归需具备的条件: 子问题须与原始问题为同样的事,且更为简单; 不能无限制地调用本身,须有个出口,化简为非递归状况处理。...理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)递归比迭代效率要低。 [递归与迭代结构图] 相同点: 递归和迭代都是循环的一种。...总结 递归与迭代都是函数实现的一种方式,包含了不同的逻辑思想; 递归反复调用自身函数,编程思路比较清晰; 迭代从变量最初的值开始,不断用变量旧值递推出新值。

    2K30

    「函数」递归与迭代

    一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量...其他解释 递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。...构成递归需具备的条件: 子问题须与原始问题为同样的事,且更为简单; 不能无限制地调用本身,须有个出口,化简为非递归状况处理。...理论上递归和迭代时间复杂度方面是一样的,但实际应用中(函数调用和函数调用堆栈的开销)递归比迭代效率要低。 相同点: 递归和迭代都是循环的一种。...总结 递归与迭代都是函数实现的一种方式,包含了不同的逻辑思想; 递归反复调用自身函数,编程思路比较清晰; 迭代从变量最初的值开始,不断用变量旧值递推出新值。

    78320

    了解递归:普通函数递归和非递归栈式实现之间的区别

    = null){    preView(node.right);  // 3  } } 如果我们用函数栈帧的思想,每调用一个函数,就把一个栈帧入栈 ? ? ? ? ?...这里的问题就是:栈帧无法为我们提供足够的信息,让我们正确的继续用栈执行递归。 如果编译器编译上述的伪代码,那么在函数栈帧中会保存要返回的地址。...(递归调用右子节点,代码中行3)走,还是说都走过了,要弹出(即已经执行了代码中行2,行3,函数执行完毕返回)。...递归子函数的栈帧弹出后,返回到针对当前节点的栈帧:有以下情况 0,如果这个int变量为0,则左右子节点都未被递归调用 1,如果这个int变量为1,则把右子节点对应栈帧入栈,并且把当前栈帧中这个int变量修改成...其实在知道左子节点入栈了,但右子节点未入栈后,没必要保存当前栈帧,因为上述伪代码对右子节点的递归是尾递归,即当前函数递归调用当前函数,但是并不期待这个递归调用 给当前的函数带来些什么,递归调用也用不到当前函数栈帧

    1.4K30

    函数递归与迭代

    递归的简介 1.1 递归的思想: 把⼀个大型复杂问题层层转化为⼀个与原问题相似,但规模较小的子问题来求解;直到⼦问题不能再 被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。...第n项斐波那契数可以通过第n-1项和n-2项斐波那契数相加而得到,在这里也能看出函数递归的意思来; int count = 0; int Fib(int n) { if(n == 3) count+...递归的缺陷: 在C语⾔中每⼀次函数调⽤,都需要为本次函数调⽤在内存的栈区,申请⼀块内存空间来保存函数调 ⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。...函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归 函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。...所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题。

    23710

    函数递归与迭代

    很高兴与大家见面! 给生活添点快乐,开始今天的编程之路。 一 递归是什么? 1定义:递归即递推和回归,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。...⽤了 main 函数 return 0 ; } 上述就是⼀个简单的递归程序,只不过上⾯的递归只是为了演⽰递归的基本式,不是为了解决问 题,代码最终也会陷⼊死递归,导致栈溢出...这是因为代码运行会向内存申请空间,而死递归导致向内存申请空间超过导致栈溢出。...坏处: 在C语⾔中每⼀次函数调⽤,都需要为本次函数调⽤在内存的栈区,申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。...所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出的问题。 这是 不想使⽤递归,就得想其他的办法, 通常就是迭代的⽅式(通常就是循环的⽅式)。

    29610

    「算法小记」-1:Ackermann函数阿克曼函数的一点思考解法【递归非递归堆栈方法】(C++ )

    Ackermann函数详解 Ackermann函数要求如下: 我们需要知道的是这个函数的时间复杂度增长的非常非常快,A(2,3)和A(5,0)应该差了几百个量级。...解法1: 常规递归(只适合输入量很小的情况) 这个就是无限递归了,如果输入量是 2 3,这种很容易就出答案,因为很容易算。 但是这个代码只适合不限制时间的情况下进行操作。...} } int main() { int a,b; cin >> a>>b; int ans = A(a,b); cout << ans %1000000009<<endl; } 解法2:堆栈解法...创建一个数组,当成一个堆栈。...} } } int main() { int m,n; cin >> m >> n; int b=A(m,n); cout<<b <<endl;; return 0; } 解法3:优化递归

    1.4K10

    关于php递归函数内存溢出的问题

    简单写一个递归函数: echo '运行前内存:' . round(memory_get_usage() / 1024 / 1024, 2) . ...'MB', PHP_EOL;     recursive($i-1); } 可看到,内存占用将一直上升,直到运行完毕或者内存溢出强制退出,那么为什么会出现这样的情况呢?...主要是因为php的内存回收机制: php的垃圾回收机制 php只有在该函数执行完毕后才会进行回收,而该函数需要调用新的函数(递归),导致$data一直没有回收,直到执行完毕之后才会进行回收,所以造成了内存溢出...解决方案 解决方案也很简单,在使用完data之后,递归调用之前,进行unset销毁data即可: 本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

    3.4K20

    python 递归与高阶函数

    在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。...递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。...由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出) 问题规模,指的是传递参数。比如说第一次传进去10,第二次,就不应该还是10,要比10小,否则程序无法结束。...下面将一个正式的递归函数 传一个参数进去,每次除以2,直到不能除为止,程序结束。...,f):     return f(a)+f(b) res = add(3,-6,abs) print(res) 执行输出 9 说明: abs是python的内置方法,用来取绝对值的,它会返回一个 非负数

    1.3K40

    【通关函数的递归】--递归思想的形成与应用

    前言:上篇博文分享了扫雷游戏的实现,这篇文章将会继续分享函数的递归相关知识点,让大家了解并掌握递归的思路; 往期回顾: 【趣味小游戏】--扫雷游戏 一.递归的概念与思想 1.定义 ---递归其实是一种解决问题的方法...,在c语言中递归就是函数自己调用自己,举个最简单的例子: #include int main() { printf("hehe\n"); main();//main函数中...⼜调⽤了main函数 return 0; } 上述就是一个简单的递归程序,只不过上面的递归只是为了演示递归的基本形式,不是为了解决问题,代码最后也会陷入死递归,导致栈溢出(stack overflow...2.递归的思想 把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。...printf("%d ", n % 10); } int main() { int m = 0; scanf("%d", &m); Print(m); return 0; } 画图推演: 三.递归与迭代

    31010

    C语言函数递归详解:理解递归的原理与应用

    摘要: 本文将详细介绍C语言中的函数递归,包括递归的原理、递归的基本结构、递归的应用场景以及递归的注意事项。通过代码示例,帮助读者深入理解和掌握C语言函数递归的概念与用法。...本文将详细介绍C语言中的函数递归,带你一步步了解它的原理、用法以及注意事项。 二、递归的原理 函数递归的原理基于两个关键思想:基本情况和递归调用。...三、递归的基本结构 函数递归的基本结构包括两个部分:递归函数的定义和递归函数的调用。 1. 递归函数的定义: 递归函数需要在函数体内部调用自身。函数的参数和返回值可以根据具体问题进行定义。...递归函数的调用: 在递归函数内部调用自身,将问题分解为更小的子问题。通过递归调用,函数可以不断地向基本情况靠近,最终解决问题。...六、总结 本文详细介绍了C语言中的函数递归,包括递归的原理、基本结构、应用场景以及注意事项。通过代码示例,希望读者能够更加深入地理解和掌握函数递归的概念与用法。

    1.4K10

    C语言--函数递归与迭代

    递归在书写的时候,有两个必要条件: 1.递归存在限制条件,但凡满足这个限制条件时,递归便不再继续 2.每次递归调用之后越来越接近这个限制条件 递归的思想: 把大事化小事 递归其实就是函数自己调用自己 /...,一直打印hehe 总而言之,在函数中再次调用自己就是递归 如果递归无限的递归下去,就会出现这样的错误,栈溢出 // 每一次函数调用,都要为这次函数调用分配内存空间是内存的栈区上分配的, 如果无限的递归调用函数...,就会将栈区空间使用完, 就会出现栈溢出的现象 //递归---求n的阶乘 //n的阶乘就是1~n的数字累计相乘 //n!...总而言之,在函数中再次调用自己就是递归 如果递归无限的递归下去,就会出现这样的错误,栈溢出 // 每一次函数调用,都要为这次函数调用分配内存空间是内存的栈区上分配的, 如果无限的递归调用函数,就会将栈区空间使用完..., 就会出现栈溢出的现象 //递归---求n的阶乘 //n的阶乘就是1~n的数字累计相乘 //n!

    64910

    C语言入门指南:函数与函数递归

    • 如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误。 二、数组做函数参数 在 C 语言中,数组作为函数参数是一个重要且容易混淆的知识点。...即 如何正确的递归:两要素 递归基 (Base Case) / 终止条件:这是递归的“出口”。当问题小到一定程度时,就不再调用自己,而是直接给出答案。没有终止条件的递归会导致无限循环,最终栈溢出 。...忘记定义基准情形是编写递归函数时的常见错误之一。(利用return) 递归关系 (Recursive Case):这是递归的“核心”。 3.3 经典例子 1 :计算阶乘(n!) 阶乘的定义: 0!...符合人类思维:很多问题天然具有递归结构,用递归解决很自然。 4.2 缺点和风险 栈溢出风险:每次函数调用都会占用一定的栈空间。如果递归层次太深(比如无限递归),栈空间会被耗尽,导致程序崩溃。...递归是一种强大的编程技巧,记住“递归基”和“递归关系”两大法宝,同时警惕栈溢出的风险。 函数和递归是C语言乃至所有编程语言的基础。多加练习,动手写代码,你一定能熟练运用它们,写出更优秀的程序!加油!

    33310

    函数(五)(函数的嵌套与递归调用)

    for(i=; i<=n; i++) { s = s + fac(i); } return s; } 结果示例: 函数的递归调用...递归是指函数直接或间接的调用自己的过程。...C语言的特点之一就是允许函数的递归调用,即在函数体中直接或间接的调用函数自身。如果一个函数直接调用了自己,称为直接递归;如果一个函数调用了其他函数,而被调用的函数又调用了主调函数,则称为间接递归。...递归调用的函数在定义时需要满足两个条件: (1) 有一个或多个终止状态,即最简单的情况,用于结束递归调用。 (2) 每次递归调用都必须简化当前问题的求解,使问题越来越接近终止状态,最终达到终止状态。...例:使用函数递归调用实现将一个正整数输出其二进制形式,例如,输入10,输出1010 思路分析:将十进制的正整数转换成其二进制形式输出,可以采用“除2取余,逆序排列”方法。

    3.1K10

    C语言:函数的嵌套与递归

    函数的嵌套 在C语言中,所有函数都是相互平行,且相互独立的。在定义函数时,一个函数内不能再定义另一个函数,不能嵌套定义,但是可以嵌套使用。 例:编写一个求四个整数中最小值的函数,并在主函数进行调用。...b:a; } 函数的递归--->循环 在函数的调用过程中,出现一个函数调用自己本身的情况,就是在运行的过程中调用自己。...函数的递归有两个必要条件: 函数的出口,不能无限制地调用本身,须有个出口,化简为非递归状况处理。 递推公式。...(偷懒) 递归的理解方法: 例如:求1+2+3+4+...+100 #include int main(){ int sum(int n); printf("%d",...int sum(int n){ if(n==1){ return 1; }else{ return sum(n-1)+n; } } 更多的关于函数递归的例题请见下一篇

    1.6K30

    Scala的基础概念

    引用透明性 对于上述Add函数,对于同一输入y,返回结果均相同 所以,Add具有引用透明性 如何确保引用透明 不变性Immutablity:任何的状态和值都是不变的,才能获得引用透明 函数与变量,对象类是同一级的...: 严格求值:call by value 非严格求值:call by name 惰性求值 定义表达式时不会立即求值,只在第一次调用时才求值 递归函数 函数式编程中没有循环语句,全部的循环用递归实现...调优递归:尾递归 函数式编程的优点 Lisp是第一种函数式编程语言 编程代码量少 当构造完含数之后,对于相同输入,输出相同,便于调试 非常适用于并行编程,没有副作用,具备引用透明性,在n个节点运算结果是相同的...对于数值类型:低精度可以赋值给高精度,反之不行,数据会缺失:报类型不匹配错误 Unit 往往作为函数的返回值出现,表明函数有副作用 Null 表示一个引用类型的值为空。...递归优化:变成尾递归,尾递归会复写当前栈,不会导致堆栈溢出 尾递归优化:用¥annotation.tailrec显示指明编译时进行尾递归优化 @annotation.tailrec def factorial

    1.2K30
    领券