如题:尾递归 Kotlin可以Java不行
为什么?
下面的错误是怎么产生的, 是由于深递归(deep recursion)的操作而出现这个异常提示我们栈溢出了,如果解决,解决这个问题的方法有很多,在Kotlin里就需要用到“尾递归” 这个方式
FATAL EXCEPTION: main
Process: com.demo.wiki_blog, PID: 32660
java.lang.StackOverflowError: stack size 8192KB
一. 先从递归说起
- 什么是递归 :
程序调用自身的编程技巧称为递归 (来自:百度百科)
- 举个栗子
求N的阶乘 (例如:5!= 54321)
private fun initTest() {
Log.i(TAG, "initTest: ${fact(5)}")
}
private fun fact(num: Int): Int {
if (num == 1) {
return 1
} else {
return num * fact(num - 1)
}
}
写一个求1到N的求和公式
private fun initTest() {
Log.i(TAG, "initTest: ${sum(1000 * 1000)}")
}
var count = 1
private fun sum(num: Int): Int {
if (num == 1) {
return 1
} else {
Log.i(TAG, "count: ${count++}")
return num + sum(num - 1)
}
}
加粗样式最终我的手机运行到123213次之后 出现StackOverflowError 异常了,那我如果就想用迭代方式,计算一百万个数叠加怎么处理呢!
二. 尾递归
tailrec关键字提示编译器尾递归优化, 并不是加上这个关键字就可以了,
只加上Tailrec关键字,会提示A function is marked as tail-recursive but no tail calls are found( 函数被标记为尾递归,但没有找到尾调用)
另外 Recursive call is not a tail call (递归调用不是尾调用)
三. 尾递归优化
其实上面异常提示意思是,开发者觉得这段代码可以尾递归优化,但是编译器看着不能, 编译器看着不能那就是有问题!
如果想让这段代码尾递归优化的话, 得2个return 返回的应该是这个方法本事 而不是一个加法表达式 num + sum(num - 1) 所以我们改一下代码
private fun initTest() {
Log.i(TAG, "initTest: ${sum(1000 * 1000, 0)}")
}
private tailrec fun sum(num: Int, sun: Int): Int {
if (num == 0) {
return sun
} else {
return sum(num - 1, sun + num)
}
}
计算无压力
四. 尾递归是怎么优化 栈信息的呢?看源码
先看下优化前(也就是不添加tailrec时)
在看下优化后(即添加tailrec后)
我们发现,没有优化的findListNode对应的java代码依然是递归函数,优化后(即添加了tailrec关键字)的findListNode方法对应的java代码已经不再是递归函数,而是通过循环来实现功能,这样就不会再出现stackoverflowerror了,这样我们就在kotlin中既实现了递归函数代码简洁的优势,又规避了在java中使用递归函数容易出现的问题
参考文章:https://blog.csdn.net/zsp765098084/article/details/90901260