kotlin 尾递归_Kotlin的尾递归

kotlin 尾递归

In Kotlin, there are a bunch of modifier keywords that are very useful in some use cases. Some of them are already well-known because of their high applicability such as: inline, inner, lateinit, reified, open, override etc. In contrast, several ones are not largely known because there is not often use case that requires these features.

在Kotlin中,有一堆修饰符关键字在某些用例中非常有用。 由于它们的高度适用性,其中一些已经广为人知,例如: 内联,内部,lateinit,修饰化,打开,覆盖等。相比之下,由于没有太多的用例需要这些功能,因此其中几个并没有广为人知。

In this article, we will discover such a keyword: tailrec, and what it provides.

在本文中,我们将发现这样的关键字: tailrec及其提供的内容。

According to the official documentation:

根据官方文件:

tailrec marks a function as tail-recursive (allowing the compiler to replace recursion with iteration)

tailrec将函数标记为尾递归(允许编译器用迭代替换递归)

Following this definition, there must be something wrong with a recursive function since the compiler has to replace it with an iteration (for loop for instance).

遵循此定义,递归函数一定存在问题,因为编译器必须将其替换为迭代( for 例如循环)。

那么,怎么了? (So, what is wrong?)

A recursion function will cause the stackOverflowError when the number of recursive-calls is significant enough.

递归函数将在以下情况下导致stackOverflowError 递归调用的数量足够多。

Look at this function:

看一下这个功能:

private fun factorial(n : Long) : Long {
return if (n == 1L) n
else n * factorial(n -1)
}

To calculate a factorial of a given n, there would be n factorial() functions that are executed. The thing is with i within n to 2 inclusive, all the factorial(i) functions will not be done without the result of factorial(i-1). Thus, the virtual machine will store these functions into the stack and invoke the last multiplication once the result is out. When the number of such functions crosses a certain threshold, we will get stackOverflowError.

到计算出n个给定的阶乘就为n factorial()所执行的功能。 问题是,如果in到2之内(包括2和2 factorial(i)则没有factorial(i-1)的结果将无法完成所有factorial(i)函数 因此, 虚拟机会将这些函数存储到堆栈中,并在结果输出后调用最后的乘法。 当此类函数的数量超过某个阈值时,我们将得到stackOverflowError

我们如何解决这个缺陷? (How do we resolve this flaw?)

Well, the idea is that each call has to be independently accomplished. However, the principle of a recursive function does not allow us to achieve that idea because it has to call itself no matter what. Thus, the solution is very simple: do not use recursion; and that is why, back to the definition above, it is replaced by its alternative : an iteration.

好吧,我们的想法是每个呼叫必须独立完成。 但是,递归函数的原理不允许我们实现该想法,因为无论如何它都必须调用自身 。 因此,解决方案非常简单: 不使用递归 ; 这就是为什么,回到上面的定义,将其替换为替代方案:迭代。

The problem arises again when certain people still prefer using the recursion over the iteration (this is one of the styles of functional programming). Therefore, we need help of the compiler to silently change that recursion into an iteration in compile time.

当某些人仍然喜欢使用递归而不是迭代时,问题再次出现(这是函数式编程的样式之一)。 因此,我们需要编译器的帮助,以在编译时将递归静默地更改为迭代。

However, not all recursive functions are comprehensive for the compiler. It can only translate a tail-recursive function into an iteration.

但是,并非所有递归函数对于编译器都是全面的。 它只能将尾递归函数转换为迭代。

什么是尾递归函数以及如何创建它? (What is a tail-recursive function and how to create one?)

A recursive function is tail-recursive when recursive call is the last thing executed by the function. In other words, to calculate f(n), f(n-1)must be the last thing to be executed:

当递归调用是该函数执行的最后一件事时,递归函数是尾递归 。 换句话说,要计算f(n)f(n-1)必须是要执行的最后一件事:

private fun f(n : Long) : Long {return if (n == 1L) n
else f(n -1)
}

Back to the factorial function, this is how we can turn it to be tail-recursive:

回到阶乘函数,这是我们可以将其变为尾递归的方法

/**
* A tail-recursive function
*/

private fun factorial(n : Long, a : Long = 1) : Long {
return if (n == 1L) a
else factorial(n - 1, n * a)
}

Note that tail-recursive function does not help to avoid stackoverflowerror, it is only a style of recursion.

请注意,尾递归函数无助于避免stackoverflowerror ,它只是一种递归样式。

将尾递归函数转换为迭代。 (Translate a tail-recursive function to an iteration.)

Once we have prepared the tail-recursive function, the last step is adding the modifier keyword tailrec in order for the compiler to convert it into an iteration.

一旦准备好tail-recursive函数,最后一步就是添加修饰符tailrec 为了使编译器将其转换为迭代。

/**
* a tail-recursive function with keyword tailrec
*/
private tailrec fun factorial(n : Long, a : Long = 1) : Long {return if (n == 1L) a
else factorial(n - 1, n * a)
}

This is how the function looks like after being compiled:

这是函数在编译后的样子:

private final long factorial(long n, long a) {while(n != 1L) {long var10000 = n - 1L;
a = n * a;
n = var10000;}return a;}

As you can see, this is no longer a recursion but a while loop.

如您所见,这不再是递归,而是while循环。

Sometimes, developers must choose between a clean or a performance code. However, in the case of tailrec, you have a clean code with the recursion style and the compiler takes care of the performance. Best of both worlds!

有时,开发人员必须在干净的代码或性能代码之间进行选择。 但是,对于tailrec来说 ,您的代码很干净,具有递归样式,编译器负责性能。 两全其美!

In Android and any other high-level products, we only work with high-level languages or frameworks, so algorithm is not often a concern for developers. Iteration is a straightforward and easy-to-implement solution, so people often think about it first instead of a recursion. That is the reason why tailrec is quite unnecessary in my opinion. However, the ideas of tail recursion and tailrec are still very interesting. It inspires us to understand further how a function and a stack work.

在Android和任何其他高级产品中,我们仅使用高级语言或框架,因此算法通常不受开发人员的关注。 迭代是一种直接且易于实现的解决方案,因此人们通常首先考虑它而不是进行递归。 这就是我认为tailrec完全不必要的原因。 但是,尾递归和tailrec的思想仍然非常有趣。 它激发我们进一步了解函数和堆栈的工作方式。

tailrec does not often appear in our daily coding, but it is worth understanding what it brings us.

tailrec通常不会出现在我们的日常编码中,但是值得理解它给我们带来了什么。

Hope you find some useful information in this article.

希望您能在本文中找到一些有用的信息。

Lam Pham

林潘

翻译自: https://medium.com/swlh/tail-recursion-in-kotlin-7585b5357e70

kotlin 尾递归

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值